Compare commits

...

545 Commits

Author SHA1 Message Date
Geert Weening
8d50034265 Bump version to 0.13.0-rc1 2015-07-22 12:02:15 -07:00
Geert Weening
64f451e904 Merge branch 'release' into develop
Conflicts:
	npm-shrinkwrap.json
	package.json
2015-07-22 11:59:36 -07:00
Geert Weening
b1b47d7d91 Bump version to 0.12.5 2015-07-22 11:53:13 -07:00
Geert Weening
a93c580c92 Bump version to 0.12.5-rc3 2015-07-22 11:49:08 -07:00
Geert Weening
030e2786d1 Update release notes 2015-07-22 11:48:34 -07:00
Geert Weening
56bbe1d437 Update shrinkwrap 2015-07-22 11:42:34 -07:00
Ivan Tivonenko
1fff5ea6dc [FIX] fix AutobridgeCalculator (RT-3445)
pass issuers to AutobridgeCalculator so
it can create offers with right issuers
2015-07-22 11:28:10 -07:00
Ivan Tivonenko
ad9956375f [FIX] change var to let and const 2015-07-22 11:28:00 -07:00
Chris Clark
4787e5d29a Merge pull request #444 from darkdarkdragon/develop-RLJS-370-6
cover api/transaction/order.js with tests
2015-07-21 18:26:36 -07:00
Ivan Tivonenko
194f76d57f cover api/transaction/order.js with tests 2015-07-22 04:18:35 +03:00
Chris Clark
d2ee5cb0bc Merge pull request #443 from darkdarkdragon/develop-RLJS-370-5
cover src/api/transaction/payment.js with unit tests
2015-07-21 17:50:05 -07:00
Ivan Tivonenko
040298db2c cover src/api/transaction/payment.js with unit tests 2015-07-22 03:29:30 +03:00
Chris Clark
b4e6d4b98c Merge pull request #442 from reedrosenbluth/proxy
check if proxy option is undefined, not if it exists
2015-07-21 17:11:42 -07:00
Reed Rosenbluth
a2b31e2677 check if proxy option is undefined, not if it exists 2015-07-21 17:01:02 -07:00
Chris Clark
e997c44a18 Merge pull request #440 from darkdarkdragon/develop-RLJS-370-4
increase test coverage of Account object
2015-07-21 11:37:53 -07:00
Alan Cohen
b27011fb38 Merge pull request #441 from lumberj/add-flow-to-api
Add flow to api
2015-07-21 11:22:47 -07:00
Alan Cohen
b477eb238b Add flow-bin to dev dependencies and ignore it in flowconfig
Add flow to api/server/server.js

Add flow check to src/api: index, accountinfo, balances, orderbook..

orders, settings, transaction, transactions

Add flow typecheck to api/ledger/pathfind

Use eslint plugin flowtype to allow flow type annotations

Babel-eslint emits errors when using the type keyword. This plugin works
around the issue by stripping flow annotations before linting.

Source: https://www.npmjs.com/package/eslint-plugin-flowtype

Add flow to ledger/parse/account-order, trustline, orderbook-order
2015-07-21 10:59:49 -07:00
Ivan Tivonenko
02bc256225 increase test coverage of Account object 2015-07-21 09:19:25 +03:00
sublimator
9f7293127c Merge pull request #439 from clark800/cleanup
Cleanup
2015-07-21 10:38:57 +07:00
sublimator
61a0ea7eac Merge pull request #437 from clark800/naming
Rename "incoming"/"outgoing" to "initiated"
2015-07-21 10:37:40 +07:00
sublimator
51ef93e23b Merge pull request #435 from clark800/consistency
Don't show flags that are set to false
2015-07-21 09:06:05 +07:00
Chris Clark
ab11220e27 Merge pull request #438 from reedrosenbluth/proxy
Proxy Support
2015-07-20 18:22:59 -07:00
Reed Rosenbluth
b23d588747 disable eslint for lines 538 - 548 2015-07-20 18:18:45 -07:00
Reed Rosenbluth
a71dc28523 updated shrinkwrap 2015-07-20 18:02:42 -07:00
Reed Rosenbluth
2de0e13ec5 proxy support 2015-07-20 17:39:46 -07:00
Chris Clark
d0c922ed13 Require uppercase hex for currency codes 2015-07-20 17:34:03 -07:00
Chris Clark
8c82ebec79 Use "core" instead of "utils.core" 2015-07-20 17:33:19 -07:00
Chris Clark
ddd83d10fa Don't show flags that are set to false 2015-07-20 16:52:43 -07:00
Chris Clark
031375e701 Rename "incoming"/"outgoing" to "initiated" 2015-07-20 14:59:59 -07:00
sublimator
4bc285313c Merge pull request #434 from clark800/fixes
Simplify getServerInfo response
2015-07-21 01:21:16 +07:00
Chris Clark
f42dd69b53 Simplify getServerInfo response 2015-07-20 11:15:54 -07:00
sublimator
1dae06fdd8 Merge pull request #433 from clark800/mock-fixtures
Organize rippled mock fixtures
2015-07-20 14:26:17 +07:00
Chris Clark
ff29247b9e Organize rippled mock fixtures 2015-07-17 15:36:37 -07:00
Chris Clark
d9eca203ed Merge pull request #431 from darkdarkdragon/develop-RLJS-370-2
test parseTrustline in getTransaction
2015-07-17 14:22:40 -07:00
Chris Clark
4a85182a79 Merge pull request #432 from darkdarkdragon/develop-RLJS-370-3
testing - add memo field into payment transaction so parsePaymentMemo…
2015-07-17 14:21:46 -07:00
Ivan Tivonenko
055d275f0a test parseTrustline in getTransaction 2015-07-18 00:18:35 +03:00
Ivan Tivonenko
eb7bbe5715 testing - add memo field into payment transaction so parsePaymentMemos from parsePayment is tested 2015-07-18 00:13:31 +03:00
Chris Clark
8d5752883f Merge pull request #430 from clark800/fixtures
Organize fixtures
2015-07-16 15:29:59 -07:00
Chris Clark
2b5f356de8 Organize fixtures 2015-07-16 15:02:27 -07:00
Chris Clark
c9610900fd Merge pull request #429 from darkdarkdragon/develop-RLJS-370-1
add test for getting order and order cancellation transations
2015-07-16 13:31:15 -07:00
Ivan Tivonenko
79b1a65a7b add test for getting order and order cancellation transations 2015-07-16 23:08:57 +03:00
Chris Clark
df21b9453f Merge pull request #428 from darkdarkdragon/develop-fix-transaction-parse
fix attachTransactionDate in getTransaction
2015-07-16 10:25:42 -07:00
Ivan Tivonenko
22257bdd2b fix attachTransactionDate in getTransaction 2015-07-16 05:23:56 +03:00
sublimator
aa3767b180 Merge pull request #427 from clark800/account-info
Add getAccountInfo method and move sequence field in response
2015-07-16 09:48:02 +08:00
Chris Clark
c073c2b7de Add getAccountInfo method and unit test 2015-07-15 17:13:14 -07:00
Chris Clark
529a55efb0 Remove sequence from AccountFields 2015-07-15 15:49:05 -07:00
Chris Clark
c36567e062 Merge pull request #426 from clark800/cleanup
Fix hash128 schema
2015-07-15 15:19:02 -07:00
Chris Clark
f7873f3b41 Fix hash128 schema 2015-07-15 15:15:31 -07:00
Chris Clark
6b54f1c1eb Merge pull request #423 from clark800/cleanup
Cleanup RippleAPI
2015-07-15 15:06:52 -07:00
Chris Clark
1fb1bc7404 Cleanup RippleAPI 2015-07-15 14:55:24 -07:00
Chris Clark
aa646a3acf Merge pull request #421 from clark800/bugfix
Fix parsing of settings transactions
2015-07-15 14:54:25 -07:00
Chris Clark
18ac8a9d03 Fix parsing of settings transactions 2015-07-15 14:43:04 -07:00
Chris Clark
7c357c5d52 Merge pull request #424 from clark800/disable-resubmit
Disable automatic transaction resubmission for RippleAPI
2015-07-15 14:24:18 -07:00
Chris Clark
816db9e0dc Merge pull request #425 from clark800/tx-hash
Add transaction id to response of getTransaction and getTransactions
2015-07-15 14:22:08 -07:00
Chris Clark
fa9e1de4df Add transaction id to response of getTransaction and getTransactions 2015-07-15 13:57:00 -07:00
Chris Clark
137d947606 Disable automatic transaction resubmission for RippleAPI 2015-07-15 13:45:27 -07:00
sublimator
77a05c1881 2000 2015-07-15 19:49:39 +08:00
sublimator
ab13e3fe18 Merge pull request #422 from darentuzi/patch-1
Ternary operator syntax fix
2015-07-15 19:47:22 +08:00
darentuzi
fc4b085026 Ternary operator syntax fix
let to const and syntax update
2015-07-15 14:44:41 +03:00
Chris Clark
67e8f6ab65 Merge pull request #420 from darkdarkdragon/develop-RLJS-408
Test all methods of RippleApi in integration-test
2015-07-14 17:21:37 -07:00
Ivan Tivonenko
792b30c8b5 Test all methods of RippleApi in integration-test 2015-07-15 03:17:39 +03:00
sublimator
3407aadfb8 Merge pull request #418 from clark800/ledger-gaps
Refine ledger gap detection to reduce false positives
2015-07-15 07:21:09 +08:00
sublimator
29e59ebfd2 Merge pull request #419 from clark800/set-regular-key
Add support for regularKey in prepareSettings
2015-07-14 15:29:46 +08:00
Chris Clark
286a684963 Add support for regularKey in prepareSettings 2015-07-13 16:46:57 -07:00
Chris Clark
3ef586d2d3 Refine ledger gap detection to reduce false positives 2015-07-13 15:57:50 -07:00
sublimator
5889037a71 Merge pull request #414 from clark800/merge-orderbook
Merge flipped orderbook in getOrderbook
2015-07-14 06:54:54 +08:00
sublimator
5e3f3969f5 Merge pull request #403 from clark800/no-limit
Get all results when limit is not specified
2015-07-14 06:52:20 +08:00
Chris Clark
eecf45918b Merge pull request #415 from darkdarkdragon/develop-RLJS-404
Port integration tests from ripple-rest
2015-07-13 15:50:54 -07:00
Chris Clark
6d91e6e6b2 Merge pull request #417 from darkdarkdragon/develop-fix-gettransaction-missing-history
throw MissingLedgerHistoryError in getTransaction only when ledgers a…
2015-07-13 15:43:06 -07:00
Ivan Tivonenko
7808f87060 Throw MissingLedgerHistoryError in getTransaction only when ledgers are missing prior to the last closed ledger. 2015-07-14 01:25:07 +03:00
Ivan Tivonenko
6c48b22eea Port integration tests from ripple-rest 2015-07-14 01:10:42 +03:00
Chris Clark
7344f1178b Merge flipped orderbook in getOrderbook 2015-07-13 14:09:33 -07:00
Chris Clark
5a6a3ce6e0 Merge pull request #413 from darkdarkdragon/develop-fix-gettransaction
[FIX] fix getTransaction
2015-07-13 11:06:59 -07:00
Chris Clark
f3a54bf02a Get all results when limit is not specified 2015-07-13 10:30:51 -07:00
Ivan Tivonenko
766dc5d0ce [FIX] fix getTransaction
Do not try to parse transaction if there was error

Throw NotFoundError instead of ApiError in case of ledger_index not defined.
2015-07-11 15:30:41 +03:00
wltsmrz
f8f196903a Merge pull request #406 from wltsmrz/add-resubmission-switch
Add switch to disable transaction resubmission
2015-07-11 02:22:30 +08:00
sublimator
337c41fe10 Merge pull request #400 from clark800/refactor
Refactor to make quality adjustment for XRP more clear
2015-07-11 01:48:35 +08:00
Chris Clark
fb8eefd00d Refactor to make quality adjustment for XRP more clear 2015-07-10 10:24:52 -07:00
wltsmrz
3082b959f6 Add switch to disable transaction resubmission
* Add Transaction.setResumittable(Boolean)
* Add Transaction.isResumittable() -> Boolean
* Add automatic_resubmission (Boolean) config option to Remote
2015-07-10 09:53:31 -07:00
wltsmrz
487371737c Merge pull request #408 from wilsonianb/subscribe-vals
Handle validations subscription messages
2015-07-11 00:51:36 +08:00
Chris Clark
14ccc9359d Merge pull request #410 from clark800/range-check
Check for ledger gaps in getTransactions
2015-07-09 15:19:33 -07:00
Chris Clark
665b76271c Merge pull request #411 from clark800/get-paths
Rename getPathFind to getPaths
2015-07-09 15:15:49 -07:00
Chris Clark
11ca00bb45 Check for ledger gaps in getTransactions 2015-07-09 15:14:59 -07:00
Chris Clark
046d397dfb Rename getPathFind to getPaths 2015-07-09 15:12:45 -07:00
wilsonianb
58cc01b6d3 Handle validations subscription messages 2015-07-09 15:08:39 -07:00
Chris Clark
6a3eb7b9b9 Merge pull request #409 from clark800/counterparty
Add counterparty option to getTransactions
2015-07-09 14:59:10 -07:00
Chris Clark
935a463d3b Merge pull request #407 from clark800/rangeset
Add support for efficient range checking to RangeSet
2015-07-09 13:57:47 -07:00
Chris Clark
f2f4173d7b Add support for efficient range checking to RangeSet 2015-07-09 13:51:27 -07:00
Chris Clark
941aaf6d8b Add counterparty option to getTransactions 2015-07-09 13:46:12 -07:00
Chris Clark
1068b68568 Merge pull request #404 from clark800/options-schemas
Add schemas for options for each ledger request
2015-07-08 16:59:50 -07:00
Chris Clark
e76b9a9051 Add schemas for options for each ledger request 2015-07-08 16:49:47 -07:00
Chris Clark
410ac117f6 Merge pull request #402 from clark800/get-ledger-version
Add ledgerVersion option to getSettings
2015-07-08 16:23:45 -07:00
Chris Clark
fa89c4dee8 Add ledgerVersion option to getSettings 2015-07-08 14:48:29 -07:00
Chris Clark
a464ca2368 Merge pull request #401 from clark800/get-ledger-version
Add getLedgerVersion and unit test
2015-07-08 14:21:16 -07:00
Chris Clark
befd89c3d7 Add getLedgerVersion and unit test 2015-07-08 14:05:25 -07:00
Chris Clark
49640cf282 Merge pull request #384 from mwshortt/IOU-suffix-removal
Decouple the Amount class from math performed on values
2015-07-08 12:14:05 -07:00
Madeline Shortt
2ac4549712 Rename IOUValue.js and XRPValue.js to lowercase and other fixes 2015-07-08 11:49:15 -07:00
Madeline Shortt
6bffe06c3b Change this._value to be an instance of IOU or XRP Value 2015-07-08 11:49:14 -07:00
Madeline Shortt
13e9ad45f9 Remove BigNumber completely from Amount and small changes 2015-07-08 11:49:13 -07:00
Madeline Shortt
193fcc9014 Create separate Value classes to decouple currency math and the Amount class 2015-07-08 10:25:45 -07:00
Madeline Shortt
30529b7a04 Remove IOU_SUFFIX from orderbookutils.js and clean up serializedtypes.js 2015-07-08 10:25:45 -07:00
sublimator
38b254e7f0 Merge pull request #399 from clark800/rename
Rename getAccountTransactions to getTransactions
2015-07-08 09:08:16 +07:00
sublimator
c4b98d2139 Merge pull request #398 from clark800/orders
Add makerExchangeRate (quality) to orders, and maker to account orders
2015-07-08 08:55:41 +07:00
Chris Clark
1309b58592 Add makerExchangeRate (quality) to orders, and maker to account orders 2015-07-07 18:28:37 -07:00
Chris Clark
9b9d2dc32b Rename getAccountTransactions to getTransactions 2015-07-07 17:52:28 -07:00
Chris Clark
73020fb8ae Merge pull request #397 from lukeburns/develop
Added issuer to payment example in guide
2015-07-07 14:00:02 -07:00
sublimator
8221db833f Merge pull request #391 from clark800/get-pathfind
Convert getPathFind and add unit test
2015-07-07 13:16:16 +07:00
Chris Clark
09b10d3f2c Convert getPathFind and add unit test 2015-07-06 16:11:23 -07:00
Luke Burns
c31798c7f8 changed currency to USD in submit payment example 2015-07-03 00:43:46 -04:00
Luke Burns
064c5e9e50 added issuer to submit payment example 2015-07-02 23:21:19 -04:00
Chris Clark
077a53475d Merge pull request #392 from clark800/server
Convert server methods, add unit tests, and cleanup
2015-07-02 11:23:29 -07:00
Chris Clark
32ca23a00b Convert server methods, add unit tests, and cleanup 2015-07-02 11:17:06 -07:00
Chris Clark
1a7cdd7d04 Merge pull request #396 from sublimator/eslint-update
Use parser: babel-eslint which supports facebook flow
2015-07-02 10:13:43 -07:00
Nicholas Dudfield
85befa467b Use parser: babel-eslint which supports facebook flow 2015-07-02 18:30:11 +07:00
wltsmrz
9b956e57ac Merge pull request #394 from sublimator/eslint-update
Update eslint
2015-07-02 18:56:40 +08:00
sublimator
93c0c49002 Merge pull request #390 from wltsmrz/refactor-api-deprecation
Refactor API deprecation, fix request constructors with positional args
2015-07-02 17:51:28 +07:00
wltsmrz
44954621e0 Refactor API deprecation, fix request constructors with positional args 2015-07-02 03:46:55 -07:00
Nicholas Dudfield
96e5d484da Update eslint 2015-07-02 17:44:38 +07:00
sublimator
4efe0b920e Merge pull request #388 from clark800/get-orderbook
Convert getOrderBook and add unit test
2015-07-01 12:43:00 +07:00
Chris Clark
d158cc7fc1 Fix other indentation problems 2015-06-30 16:53:48 -07:00
Chris Clark
034cd4eaf6 Fix indentation 2015-06-30 16:36:14 -07:00
Chris Clark
d5f3d90486 Add flipped orderbook to getOrderBook response 2015-06-30 15:43:30 -07:00
Chris Clark
99e076b7dd Sort orderbook orders by price 2015-06-30 15:21:28 -07:00
Chris Clark
8f9aec83f2 Fix counterparty/issuer bug 2015-06-30 13:02:42 -07:00
Chris Clark
87a8745a64 Fix unit tests 2015-06-30 12:52:21 -07:00
Chris Clark
2ab51d7a8a Revert remote.js 2015-06-30 12:12:21 -07:00
Chris Clark
3e0f43e44e Convert getOrderBook and add unit test 2015-06-26 16:27:34 -07:00
Chris Clark
6a763fab18 Merge pull request #385 from clark800/get-orders
Convert getOrders and add unit test
2015-06-26 10:46:41 -07:00
Chris Clark
3fe6726789 Merge pull request #386 from sublimator/rm-message
Remove message.js
2015-06-26 10:30:04 -07:00
Nicholas Dudfield
5b4deabd90 Remove message.js 2015-06-26 15:33:26 +07:00
Chris Clark
84bc7dd4aa Convert getOrders and add unit test 2015-06-25 14:21:39 -07:00
Chris Clark
3960b4e11f Merge pull request #383 from clark800/remove
Add unit test for generateWallet and reorganize API files
2015-06-25 11:43:39 -07:00
Chris Clark
141215fc38 Add unit test for getSettings 2015-06-25 11:23:56 -07:00
Chris Clark
2446e2f6da Add unit test for generateWallet and reorganize API files 2015-06-24 18:03:03 -07:00
Chris Clark
81a9bc0739 Merge pull request #382 from clark800/notifications
Merge notifications functionality into getAccountTransactions
2015-06-24 16:44:53 -07:00
Chris Clark
64e86f403e Merge notifications functionality into getAccountTransactions 2015-06-24 16:14:28 -07:00
Chris Clark
77f1351e5b Merge pull request #381 from clark800/balances
Update getBalances to use getTrustlines
2015-06-24 12:53:58 -07:00
Chris Clark
47a87f3a92 Update getBalances to use getTrustlines 2015-06-24 12:38:13 -07:00
Chris Clark
d2df75dc25 Merge pull request #380 from clark800/trustlines
Add getTrustlines and unit test
2015-06-24 11:36:34 -07:00
Chris Clark
d92fbfb7aa Add getTrustlines and unit test 2015-06-24 11:17:46 -07:00
wltsmrz
1b3be55711 Merge pull request #378 from sublimator/signing-data
Add Transaction.signingData
2015-06-23 20:58:21 +08:00
Nicholas Dudfield
8d98e443c5 Add Transaction.signingData() 2015-06-23 19:52:41 +07:00
Nicholas Dudfield
46121edd62 Remove global install for CI dependencies:
* Add them to package.json
* Fixes weird error with missing `esprima-fb`
2015-06-23 19:47:05 +07:00
sublimator
1a32536ac8 Merge pull request #355 from ripple/validate-uint-hex-input
Validate UInt* hex input
2015-06-22 13:59:17 +07:00
Chris Clark
85bf6891f9 Merge pull request #374 from clark800/empty-strings
Remove empty counterparties in XRP amounts
2015-06-18 17:39:18 -07:00
Chris Clark
1ccca1c4ec Remove empty counterparties in XRP amounts 2015-06-18 17:36:45 -07:00
Geert Weening
22cd70e53f Merge pull request #373 from clark800/get-acct-tx
Convert getAccountTransactions
2015-06-18 17:29:38 -07:00
Chris Clark
d8aad1444e Merge pull request #375 from clark800/flow-check
Use "flow check" to prevent "out of retries" errors
2015-06-18 15:12:56 -07:00
Chris Clark
621dfd9ca5 Use "flow check" to prevent "out of retries" errors 2015-06-18 15:10:16 -07:00
wltsmrz
2ad6a1a77e Validate UInt hex input 2015-06-18 13:50:11 -07:00
Chris Clark
8f37438a08 Convert getAccountTransactions 2015-06-17 18:04:24 -07:00
Chris Clark
ff6ac0333c Merge pull request #369 from clark800/parse-tx
Rewrite transaction parser and add unit test for getTransaction
2015-06-16 16:55:15 -07:00
Chris Clark
1b936d2aa2 Rewrite transaction parser and add unit test for getTransaction 2015-06-16 16:24:21 -07:00
sublimator
df0cff969c Merge pull request #372 from clark800/license
Add license to package.json
2015-06-16 18:33:49 +07:00
Chris Clark
46e2598499 Add license to package.json 2015-06-15 17:28:38 -07:00
Chris Clark
0b32378ab5 Merge pull request #364 from clark800/submit-test
Add unit test for submit
2015-06-15 14:39:19 -07:00
Chris Clark
bf25eb350f Display git log in travis output 2015-06-15 14:32:21 -07:00
Chris Clark
97cea2ce4d Add unit test for submit 2015-06-12 16:54:38 -07:00
Chris Clark
82ed402b16 Merge pull request #370 from clark800/fix-perf-test
Fix perf test
2015-06-12 16:28:24 -07:00
Chris Clark
ca7b69a2a0 Fix perf test 2015-06-12 16:22:05 -07:00
Chris Clark
d9c61a9431 Merge pull request #366 from darkdarkdragon/develop-RT-3462
[FIX] emit empty offers list (RT-3462)
2015-06-10 11:38:25 -07:00
Ivan Tivonenko
3b636ce2d1 [FIX] emit empty offers list (RT-3462)
For autobridged order books, emit 'model' event,
even if offers is empty, to indicate that
orderbook is loaded
2015-06-10 21:10:18 +03:00
wltsmrz
2196352335 Merge pull request #363 from clark800/parallel
Parallelize CircleCI tests
2015-06-10 05:06:11 +08:00
Chris Clark
823d7048ba Parallelize CircleCI tests 2015-06-09 13:13:13 -07:00
Chris Clark
d066e1145d Merge pull request #361 from clark800/prepare-trustline
Add unit tests for prepareTrustline, prepareOrderCancellation, and sign
2015-06-09 13:12:24 -07:00
Chris Clark
fb7021abcc Add unit tests for prepareTrustline, prepareOrderCancellation, and sign 2015-06-09 11:30:51 -07:00
Chris Clark
9c14fb2379 Merge pull request #360 from clark800/prepare-order
Add unit test for prepareOrder and fix bugs
2015-06-08 14:36:13 -07:00
Chris Clark
a114bf42c4 Add unit test for prepareOrder and fix bugs 2015-06-08 14:11:35 -07:00
Chris Clark
87b6c09de3 Merge pull request #358 from clark800/flow-type
Add flow type checking
2015-06-08 14:08:07 -07:00
Chris Clark
f1c95112bd Add circle.yml file 2015-06-08 14:00:08 -07:00
Chris Clark
76d8c8b061 Add flow type checking 2015-06-08 14:00:08 -07:00
Chris Clark
7cbcb9a220 Merge pull request #359 from darkdarkdragon/develop-RT-3445
[FIX] fix AutobridgeCalculator (RT-3445)
2015-06-08 11:08:44 -07:00
Ivan Tivonenko
4433ac57bd [FIX] change var to let and const 2015-06-08 20:38:22 +03:00
Ivan Tivonenko
5379da4874 [FIX] fix AutobridgeCalculator (RT-3445)
pass issuers to AutobridgeCalculator so
it can create offers with right issuers
2015-06-07 01:35:49 +03:00
Chris Clark
d488ce55b3 Merge pull request #357 from clark800/v2-schemas
Update schemas, use schemas for validation, and switch to is-my-json-valid
2015-06-05 15:25:32 -07:00
Chris Clark
908e306f04 Update schemas, use schemas for validation, and switch to is-my-json-valid 2015-06-05 14:58:23 -07:00
Chris Clark
62b5953abe Merge pull request #356 from sublimator/remove-sjcl-custom
Remove sjcl-custom in favor of sjcl-extended module in separate repo
2015-06-04 11:50:10 -07:00
Nicholas Dudfield
35ae968d9f Remove sjcl-custom in favor of sjcl-extended module in separate repo 2015-06-04 16:19:31 +07:00
Chris Clark
22dc39b920 Merge pull request #354 from clark800/v2
Move ripple-rest/api into src/api, exposing RippleAPI
2015-05-29 11:12:39 -07:00
Chris Clark
278331cc4a Move ripple-rest/api into src/api, exposing RippleAPI 2015-05-29 10:49:14 -07:00
wltsmrz
16e3541a10 Merge pull request #353 from clark800/develop
Fix bug in trace message for updateOwnerOffersFundedAmount
2015-05-28 13:54:43 -07:00
Chris Clark
7d7970d318 Fix bug in trace message for updateOwnerOffersFundedAmount 2015-05-27 11:56:27 -07:00
wltsmrz
f4fa10b9c0 Merge pull request #351 from clark800/sjcl-update
Update sjcl and delete custom ripemd160 and montgomery
2015-05-26 15:05:46 -07:00
Chris Clark
e68096bd27 Fix lint errors in sjcl-ecdsa-recoverablepublickey.js 2015-05-26 14:42:30 -07:00
Chris Clark
50cda426eb Update sjcl and delete custom ripemd160, montgomery, and jacobi 2015-05-26 14:42:18 -07:00
Chris Clark
1038421428 Merge pull request #350 from fhinkel/develop
Add missing semicolon in example
2015-05-26 13:56:44 -07:00
Franziska Hinkelmann
a5046ab086 Add missing semicolon in example 2015-05-24 11:40:43 +02:00
Geert Weening
ebbec1954e Merge pull request #348 from clark800/fix-orderbook-crash
Prevent crash when listening for "model" events on the OrderBook clas…
2015-05-21 12:07:47 -07:00
Chris Clark
4021018931 Fix lint errors in orderbook.js 2015-05-21 10:07:24 -07:00
Chris Clark
5824c3cb7c Prevent crash when listening for "model" events on the OrderBook class and an issuer creates an order with one of their issues 2015-05-21 09:53:55 -07:00
Geert Weening
45a1b9471e Merge pull request #328 from ripple/fix-remote-error-log
Fix error logging on Remote for object-type messages
2015-05-21 09:07:48 -07:00
wltsmrz
70bc819665 Lint 2015-05-21 04:17:11 -07:00
sublimator
e05f3e9b9c Merge pull request #343 from ripple/fix-serializedobject-append
Fix serializedobject append for excessively large bytes length
2015-05-21 10:35:20 +07:00
wltsmrz
e93f1ab6f4 Fix serializedobject append for excessively large bytes length 2015-05-20 20:18:17 -07:00
wltsmrz
0878a8ecf0 Fix error logging on Remote
* JSON messages are now parsed in server.js
* Remote._handleMessage expects all messages to be objects rather
  than objects or strings
* Invalid responses from the server will now result in a log
  message that is not entirely vague
* Added Log.setEngine/getEngine. Use Log.setEngine(Log.engines.none)
  to silence logging
2015-05-20 20:08:32 -07:00
sublimator
6ea07139dc Merge pull request #349 from ripple/check-unknown-serialization-fields
Check unknown serialization fields
2015-05-21 09:14:17 +07:00
wltsmrz
55fca2d7d5 Lint 2015-05-20 18:47:05 -07:00
wltsmrz
8596dcef21 Check unknown serialization fields 2015-05-20 18:09:02 -07:00
Geert Weening
bca84d5508 Merge pull request #338 from ripple/fix-tx-summary
Fix transaction summary for transactions that fail format validation
2015-05-20 16:11:27 -07:00
wltsmrz
65f7485497 Lint 2015-05-20 05:00:08 -07:00
wltsmrz
457b02c781 Merge pull request #345 from sublimator/es6-lints
Fix eslint issues
2015-05-20 04:40:43 -07:00
Nicholas Dudfield
c8e0fa85f3 Fix eslint issues. 2015-05-20 16:04:04 +07:00
wltsmrz
5e714f6143 Fix transaction summary for transactions that fail with remoteError 2015-05-15 22:41:29 -07:00
Chris Clark
4ecbf31898 Merge pull request #344 from clark800/babel
Setup babel transpiler
2015-05-15 16:59:32 -07:00
Chris Clark
ea24bf0415 Fix lint errors 2015-05-15 16:45:24 -07:00
Chris Clark
171f8349cb Fix requires in bin directory 2015-05-15 16:34:50 -07:00
Chris Clark
398f8d001f Set up babel transpiler 2015-05-14 16:07:47 -07:00
Chris Clark
e66978fb48 Merge pull request #342 from clark800/remove-cpp
Remove src/cpp and clean up files
2015-05-13 17:13:44 -07:00
Chris Clark
c57d528db7 Fix lint errors in remote-test.js 2015-05-13 16:37:46 -07:00
Chris Clark
0c47310063 Prevent lint checking of moved files 2015-05-13 15:21:20 -07:00
Chris Clark
cb4f6e37a8 Remove src/cpp and clean up files 2015-05-13 15:03:32 -07:00
Geert Weening
ab943f36c3 Merge pull request #337 from clark800/sjcl_npm
Use sjcl npm module, delete sjcl code
2015-05-12 16:14:59 -07:00
Chris Clark
de7fc78ef0 Remove lint errors in sjcl-custom 2015-05-12 15:00:07 -07:00
Chris Clark
9a502580fd Use sjcl npm module, delete sjcl code 2015-05-12 15:00:02 -07:00
Chris Clark
d56e70b995 Merge pull request #340 from clark800/remove-return-value
Remove return value of mergeDirectAndAutobridgedBooks
2015-05-11 16:51:55 -07:00
Chris Clark
e9aaf50d59 Remove return value of mergeDirectAndAutobridgedBooks 2015-05-11 10:49:13 -07:00
Geert Weening
ac0a4f521a Bump version to 0.12.5-rc2 2015-05-05 11:20:08 -07:00
Geert Weening
a3380c5cdd Update release notes 2015-05-05 11:15:31 -07:00
Geert Weening
6a6d2a0787 Merge pull request #339 from boxbag/autobridging-empty-orders
[TASK] add short circuit when there are no direct nor autobridged offers
2015-05-05 09:52:42 -07:00
Bo Chen
64809d9ae2 [TASK] add short circuit when there are no direct nor autobridged offers 2015-05-04 13:11:54 -07:00
Chris Clark
d14b38bc91 Merge pull request #335 from clark800/remove-float
Remove float.js and wallet.js
2015-05-01 13:31:17 -07:00
Chris Clark
440dfb5785 Merge pull request #334 from clark800/remove-config
Remove config singleton (global state)
2015-05-01 13:30:36 -07:00
Chris Clark
d4a4b5f4fb Remove float.js and wallet.js 2015-04-30 18:47:43 -07:00
Chris Clark
0c000a7fee Fix lint errors in amount-test.js 2015-04-30 18:31:59 -07:00
Chris Clark
c655c2a20e Remove config singleton (global state) 2015-04-30 18:30:53 -07:00
Geert Weening
3ba5a18b91 Bump version to 0.12.5-rc1 2015-04-29 11:54:36 -07:00
Geert Weening
bdb3415855 Update release notes 2015-04-29 11:54:05 -07:00
Geert Weening
5ef5bdd9d9 Merge pull request #332 from geertweening/develop
Add offer autobridging
2015-04-29 11:47:05 -07:00
Bo Chen
c7bbce8371 [FEATURE] add offer autobridging 2015-04-29 11:28:14 -07:00
Geert Weening
5e2c26a4a2 Merge branch 'release'
Conflicts:
	npm-shrinkwrap.json
	package.json
2015-04-29 08:50:44 -07:00
Geert Weening
631faa20ec Generate shrinkwrap for node 0.10.38 2015-04-29 08:48:39 -07:00
Geert Weening
2db17ba67c Regenerate shrinkwrap using npm-shrinkwrap
cleans up inconsistencies
2015-04-24 16:22:16 -07:00
Geert Weening
50eca42e35 Bump version to 0.12.4 2015-04-24 16:21:32 -07:00
Geert Weening
f327487157 Update release notes 2015-04-24 16:18:05 -07:00
Geert Weening
85b64b7ac3 Bump version to 0.12.4-rc1 2015-04-24 15:44:08 -07:00
Geert Weening
cf17a9e8d6 Update release notes 2015-04-24 15:44:07 -07:00
Chris Clark
71a1282b89 Remove unused dependency: superagent 2015-04-24 15:44:07 -07:00
Chris Clark
85e1f2f47d Fix lint errors in ledger.js 2015-04-24 15:44:07 -07:00
Chris Clark
1f68eba146 Delete unused crypt.js 2015-04-24 15:44:07 -07:00
Chris Clark
d71873442f Update ripple-wallet-generator version 2015-04-24 15:44:07 -07:00
Chris Clark
10ca2da2d6 Fix lint errors in crypt.js, messages.js, remote.js, message-test.js 2015-04-24 15:44:07 -07:00
Chris Clark
c7ba822320 Improve entropy security and increase CCM tag length 2015-04-24 15:44:06 -07:00
Geert Weening
ef3ce46d00 Bump dependency versions
bignumber 2.0.7
lodash 3.6.0
lru-cache 2.5.2
2015-04-24 15:44:06 -07:00
Geert Weening
c4595e03ce Bump version to 0.12.3 2015-04-24 15:44:06 -07:00
Geert Weening
26a7eb456b Bump version to 0.12.3-rc2 2015-04-24 15:44:06 -07:00
Geert Weening
142a85d6a7 Update release notes 2015-04-24 15:44:06 -07:00
Geert Weening
ef51490a1a Bump version to 0.12.3 2015-04-13 16:27:52 -07:00
Geert Weening
c40d643238 Bump version to 0.12.3-rc2 2015-04-10 14:36:47 -07:00
Geert Weening
6f23c88567 Update release notes 2015-04-10 14:36:15 -07:00
Geert Weening
56958a6242 Merge pull request #324 from clark800/develop
Add Amount.scale
2015-04-10 13:25:57 -07:00
Chris Clark
74dac97b36 Add Amount.scale 2015-04-10 12:56:50 -07:00
Geert Weening
2f2e41c781 Bump version to 0.12.3-rc1 2015-04-08 13:36:23 -07:00
Geert Weening
8c872f71c6 Update release notes 2015-04-08 13:36:01 -07:00
wltsmrz
b40b496866 Merge pull request #322 from clark800/rest1.7
Add getLedgerSequence method
2015-04-08 11:20:46 -07:00
Geert Weening
569fec296e Merge pull request #321 from clark800/paranoia
[FIX] Fail if PRNG has not been seeded with at least 256 bits of entr…
2015-04-08 11:16:47 -07:00
Chris Clark
56d8aa797a Fix lint errors in transaction-test.js and transaction-manager-test.js 2015-04-07 18:08:07 -07:00
Chris Clark
fe7e30b737 [FIX] Fail if PRNG has not been seeded with at least 256 bits of entropy before generating ECDSA signatures 2015-04-07 18:08:02 -07:00
Chris Clark
a114281c60 Update comment about the fee base reference constant 2015-04-07 14:34:26 -07:00
Chris Clark
d09548d04d Add getLedgerSequence 2015-04-07 14:34:22 -07:00
Geert Weening
a02b8e3e5c Merge branch 'release' into develop 2015-04-06 13:00:36 -07:00
Geert Weening
2c3f9ca202 bump version to 0.12.2 2015-04-06 12:57:47 -07:00
Geert Weening
587782820d Merge pull request #318 from sublimator/serial-offender
Fix SerializedObject.append performance issue
2015-04-06 12:54:48 -07:00
Nicholas Dudfield
8fad048569 Update verify_ledger_json.js script:
* Set `Amount.strict_mode = false` in verify_ledger_json.js script
* Don't try and calculate hash of non present accountState
* Fix lint issues
2015-04-04 14:51:35 +07:00
Nicholas Dudfield
f7c35b118e Fix SerializedObject.append perf issue:
* Replace array.concat(array2) with Array.prototype.push.apply
2015-04-04 14:51:12 +07:00
Geert Weening
65a669bbb2 Merge pull request #319 from sublimator/lints
eslint fixes
2015-04-04 00:35:57 -07:00
Nicholas Dudfield
9985acc539 Fix linting issues:
* Fix lint violations in various files
* Use per-file comment directives to make new-cap a warning instead of error
  * sjcl.* don't conform to our standards and eslint exemptions are unweildy
2015-04-04 14:23:41 +07:00
Geert Weening
f1f0a43f21 Bump version to 0.12.1-rc1 2015-03-27 16:56:27 -07:00
Geert Weening
6b856c3cc5 Update release notes 2015-03-27 16:55:44 -07:00
Geert Weening
d92888ed73 Merge pull request #317 from ripple/fix-browser-log
Check that Error.stack is available, fixes logging in browser
2015-03-27 16:52:18 -07:00
wltsmrz
0357840654 Lint 2015-03-27 15:54:26 -07:00
wltsmrz
53cae3a66d Check that stack trace is available, fixes logging in browser 2015-03-27 15:49:13 -07:00
Geert Weening
949a1ca4ae Bump version to 0.12.1 2015-03-26 15:12:14 -07:00
Geert Weening
e667536a5b Bump version to 0.12.1-rc6 2015-03-25 12:15:09 -07:00
Geert Weening
dde000a4bb Update release notes 2015-03-25 12:14:30 -07:00
Geert Weening
aa1f5a8e7d Merge pull request #316 from clark800/fix/to_human_small_number
[FIX] Make Amount.to_human return correct results for very small numbers
2015-03-25 12:09:13 -07:00
Chris Clark
bfbfcc2894 Fix lint errors in amount-test.js 2015-03-25 11:28:13 -07:00
Chris Clark
6abfa759aa [FIX] Make Amount.to_human return correct results for very small numbers 2015-03-25 11:19:25 -07:00
Alan Cohen
7cbac2e757 Merge pull request #315 from ripple/readme-link-fix
Fix link in GUIDES.md
2015-03-24 11:04:31 -07:00
Alan Cohen
1012381d3d Update GUIDES.md 2015-03-24 09:17:43 -07:00
Geert Weening
6de96f62df Merge pull request #314 from lumberj/addflags
[TASK] Add GlobalFreeze and NoFreeze flags
2015-03-23 12:23:05 -07:00
Alan Cohen
e2ed2bdbf6 [TASK] Add GlobalFreeze and NoFreeze flags
See: https://wiki.ripple.com/Ledger_Format#AccountRoot
2015-03-20 14:31:47 -07:00
Geert Weening
e248c54aa5 Merge pull request #312 from clark800/mocha-timeout
Increase timeout for mocha tests because sometimes tests fail on travis ...
2015-03-19 09:09:46 -07:00
Chris Clark
1c9635edad Increase timeout for mocha tests because sometimes tests fail on travis due to taking too long 2015-03-18 17:27:06 -07:00
Geert Weening
25cf6c52e4 Bump version to 0.12.1-rc5 2015-03-18 17:24:24 -07:00
Geert Weening
7859ef6145 Update release notes 2015-03-18 17:23:46 -07:00
Geert Weening
6efaa4ac7e Merge pull request #311 from vhpoet/patch-4
[FIX] Amount: clone in ratio_human, product_human
2015-03-18 17:21:21 -07:00
Vahe Hovhannisyan
19e17a8431 [FIX] Amount: clone in ratio_human, product_human
Amount.ratio_human and Amount.product_human should change and return the cloned Amount object.
2015-03-18 17:09:17 -07:00
Geert Weening
c865ae9734 Bump version to 0.12.1-rc4 2015-03-17 11:19:34 -07:00
Geert Weening
6959f74073 Update release notes 2015-03-17 11:14:36 -07:00
Chris Clark
9f4d21e976 Merge pull request #310 from darkdarkdragon/release-base-decode-fix
[FIX] fix Base:decode
2015-03-17 10:12:07 -07:00
Ivan Tivonenko
719f39c01c [FIX] fix Base:decode
check for invalid input in decoded data
2015-03-17 05:53:02 +02:00
Geert Weening
25bb9c7320 Bump version to 0.12.1-rc3 2015-03-11 11:49:30 -07:00
Geert Weening
a160e16abd Update release notes 2015-03-11 11:48:46 -07:00
Geert Weening
ec31841aa5 Merge pull request #305 from geertweening/add-default-rippling-flag
Add DefaultRipple account flag
2015-03-11 11:41:49 -07:00
wltsmrz
3e249902c4 Add DefaultRipple account flag 2015-03-10 11:44:50 -07:00
Geert Weening
21bb766f06 Update release notes 2015-03-10 11:30:34 -07:00
Geert Weening
a883151400 Bump version to 0.12.1-rc2 2015-03-09 10:06:04 -07:00
Geert Weening
3c7fe82cbd Regenerate shrinkwrap 2015-03-09 10:05:49 -07:00
Geert Weening
899fc09704 Update release notes 2015-03-09 10:03:32 -07:00
Geert Weening
daa45a44b9 Merge pull request #297 from ripple/add-log-info
Add filename and line number to log, use log.warn() for deprecations
2015-03-09 09:45:18 -07:00
Geert Weening
52494628c3 Merge pull request #301 from ripple/amount-strict-mode
Add Amount.strict_mode for toggling range validation
2015-03-06 19:54:58 -08:00
Geert Weening
dbf5d21b72 Merge pull request #303 from clark800/morebasetest
[TASK] Fix issues in base.js and add more tests
2015-03-06 19:53:27 -08:00
wltsmrz
441bd4dfbf Lint remote.js 2015-03-06 19:06:24 -08:00
wltsmrz
8452f05dda Lint log.js 2015-03-06 18:36:27 -08:00
Chris Clark
0d2325e646 [TASK] Fix seed-test.js lint errors 2015-03-06 17:41:42 -08:00
wltsmrz
90329d3d73 Add filename and line number to log, use log.warn() for deprecations 2015-03-06 17:35:24 -08:00
Chris Clark
ca83a142f8 [TASK] Fix issues in base.js and add more tests 2015-03-06 17:33:52 -08:00
wltsmrz
d3b2d3d5c5 Merge pull request #304 from clark800/fix/travis_eslint2
[FIX] Fix bug in .travis.yml generation of list of modified files in pul...
2015-03-06 17:18:55 -08:00
Chris Clark
255177487c [FIX] Fix bug in .travis.yml generation of list of modified files in pull request 2015-03-06 15:53:52 -08:00
wltsmrz
ed0b75bcde Use new Amount(NaN) rather than Amount.NaN() 2015-03-06 15:38:44 -08:00
Chris Clark
06500a7909 Merge pull request #302 from clark800/fix/travis_eslint
[FIX] Don't lint deleted files and pull eslintrc from javascript-style-g...
2015-03-06 13:32:47 -08:00
wltsmrz
6e16bf68ae Lint serializedobject test 2015-03-06 13:01:57 -08:00
Chris Clark
ad22480117 [FIX] Don't lint deleted files and pull eslintrc from javascript-style-guide repo to ensure that it is up to date 2015-03-06 11:20:20 -08:00
wltsmrz
2fcd09072f Lint serializedtypes.js 2015-03-06 01:10:22 -08:00
wltsmrz
f0c785b196 Lint amount.js 2015-03-06 01:06:58 -08:00
Geert Weening
84fe76bada Merge pull request #296 from ripple/fix-currency-parsing
Fix currency parsing of non-alphanumeric and no-currency currencies
2015-03-05 22:32:04 -08:00
wltsmrz
b5ed8f59a7 Add Amount.strict_mode for toggling range validation 2015-03-05 22:21:35 -08:00
Geert Weening
52526f90d7 Merge pull request #298 from clark800/basetest
[TEST] Add unit tests for Base
2015-03-05 13:08:56 -08:00
wltsmrz
99e6e81e65 Merge pull request #299 from boxbag/fix-taker-pays-funded
[FIX] fix taker pays funded calculation
2015-03-05 11:17:43 -08:00
Bo Chen
5af824f5cf [FIX] fix taker pays funded calculation
When calling `parseInt` on a string with scientific notation, it ignores the exponents.
2015-03-05 09:43:27 -08:00
wltsmrz
2166bb2e88 Fix currency parsing of non-alphanumeric and no-currency currencies 2015-03-04 19:10:39 -08:00
Chris Clark
ae884c0200 [TEST] Add unit tests for Base 2015-03-04 13:02:36 -08:00
wltsmrz
423ec7d08a Merge pull request #294 from clark800/baseconverter
Refactor base conversion
2015-03-03 20:23:08 -08:00
Chris Clark
914cd6ecb2 Add unit tests for convertBase 2015-03-03 12:22:42 -08:00
Geert Weening
f221c82859 Merge pull request #293 from boxbag/offer-quality
Offer quality
2015-03-03 10:52:38 -08:00
wltsmrz
d57be723e6 Merge pull request #295 from stevenzeiler/ripple-wallet-version
[TASK] Bump ripple-wallet-generator patch version
2015-03-02 16:58:40 -08:00
Steven Zeiler
777554809a [TASK] Bump ripple-wallet-generator patch version 2015-03-02 16:53:52 -08:00
Chris Clark
f2b63fa4a8 Refactor base conversion 2015-02-27 21:28:23 -08:00
Bo Chen
4d06ce7454 [FIX] fix eslint errors 2015-02-27 16:12:06 -08:00
Bo Chen
8da6ec5fa3 [TASK] reverse skip of order book tests 2015-02-27 09:18:01 -08:00
Bo Chen
2a5a8b498d [FIX] fix handling of quality in order book 2015-02-27 09:18:01 -08:00
Bo Chen
a9b7d7d793 Merge pull request #292 from ripple/revert-286-set-user-agent
Revert "Set User-Agent Header with ripple-lib/{version}"
2015-02-27 09:17:27 -08:00
Bo Chen
6578cf5dd7 Revert "Set User-Agent Header with ripple-lib/{version}" 2015-02-26 14:40:07 -08:00
Geert Weening
2e21e8a43c Merge pull request #290 from ripple/transactionmanager-tests
Add TransactionManager test
2015-02-26 12:48:42 -08:00
Geert Weening
176e1fd9d4 Merge pull request #291 from ripple/update-binary-format
Update binary format
2015-02-26 11:41:34 -08:00
wltsmrz
c3b274b18f Add TransactionManager test 2015-02-25 18:49:59 -08:00
wltsmrz
8e134918fb Update binary format 2015-02-25 18:39:42 -08:00
wltsmrz
2b531d2a1f Disable breaking OrderBook tests 2015-02-25 01:36:12 -08:00
Geert Weening
87317dd54a Merge pull request #289 from ripple/fix-setregularkey-transaction-format
Fix RegularKey requirement in SetRegularKey transaction format
2015-02-24 09:23:48 -08:00
wltsmrz
618548c88d Merge pull request #273 from clark800/feature/eslint
[TASK] Add eslint enforcement to travis.yml
2015-02-23 17:50:41 -08:00
Chris Clark
b62f42006c [TASK] Add eslint enforcement to travis.yml 2015-02-23 16:34:22 -08:00
wltsmrz
c275174f27 Fix RegularKey requirement in SetRegularKey transaction format 2015-02-23 13:09:50 -08:00
Geert Weening
af4ed295e0 Bump version to 0.12.1-rc1 2015-02-23 09:25:42 -08:00
Geert Weening
7614a03ea8 Update release notes 2015-02-23 09:24:13 -08:00
Geert Weening
d9527726b6 Merge pull request #285 from boxbag/orderbook-fix
[FIX] fix order funded amount calculation
2015-02-23 09:16:44 -08:00
Geert Weening
05f4099709 Merge pull request #288 from ripple/fix-setfee-transaction-format
Fix Features field requirement in SetFee transaction format
2015-02-23 09:16:13 -08:00
wltsmrz
a20a649013 Fix Features field requirement in SetFee transaction format 2015-02-20 15:01:05 -08:00
Geert Weening
0e3e64105c Merge pull request #287 from clark800/bignumber_update
[TASK] Update bignumber.js and use new feature to simplify our code
2015-02-20 14:50:59 -08:00
Bo Chen
b2cdb1a6ae [FIX] fix order funded amount calculation 2015-02-20 14:49:47 -08:00
Alan Cohen
812432db96 Merge pull request #286 from lumberj/set-user-agent
Set User-Agent Header with ripple-lib/{version}
2015-02-20 10:23:32 -08:00
Alan Cohen
5b2c4aef2d Set User-Agent Header with ripple-lib/{version}
- This would be helpful for us to analyze usage of ripple-lib for anyone
  using the public rippled
2015-02-20 09:06:02 -08:00
Chris Clark
b7ccf424f4 [TASK] Update bignumber.js and use new feature to simplify our code 2015-02-19 19:51:05 -08:00
Geert Weening
77d5db168b Merge pull request #283 from clark800/feature/amount_sanity
[TASK] Disable parsing native amounts in foating point format
2015-02-17 14:29:38 -08:00
Chris Clark
e80cd1ff55 [TASK] Disable parsing native amounts in foating point format 2015-02-17 14:03:23 -08:00
Geert Weening
4ff25a21f6 Merge pull request #282 from boxbag/orderbook
[TEST] test and refactor orderbooks
2015-02-17 13:35:55 -08:00
Bo Chen
f184a71360 [TEST] test and refactor orderbooks 2015-02-13 13:37:57 -08:00
wltsmrz
fc38a9853d Merge pull request #281 from shekenahglory/develop
fix handling of false parameters in requestLedger
2015-02-11 14:24:27 -08:00
Matthew Fettig
6023efed41 fix handling of false parameters in requestLedger 2015-02-11 14:11:24 -08:00
Geert Weening
2abac6ce5c Bump version to 0.12.0 2015-02-11 12:24:40 -08:00
Geert Weening
53c7705c36 Update release notes 2015-02-11 12:24:00 -08:00
Geert Weening
7059ab65d6 Bump version to 0.12.0-rc3 2015-02-10 16:46:58 -08:00
Geert Weening
e133988b36 Update release notes 2015-02-10 16:46:36 -08:00
Geert Weening
9af27e7964 Merge branch 'develop' into release 2015-02-10 16:44:01 -08:00
Geert Weening
a57b3835fb Merge pull request #279 from ripple/fix-set-funded-amount
Fix IOU value passed to Amount.from_json()
2015-02-10 15:01:18 -08:00
Geert Weening
451cbb809e Disable Travis email notifications 2015-02-09 13:57:48 -08:00
wltsmrz
fd1b64393d Fix IOU value passed to Amount.from_json() 2015-02-09 13:41:40 -08:00
Geert Weening
ed875a35b4 Bump version to 0.12.0-rc2 2015-02-07 11:52:04 -08:00
Geert Weening
e85b0c2122 Update release notes 2015-02-07 11:51:20 -08:00
Geert Weening
f5b192f55f Merge branch 'develop' into release 2015-02-07 11:49:42 -08:00
Geert Weening
ff86d5381d Merge pull request #277 from geertweening/fix/memo_serialization
[FIX] unsymmetric memo serializing
2015-02-07 10:28:08 -08:00
Geert Weening
b63ac4addb [TASK] bump version to 0.12.0-rc1 2015-02-06 15:10:03 -08:00
Geert Weening
3e1a66d617 [DOC] update release notes 2015-02-06 15:09:02 -08:00
Geert Weening
93ed5a8cae Merge pull request #278 from ripple/fix-tests
Fix server-test for updated WS
2015-02-05 14:30:06 -08:00
wltsmrz
2e6e8807be Fix server-test for updated WS 2015-02-05 13:51:25 -08:00
Geert Weening
1ed36fabdb [FIX] unsymmetric memo serializing
treat memos as unknown binary, with optionally parsing unsynthesized hint fields
2015-02-05 11:13:37 -08:00
Geert Weening
8dc40ee379 Merge pull request #276 from ripple/bump-dependencies
Bump dependencies versions
2015-02-05 11:13:16 -08:00
Geert Weening
db4c7c89e3 Merge pull request #275 from ripple/mocha-reporter
Use mocha tap reporter only for Travis CI
2015-02-05 11:12:21 -08:00
wltsmrz
f9bc7cc746 Bump dependencies versions 2015-02-05 00:54:07 -08:00
wltsmrz
8f87ed65f9 Use mocha tap reporter only for Travis CI 2015-02-04 23:32:14 -08:00
Geert Weening
39c37631f3 Merge pull request #269 from clark800/feature/amount_cleanup
Cleanup amount.js
2015-02-04 14:06:17 -08:00
Chris Clark
d0fb291c4e Cleanup amount.js 2015-02-04 13:57:57 -08:00
wltsmrz
793523cbe9 Merge pull request #274 from clark800/fix/parse_human
[FIX] Handle invalid input in parse_human
2015-02-04 12:59:05 -08:00
wltsmrz
6da4dd9ecc Remove Gulp lint task, update eslint.json 2015-02-04 02:50:32 -08:00
Geert Weening
79892af8f8 Merge pull request #272 from ripple/fix-taker-gets-funded
Fix taker_gets_funded exceeding offer.TakerGets
2015-02-03 18:35:25 -08:00
Chris Clark
b86790c854 Check for null in isNumber 2015-02-03 18:23:18 -08:00
Chris Clark
c8f18c8c85 [FIX] Handle invalid input in parse_human 2015-02-03 18:06:15 -08:00
wltsmrz
b19ecb4482 Fix taker_gets_funded exceeding offer.TakerGets 2015-02-03 14:50:33 -08:00
wltsmrz
ba9af55aca Merge pull request #268 from ripple/remote-updates
Remote updates
2015-02-02 16:19:45 -08:00
wltsmrz
35d76b3520 Add deprecation warnings to request constructors
* The first argument to request constructor functions should be an
object containing request properties
* Improve Remote test coverage
2015-01-30 21:08:45 -08:00
Geert Weening
a5a0326092 Merge branch 'release' into develop 2015-01-29 15:39:18 -08:00
Geert Weening
a05833f845 bump version to 0.11.0 2015-01-29 14:58:28 -08:00
Bo Chen
c5deb60510 [FIX] update transaction binary parsing to account for XRP delivered amounts 2015-01-29 14:27:11 -08:00
Geert Weening
ff2ff89e3e Merge pull request #271 from boxbag/parse-binary-transaction
[FIX] update transaction binary parsing to account for XRP delivered amounts
2015-01-29 10:52:12 -08:00
Bo Chen
35a346a674 [FIX] update transaction binary parsing to account for XRP delivered amounts 2015-01-29 10:06:10 -08:00
Geert Weening
1217a95c52 Merge pull request #267 from clark800/feature/bignumberjs
[TASK] Refactor to use bignumber.js
2015-01-27 11:43:09 -08:00
Chris Clark
d025b4a0c3 [TASK] Refactor to use bignumber.js 2015-01-27 10:23:33 -08:00
Geert Weening
2cab50f920 [TASK] bump version to 0.11.0-rc3 2015-01-26 14:42:51 -08:00
Geert Weening
b049278dde [DOC] update release notes 2015-01-26 14:42:34 -08:00
Geert Weening
93335e74cb Merge branch 'release' into develop 2015-01-26 14:34:18 -08:00
wltsmrz
2833a7b66e Cleanup, deprecations
* REMOVED Remote storage interface
* REMOVED Remote "ping" configuration
* REMOVED Old/deprecated Remote server configuration
	  (websocket_ip, websocket_port)
* REMOVED browser "online" reconnect listener
2015-01-26 14:00:54 -08:00
wltsmrz
0d05b960f7 Merge pull request #266 from clark800/feature/perftest
[TASK] Add performance test "npm run perf"
2015-01-26 13:31:31 -08:00
Chris Clark
9fd64a9209 [TASK] Add performance test "npm run perf" 2015-01-23 17:03:57 -08:00
wltsmrz
1637d26de3 Merge pull request #264 from lumberj/bug/ledger_select
[BUG] `Request.prototype.ledgerSelect` can accept ledger_index as String
2015-01-21 10:13:06 -08:00
Alan Cohen
525ff9b75e [BUG] Request.prototype.ledgerSelect can accept ledger_index as String 2015-01-21 08:54:40 -08:00
wltsmrz
42e7932f59 Merge pull request #263 from lumberj/feature/requestOffer
[FEATURE] Remote.prototype.requestOffer
2015-01-20 18:40:42 -08:00
Alan Cohen
98f40abfc3 [FEATURE] Remote.prototype.requestOffer
See: https://ripple.com/build/rippled-apis/#ledger-entry

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

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

This reverts commit 72387873b4.

Conflicts:
	test/metadata-test.js
2015-01-06 14:42:24 -08:00
Geert Weening
5d1ff1c912 Merge pull request #245 from ripple/binary-account-tx
Wrap account_tx binary parsing in async.setImmediate
2015-01-06 11:08:22 -08:00
wltsmrz
ea1be4fc50 Wrap account_tx binary parsing in async.setImmediate 2015-01-06 06:51:18 -08:00
Geert Weening
7cc05f0d92 Merge pull request #244 from mehulkar/patch-1
Update link to deprecated API docs
2015-01-05 14:48:12 -08:00
Mehul Kar
54606f3c21 Update link to deprecated API docs 2015-01-05 14:43:05 -08:00
wltsmrz
a5d1705930 Merge pull request #236 from ripple/sum-account-offers
Fix cumulative account order tracking for native amounts
2014-12-26 21:06:42 -08:00
wltsmrz
bfc0fb6c88 Merge pull request #240 from lumberj/task/add_limit_to_bookoffers
[TASK] Allow `limit` option in remote.requestBookOffers
2014-12-24 14:44:15 -08:00
Alan Cohen
d1d4452217 [TASK] Allow limit option in remote.requestBookOffers 2014-12-24 14:24:32 -08:00
wltsmrz
2166a434a3 Merge pull request #241 from ripple/add-jshintrc
Add jshintrc
2014-12-23 23:08:52 -08:00
wltsmrz
1053fa18e1 Add jshintrc 2014-12-23 20:20:31 -08:00
wltsmrz
fa147d467e Merge pull request #239 from ripple/metadata-account-fix
Fix metadata account check
2014-12-23 20:19:09 -08:00
wltsmrz
3f61598d6c Fix metadata account check 2014-12-23 03:22:46 -08:00
wltsmrz
9bf3724ce6 Merge pull request #237 from lumberj/task/fix_jsdoc
[TASK] Fixup the annotation for remote.requestBookOffers
2014-12-22 15:37:28 -08:00
Alan Cohen
c2f27a4deb [TASK] Fixup the annotation for remote.requestBookOffers 2014-12-22 15:31:56 -08:00
wltsmrz
b6b99dde02 Fix cumulative account order tracking for native amounts 2014-12-20 20:43:01 -08:00
Geert Weening
1fd0f4a8fe Merge pull request #234 from ripple/sum-account-offers
Track order funded status based on cumulative account offers
2014-12-19 17:27:38 -08:00
wltsmrz
67d39737a4 Track order funded status based on cumulative account orders 2014-12-19 16:43:37 -08:00
Geert Weening
aef4fe29a3 Merge pull request #233 from lumberj/feature_test/request_book_offers
[FEATURE] Add tests and support for ledger for requestBookOffers (RLJS-115)
2014-12-19 16:39:24 -08:00
Alan Cohen
34c0677c45 [FEATURE] Add tests and support for ledger for requestBookOffers 2014-12-19 16:35:29 -08:00
Geert Weening
3cb4a64b47 [TEST] fix Meta require 2014-12-19 16:29:29 -08:00
Geert Weening
0db0375a5e Merge pull request #227 from clark800/feature/balance_changes
[TASK] Add balance_change array to transaction response, computed from metadata
2014-12-19 15:14:22 -08:00
wltsmrz
47e6bdc644 Merge pull request #231 from clark800/feature/test_compiled_js
[TASK] Refactor tests to support testing compiled javascript
2014-12-19 13:28:03 -08:00
Chris Clark
66c2e27711 [TASK] Refactor tests to support testing compiled javascript 2014-12-19 13:17:02 -08:00
Chris Clark
72387873b4 [TASK] Add Transaction.getBalanceChanges, computed from metadata 2014-12-18 16:12:06 -08:00
Geert Weening
59017bc0bd Merge pull request #229 from ripple/account-txn-id
Add AccountTxnID flag and setter
2014-12-17 12:42:40 -08:00
wltsmrz
2dde114d3d Add AccountTxnID flag and setter 2014-12-16 13:25:32 -08:00
wltsmrz
9e89904f03 Fix undefined log function in Transaction.finalize() 2014-12-12 21:26:54 -08:00
Geert Weening
56d0aca254 Merge pull request #224 from kureus/patch-1
Doc example code syntax error
2014-12-12 11:13:54 -08:00
Dan Quirk
239710cebf Doc example code syntax error 2014-12-11 13:16:14 +00:00
Geert Weening
1eaad617cb [TASK] bump version to 0.10.0-rc2 2014-12-11 00:20:50 -08:00
wltsmrz
3c21994adc [FIX] Fix empty queue check on reconnect 2014-12-10 23:58:00 -08:00
wltsmrz
d15d14e197 [FIX] Fix undefined Transaction.prototype.summary() 2014-12-10 14:17:28 -08:00
Geert Weening
e32694dc79 [TASK] bump version to 0.10.0-rc1 2014-12-10 14:02:48 -08:00
wltsmrz
6ec8124287 [DOC] Update release notes 2014-12-10 14:00:10 -08:00
Geert Weening
2222adfc10 [DOC] update release notes 2014-12-10 13:29:47 -08:00
Geert Weening
fcc2377657 Merge pull request #223 from geertweening/feature/memo_format_type
[FEATURE] improve memo support
2014-12-10 13:08:04 -08:00
Geert Weening
1704ac4ae1 [FEATURE] improve memo support
- add MemoFormat property for memo
- MemoFormat and MemoType must be valid ASCII
- Memo content is converted on the serialization level
- add parsed_* version of Memo content if the parser understand the format
- support `text` and `json` MemoFormat

[FIX] double serialization overriding Memo contents

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

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

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

14
.flowconfig Normal file
View File

@@ -0,0 +1,14 @@
[ignore]
.*/src/api/.*
.*/src/core/.*
.*/dist/.*
.*/test/fixtures/.*
.*/node_modules/flow-bin/.*
[include]
./node_modules/
[libs]
[options]
module.system=node

11
.gitignore vendored
View File

@@ -17,7 +17,7 @@
# Ignore object files.
*.o
build/*.js
build/
tags
bin/rippled
Debug/*.*
@@ -25,6 +25,7 @@ Release/*.*
# Ignore locally installed node_modules
node_modules
!test/node_modules
# Ignore tmp directory.
tmp
@@ -52,3 +53,11 @@ npm-debug.log
# Ignore dist folder, build for bower
dist/
# Ignore flow output directory
out/
# Ignore perf test cache
scripts/cache
eslintrc

View File

@@ -1,3 +1,4 @@
deploy
lib-cov
coverage.html
src
dist/bower

View File

@@ -1,13 +1,9 @@
sudo: false # use faster docker containers
language: node_js
node_js:
- "0.10"
script: npm test --coverage
after_success:
- npm run coveralls
- "0.12"
before_script:
- sh -c "git log | head -12"
script: bin/ci.sh
notifications:
webhooks:
urls:
- https://webhooks.gitter.im/e/d1ec4245f90231619d30
on_success: change # options: [always|never|change] default: always
on_failure: always # options: [always|never|change] default: always
on_start: false # default: false
email: false

View File

@@ -1,163 +1,126 @@
/* eslint-disable no-var, no-param-reassign */
/* these eslint rules are disabled because gulp does not support babel yet */
'use strict';
var _ = require('lodash');
var gulp = require('gulp');
var concat = require('gulp-concat');
var uglify = require('gulp-uglify');
var rename = require('gulp-rename');
var webpack = require('webpack');
var jshint = require('gulp-jshint');
var map = require('map-stream');
var bump = require('gulp-bump');
var argv = require('yargs').argv;
//var header = require('gulp-header');
var pkg = require('./package.json');
var banner = '/*! <%= pkg.name %> - v<%= pkg.version %> - '
+ '<%= new Date().toISOString() %>\n'
+ '<%= pkg.homepage ? "* " + pkg.homepage + "\n" : "" %>'
+ '* Copyright (c) <%= new Date().getFullYear() %> <%= pkg.author.name %>;'
+ ' Licensed <%= pkg.license %> */'
var sjclSrc = [
'src/js/sjcl/core/sjcl.js',
'src/js/sjcl/core/aes.js',
'src/js/sjcl/core/bitArray.js',
'src/js/sjcl/core/codecString.js',
'src/js/sjcl/core/codecHex.js',
'src/js/sjcl/core/codecBase64.js',
'src/js/sjcl/core/codecBytes.js',
'src/js/sjcl/core/sha256.js',
'src/js/sjcl/core/sha512.js',
'src/js/sjcl/core/sha1.js',
'src/js/sjcl/core/ccm.js',
// 'src/js/sjcl/core/cbc.js',
// 'src/js/sjcl/core/ocb2.js',
'src/js/sjcl/core/hmac.js',
'src/js/sjcl/core/pbkdf2.js',
'src/js/sjcl/core/random.js',
'src/js/sjcl/core/convenience.js',
'src/js/sjcl/core/bn.js',
'src/js/sjcl/core/ecc.js',
'src/js/sjcl/core/srp.js',
'src/js/sjcl-custom/sjcl-ecc-pointextras.js',
'src/js/sjcl-custom/sjcl-secp256k1.js',
'src/js/sjcl-custom/sjcl-ripemd160.js',
'src/js/sjcl-custom/sjcl-extramath.js',
'src/js/sjcl-custom/sjcl-montgomery.js',
'src/js/sjcl-custom/sjcl-validecc.js',
'src/js/sjcl-custom/sjcl-ecdsa-canonical.js',
'src/js/sjcl-custom/sjcl-ecdsa-der.js',
'src/js/sjcl-custom/sjcl-ecdsa-recoverablepublickey.js',
'src/js/sjcl-custom/sjcl-jacobi.js'
];
gulp.task('concat-sjcl', function() {
return gulp.src(sjclSrc)
.pipe(concat('sjcl.js'))
.pipe(gulp.dest('./build/'));
});
gulp.task('build', [ 'concat-sjcl' ], function(callback) {
webpack({
function webpackConfig(extension, overrides) {
overrides = overrides || {};
var defaults = {
cache: true,
entry: './src/js/ripple/index.js',
entry: './src/index.js',
output: {
library: 'ripple',
path: './build/',
filename: [ 'ripple-', '.js' ].join(pkg.version)
filename: ['ripple-', extension].join(pkg.version)
},
}, callback);
module: {
loaders: [{
test: /\.js$/,
exclude: /node_modules/,
loader: 'babel-loader?optional=runtime'
}]
}
};
return _.assign({}, defaults, overrides);
}
gulp.task('build', function(callback) {
webpack(webpackConfig('.js'), callback);
});
gulp.task('bower-build', [ 'build' ], function(callback) {
return gulp.src([ './build/ripple-', '.js' ].join(pkg.version))
gulp.task('build-min', ['build'], function() {
return gulp.src(['./build/ripple-', '.js'].join(pkg.version))
.pipe(uglify())
.pipe(rename(['ripple-', '-min.js'].join(pkg.version)))
.pipe(gulp.dest('./build/'));
});
gulp.task('build-debug', function(callback) {
var configOverrides = {debug: true, devtool: 'eval'};
webpack(webpackConfig('-debug.js', configOverrides), callback);
});
/**
* Generate a WebPack external for a given unavailable module which replaces
* that module's constructor with an error-thrower
*/
function buildUseError(cons) {
return ('var {<CONS>:function(){throw new Error('
+ '"Class is unavailable in this build: <CONS>")}}')
.replace(new RegExp('<CONS>', 'g'), cons);
}
gulp.task('build-core', function(callback) {
var configOverrides = {
cache: false,
entry: './src/remote.js',
externals: [{
'./transaction': buildUseError('Transaction'),
'./orderbook': buildUseError('OrderBook'),
'./account': buildUseError('Account'),
'./serializedobject': buildUseError('SerializedObject')
}],
plugins: [
new webpack.optimize.UglifyJsPlugin()
]
};
webpack(webpackConfig('-core.js', configOverrides), callback);
});
gulp.task('bower-build', ['build'], function() {
return gulp.src(['./build/ripple-', '.js'].join(pkg.version))
.pipe(rename('ripple.js'))
.pipe(gulp.dest('./dist/'));
.pipe(gulp.dest('./dist/bower'));
});
gulp.task('bower-build-min', [ 'build-min' ], function(callback) {
return gulp.src([ './build/ripple-', '-min.js' ].join(pkg.version))
gulp.task('bower-build-min', ['build-min'], function() {
return gulp.src(['./build/ripple-', '-min.js'].join(pkg.version))
.pipe(rename('ripple-min.js'))
.pipe(gulp.dest('./dist/'));
.pipe(gulp.dest('./dist/bower'));
});
gulp.task('bower-build-debug', [ 'build-debug' ], function(callback) {
return gulp.src([ './build/ripple-', '-debug.js' ].join(pkg.version))
gulp.task('bower-build-debug', ['build-debug'], function() {
return gulp.src(['./build/ripple-', '-debug.js'].join(pkg.version))
.pipe(rename('ripple-debug.js'))
.pipe(gulp.dest('./dist/'));
.pipe(gulp.dest('./dist/bower'));
});
gulp.task('bower-version', function() {
gulp.src('./dist/bower.json')
gulp.src('./dist/bower/bower.json')
.pipe(bump({version: pkg.version}))
.pipe(gulp.dest('./dist/'));
.pipe(gulp.dest('./dist/bower'));
});
gulp.task('bower', ['bower-build', 'bower-build-min', 'bower-build-debug',
'bower-version']);
gulp.task('watch', function() {
gulp.watch('src/*', ['build-debug']);
});
gulp.task('version-bump', function() {
if (!argv.type) {
throw new Error("No type found, pass it in using the --type argument");
throw new Error('No type found, pass it in using the --type argument');
}
gulp.src('./package.json')
.pipe(bump({type:argv.type}))
.pipe(bump({type: argv.type}))
.pipe(gulp.dest('./'));
});
gulp.task('version-beta', function() {
gulp.src('./package.json')
.pipe(bump({version: pkg.version+'-beta'}))
.pipe(bump({version: pkg.version + '-beta'}))
.pipe(gulp.dest('./'));
});
gulp.task('build-min', [ 'build' ], function(callback) {
return gulp.src([ './build/ripple-', '.js' ].join(pkg.version))
.pipe(uglify())
.pipe(rename([ 'ripple-', '-min.js' ].join(pkg.version)))
.pipe(gulp.dest('./build/'));
});
gulp.task('build-debug', [ 'concat-sjcl' ], function(callback) {
webpack({
cache: true,
entry: './src/js/ripple/index.js',
output: {
library: 'ripple',
path: './build/',
filename: [ 'ripple-', '-debug.js' ].join(pkg.version)
},
debug: true,
devtool: 'eval'
}, callback);
});
gulp.task('lint', function() {
gulp.src('src/js/ripple/*.js')
.pipe(jshint())
.pipe(map(function(file, callback) {
if (!file.jshint.success) {
console.log('\nIn', file.path);
file.jshint.results.forEach(function(err) {
if (err && err.error) {
var col1 = err.error.line + ':' + err.error.character;
var col2 = '[' + err.error.reason + ']';
var col3 = '(' + err.error.code + ')';
while (col1.length < 8) {
col1 += ' ';
}
console.log(' ' + [ col1, col2, col3 ].join(' '));
}
});
}
callback(null, file);
}));
});
gulp.task('watch', function() {
gulp.watch('src/js/ripple/*', [ 'build-debug' ]);
});
gulp.task('default', [ 'concat-sjcl', 'build', 'build-debug', 'build-min' ]);
gulp.task('bower', ['bower-build', 'bower-build-min', 'bower-build-debug', 'bower-version']);
gulp.task('default', ['build', 'build-debug', 'build-min']);

View File

@@ -1,8 +1,204 @@
##0.12.15
+ [Add offer autobridging](https://github.com/ripple/ripple-lib/commit/c7bbce83719c1e8c6a4fae5ca850e7515db1a4a5)
+ [Prevent crash when listening for "model" events on the OrderBook class](https://github.com/ripple/ripple-lib/commit/5824c3cb7cb6bd834d6e037f69943aebf3d83351)
+ [Fix empty order edgecase](https://github.com/ripple/ripple-lib/commit/64809d9ae23dc24f47accd4b4788b48f49880d3e)
+ [Fix AutobridgeCalculator (RT-3445)](https://github.com/ripple/ripple-lib/commit/1fff5ea6dcbcee856536df26f3b9cf1aec3c3b55)
+ [Update sjcl and delete custom ripemd160, montgomery, and jacobi](https://github.com/ripple/ripple-lib/commit/50cda426eb83599c38c0b725e1524a01fc415da2)
+ [Fix transaction summary for transactions that fail with remoteError](https://github.com/ripple/ripple-lib/commit/5e714f6143464d7912f42537acaa553b88eaf6dc)
+ [Fix serializedobject append for excessively large bytes length](https://github.com/ripple/ripple-lib/commit/e93f1ab6f4aaad347450aee75a169af0faa2121c)
+ [Switch to sjcl npm module](https://github.com/ripple/ripple-lib/commit/9a502580fd89ec6a9aa55f4e5847f6a4a2cb5bba)
+ [Add babel transpiler](https://github.com/ripple/ripple-lib/commit/398f8d001f758bf575b959537a17e79e4042d17b)
+ [Remove unused float.js and wallet.js](https://github.com/ripple/ripple-lib/commit/d4a4b5f4fbbf09677a59ce81bace35c6426a2fda)
+ [Remove config singleton to reduce global state](https://github.com/ripple/ripple-lib/commit/c655c2a20ee5d150a4b5a1b6717b9fb81f636025)
##0.12.4
+ [Improve entropy security](https://github.com/ripple/ripple-lib/commit/c7ba822320880037796f57876d1abb4e525648ed)
+ [Remove unused crypt.js file](https://github.com/ripple/ripple-lib/commit/1f68eba1461bca03a4d22872450d15ae5a185334)
##0.12.3
+ [Add getLedgerSequence to Remote](https://github.com/ripple/ripple-lib/commit/d09548d04d3238fca653d482ec1d5faa7254559a)
+ [Improve randomness when generating ECDSA signatures](https://github.com/ripple/ripple-lib/commit/fe7e30b737ead6e71adfa466f5835ba546feab31)
+ [Improve SerializedObject.append performance](https://github.com/ripple/ripple-lib/commit/f7c35b118ebba549a64bcaa1a0629385ec6dbf6f)
+ [Add `Amount.scale`. Multiply an amounts value by a scale factor](https://github.com/ripple/ripple-lib/commit/74dac97b368493056474468520f05671f458a69f)
##0.12.2
+ [Check that stack trace is available, fixes logging in browser](https://github.com/ripple/ripple-lib/commit/53cae3a66d48e88e8a6bbb96d6489ce7b9e22975)
##0.12.1
**Breaking Changes**
+ [Removed support for parsing native amounts in floating point format](https://github.com/ripple/ripple-lib/commit/e80cd1ff55deae9cd5b0ae85be957f86856b887e)
**Changes**
+ [Fix taker pays funded calculation](https://github.com/ripple/ripple-lib/commit/5af824f5cf46c7b9caa58ee0a757bf854d26c8dc)
+ [Fix order funded amount calculation](https://github.com/ripple/ripple-lib/commit/b2cdb1a6aed968b1f306e8dadbd4b7ca37e5aa03)
+ [Fix handling of quality in order book](https://github.com/ripple/ripple-lib/commit/2a5a8b498da60df738ba18d5c265f34771e8a1af)
+ [Fix currency parsing of non-alphanumeric and no-currency currencies](https://github.com/ripple/ripple-lib/commit/2166bb2e88eae8d5f1aba77338f69e8a9edf6a6f)
+ [Add Amount.strict_mode for toggling range validation](https://github.com/ripple/ripple-lib/commit/b5ed8f59a7dab1a17491618b8d9193646c314fb4)
+ [Add filename and line number to log, use log.warn() for deprecations](https://github.com/ripple/ripple-lib/commit/90329d3d73f1a76675063655b407513e32dc048b)
+ [Add GlobalFreeze and NoFreeze flags](https://github.com/ripple/ripple-lib/commit/e2ed2bdbf6f01c7d4d690c2cf0b83fba94558dd7)
+ [Fix handling of falsy parameters in requestLedger](https://github.com/ripple/ripple-lib/commit/6023efed41b7812b3bab660a1c0dc9f0a21000b9)
+ [Fix Base:decode](https://github.com/ripple/ripple-lib/commit/719f39c01c6941d9a650aa94f95617793dd53ea0)
+ [Fix Amount: clone in ratio_human, product_human](https://github.com/ripple/ripple-lib/commit/19e17a8431550cf156b1ad669a19dedfe4e28e4a)
+ [Fix Amount.to_human for very small numbers](https://github.com/ripple/ripple-lib/commit/6abfa759aa09d68074ac558d96c4b126a7cd1719)
+ [Refactor base conversion](https://github.com/ripple/ripple-lib/commit/f2b63fa4a80663eb29472bc6bb1aea8159f1f205)
+ [Update binary transaction format](https://github.com/ripple/ripple-lib/commit/8e134918fb4c22983320a3102f955e4568bb1dfb)
+ [Add DefaultRipple account flag](https://github.com/ripple/ripple-lib/commit/3e249902c4cf25b4da5e75048c84ae391be83b10)
+ [Remove `Features` field requirement in `SetFee` transaction format](https://github.com/ripple/ripple-lib/commit/a20a649013646710c078d4ce1e210f87c7fe74fe)
+ [Remove `RegularKey` field requirement in `SetRegularKey` transaction format](https://github.com/ripple/ripple-lib/commit/c275174f27877ba8f389eb4efe969feb514d6e46)
##0.12.0
**Breaking Changes**
+ REMOVED Remote storage interface
+ REMOVED Remote `ping` configuration
+ REMOVED Old/deprecated Remote server configuration (websocket_ip, websocket_port)
+ REMOVED browser `online` reconnect listener
- [Cleanup, deprecations - 2833a7b6](https://github.com/ripple/ripple-lib/commit/2833a7b66e696dab427464625077f9b93092d0d5)
+ Remove `jsbn` and use `bignumber.js` instead for big number math
+ The `allow_nan` flag has been removed. Results for invalid amounts will always be `NaN`
- [Refactor to use bignumber.js - d025b4a0](https://github.com/ripple/ripple-lib/commit/d025b4a0c3a98a6de27a1bee9573c85347bcd66b)
- [Handle invalid input in parse_human - c8f18c8c](https://github.com/ripple/ripple-lib/commit/c8f18c8c8590b7b48e370e0325b6677b7720294f)
- [Check for null in isNumber - b86790c8](https://github.com/ripple/ripple-lib/commit/b86790c8543c239a532fd7697d4652829019d385)
- [Cleanup amount.js - d0fb291c](https://github.com/ripple/ripple-lib/commit/d0fb291c4e330193a244902156f1d74730da357d)
**Changes**
+ [Add deprecation warnings to request constructors. The first argument to request constructor functions should be an object containing request properties](https://github.com/ripple/ripple-lib/commit/35d76b3520934285f80059c1badd6c522539104c)
+ [Fix taker_gets_funded exceeding offer.TakerGets](https://github.com/ripple/ripple-lib/commit/b19ecb4482b589d575382b7a5d0480b963383bb1)
+ [Fix unsymmetric memo serializing](https://github.com/ripple/ripple-lib/commit/1ed36fabdbd54f4d31078c2b0eaa3becc0fe2821)
+ [Fix IOU value passed to `Amount.from_json()`](https://github.com/ripple/ripple-lib/commit/fd1b64393dffb3d1819cd40b8d43df43a4db042d)
+ [Update transaction binary parsing to account for XRP delivered amounts](https://github.com/ripple/ripple-lib/commit/35a346a674e6ee1e1e495db93700d55984efc7dd)
+ [Bumped dependencies](https://github.com/ripple/ripple-lib/commit/f9bc7cc746b44b24b61bbe260ae2e9d9617286da)
##0.11.0
+ [Track the funded status of an order based on cumulative account orders](https://github.com/ripple/ripple-lib/commit/67d39737a4d5e0fcd9d9b47b9083ee00e5a9e652) and [67d3973](https://github.com/ripple/ripple-lib/commit/b6b99dde022e1e14c4797e454b1d7fca50e49482)
+ Remove blobvault client from ripple-lib, use the [`ripple-vault-client`](https://github.com/ripple/ripple-vault-client) instead [9b3d62b7](https://github.com/ripple/ripple-lib/commit/9b3d62b765c4c25beae6eb0fa57ef3a07f2581b1)
+ [Add support for `ledger` option in requestBookOffers](https://github.com/ripple/ripple-lib/commit/34c0677c453c409ef0a5b351959abdc176d3bacb)
+ [Add support for `limit` option in requestBookOffers](https://github.com/ripple/ripple-lib/commit/d1d4452217c878d0b377d24830b4cd8b3162f6e0)
+ [Add `ledgerSelect` request constructor in `Remote`](https://github.com/ripple/ripple-lib/commit/98f40abfc3aa74dec5067a2d90002756cc8acd01)
+ [Default to binary data for commands that accept the binary flag](https://github.com/ripple/ripple-lib/commit/7cb113fcbcfc1e3e9830a999148b3e78df3387cc)
+ [Fix metadata account check](https://github.com/ripple/ripple-lib/commit/3f61598d6c87e3cc877af60e2d515f9eff73dfe1)
+ [Double check `tes` code before emitting `success`](https://github.com/ripple/ripple-lib/commit/97a8c874903eb7309d8f755955ac80872f670582)
+ [Decrease redundancy in binary account_tx parsing](https://github.com/ripple/ripple-lib/commit/0aba638e6e7f4f6e22cb6424eed3897ebad90a5a)
+ [Abort server connection on unrecoverable TLS error](https://github.com/ripple/ripple-lib/commit/000a2ea00c57157044aeca0fb3f24b37669b163c)
+ [Fix complete ledgers check on subscription that is not initial](https://github.com/ripple/ripple-lib/commit/89de91301e682a46dc60aaacc7ae152e8fe1b7c7)
##0.10.0
+ [Transaction changes](https://github.com/ripple/ripple-lib/pull/221)
+ **Important** `tef*` and `tel*` and errors will no longer be presented as
final. Rather than considering these errors final, ripple-lib will wait until
the `LastLedgerSequence` specified in the transaction is exceeded. This makes
failures more definitive, and ensures that no transaction will resubmit
indefinitely.
+ A new, final tej-class error is introduced to account for transactions that
are locally determined to have expired: `tejMaxLedger`.
+ [Allow per transaction fees to be set, `transaction.setFixedFee()`](https://github.com/ripple/ripple-lib/commit/9b22f279bcbe60ee6bcf4b7fa60a48e9c197a828)
+ [Improve memo support](https://github.com/ripple/ripple-lib/commit/1704ac4ae144c0ce54afad86f644c75a632080b1)
- Add `MemoFormat` property for memo
- Enforce `MemoFormat` and `MemoType` to be valid ASCII
- Support `text` and `json` MemoFormat
+ [Update jscl library](https://github.com/ripple/ripple-lib/commit/3204998fcb6f31d6c90532a737a4adb8a1e420f6)
- Improved entropy by taking advantage of platform crypto
- Use jscl's k256 curve instead of altering the c256 curve with k256 configuration
- **deprecated:** the c256 curve is linked to the k256 curve to provide backwards compatibility, this link will be removed in the future
+ [Fix empty queue check on reconnect](https://github.com/ripple/ripple-lib/commit/3c21994adcf72d1fbd87d453ceb917f9ad6df4ec)
##0.9.4
+ [Normalize offers from book_offers and transaction stream](https://github.com/ripple/ripple-lib/commit/86ed24b94cf7c8929c87db3a63e9bbea7f767e9c)
+ [Fix: Amount.to_human() precision rounding](https://github.com/ripple/ripple-lib/commit/e371cc2c3ceccb3c1cfdf18b98d80093147dd8b2)
+ [Fix: fractional drops in funded taker_pays setter](https://github.com/ripple/ripple-lib/commit/0d7fc0a573a144caac15dd13798b23eeb1f95fb4)
##0.9.3
+ [Change `presubmit` to emit immediately before transaction submit](https://github.com/ripple/ripple-lib/commit/7a1feaa89701bf861ab31ebd8ffdc8d8d1474e29)
+ [Add a "core" browser build of ripple-lib which has a subset of features and smaller file size](https://github.com/ripple/ripple-lib/pull/205)
+ [Update binformat with missing fields from rippled](https://github.com/ripple/ripple-lib/commit/cae980788efb00191bfd0988ed836d60cdf7a9a2)
+ [Wait for transaction validation before returning `tec` error](https://github.com/ripple/ripple-lib/commit/6bdd4b2670906588852fc4dda457607b4aac08e4)
+ [Change default `max_fee` on `Remote` to `1 XRP`](https://github.com/ripple/ripple-lib/commit/d6b1728c23ff85c3cc791bed6982a750641fd95f)
+ [Fix: Request ledger_accept should return the Remote](https://github.com/ripple/ripple-lib/pull/209)
##0.9.2
+ [**Breaking change**: Change accountRequest method signature](https://github.com/ripple/ripple-lib/commit/6f5d1104aa3eb440c518ec4f39e264fdce15fa15)
+ [Add paging behavior for account requests, `account_lines` and `account_offers`](https://github.com/ripple/ripple-lib/commit/722f4e175dbbf378e51b49142d0285f87acb22d7)
+ [Add paging behavior for account requests, `account_lines` and `account_offers`](https://github.com/ripple/ripple-lib/commit/722f4e175dbbf378e51b49142d0285f87acb22d7)
+ [Add max_fee setter to transactions to set max fee the submitter is willing to pay] (https://github.com/ripple/ripple-lib/commit/24587fab9c8ad3840d7aa345a7037b48839e09d7)
@@ -18,7 +214,7 @@ var options = {
ledger: < valid ledger_index or ledger_hash >
}
// The `marker` comes back in an account request if there are more results than are returned
// The `marker` comes back in an account request if there are more results than are returned
// in the current response. The amount of results per response are determined by the `limit`.
if (marker) {
options.marker = < marker >;

View File

@@ -9,15 +9,15 @@ A JavaScript API for interacting with Ripple in Node.js and the browser
###Features
+ Connect to a rippled server in JavaScript (Node.js or browser)
+ Issue [rippled API](https://ripple.com/wiki/JSON_Messages) requests
+ Issue [rippled API](https://ripple.com/build/rippled-apis/) requests
+ Listen to events on the Ripple network (transaction, ledger, etc.)
+ Sign and submit transactions to the Ripple network
###In this file
1. [Installation](README.md#installation)
2. [Quickstart](README.md#quickstart)
3. [Running tests](https://github.com/ripple/ripple-lib#running-tests)
1. [Installation](#installation)
2. [Quick start](#quick-start)
3. [Running tests](#running-tests)
###Additional documentation
@@ -44,10 +44,12 @@ A JavaScript API for interacting with Ripple in Node.js and the browser
$ bower install ripple
```
See the [bower-ripple repo](https://github.com/ripple/bower-ripple) for additional bower instructions
See the [bower-ripple repo](https://github.com/ripple/bower-ripple) for additional bower instructions.
**Building ripple-lib from github**
**Building ripple-lib for browser environments**
ripple-lib uses Gulp to generate browser builds. These steps will generate minified and non-minified builds of ripple-lib in the `build/` directory.
```
$ git clone https://github.com/ripple/ripple-lib
@@ -55,9 +57,13 @@ See the [bower-ripple repo](https://github.com/ripple/bower-ripple) for addition
$ npm run build
```
Then use the minified `build/ripple-*-min.js`
**Restricted browser builds**
##Quickstart
You may generate browser builds that contain a subset of features. To do this, run `./node_modules/.bin/gulp build-<name>`
+ `build-core` Contains the functionality to make requests and listen for events such as `ledgerClose`. Only `ripple.Remote` is currently exposed. Advanced features like transaction submission and orderbook tracking are excluded from this build.
##Quick start
`Remote.js` ([remote.js](https://github.com/ripple/ripple-lib/blob/develop/src/js/ripple/remote.js)) is the point of entry for interacting with rippled

52
bin/ci.sh Executable file
View File

@@ -0,0 +1,52 @@
#!/bin/bash -ex
NODE_INDEX="$1"
TOTAL_NODES="$2"
typecheck() {
npm install -g flow-bin
npm run typecheck
}
lint() {
REPO_URL="https://raw.githubusercontent.com/ripple/javascript-style-guide"
curl "$REPO_URL/es6/eslintrc" > ./eslintrc
echo "plugins: [flowtype]" >> ./eslintrc
node_modules/.bin/eslint --reset -c ./eslintrc $(git --no-pager diff --name-only -M100% --diff-filter=AM --relative $(git merge-base FETCH_HEAD origin/HEAD) FETCH_HEAD | grep "\.js$")
}
unittest() {
npm test --coverage
npm run coveralls
}
oneNode() {
lint
typecheck
unittest
}
twoNodes() {
case "$NODE_INDEX" in
0) lint && unittest;;
1) typecheck;;
*) echo "ERROR: invalid usage"; exit 2;;
esac
}
threeNodes() {
case "$NODE_INDEX" in
0) lint;;
1) typecheck;;
2) unittest;;
*) echo "ERROR: invalid usage"; exit 2;;
esac
}
case "$TOTAL_NODES" in
"") oneNode;;
1) oneNode;;
2) twoNodes;;
3) threeNodes;;
*) echo "ERROR: invalid usage"; exit 2;;
esac

View File

@@ -1,47 +1,52 @@
#!/usr/bin/env node
/* eslint-disable no-var */
'use strict';
var SerializedObject = require('..').SerializedObject;
var SerializedObject = require('../src/js/ripple/serializedobject').SerializedObject;
function main() {
var argv = process.argv.slice(2);
var blob = argv.shift();
var argv = process.argv.slice(2);
var blob;
blob = argv.shift();
if (blob === '-') {
read_input(ready);
} else {
ready();
if (blob === '-') {
read_input(ready);
} else {
ready(blob);
}
}
function read_input(callback) {
tx_json = '';
process.stdin.on('data', function(data) { tx_json += data; });
var tx_json = '';
process.stdin.on('data', function(data) {
tx_json += data;
});
process.stdin.on('end', callback);
process.stdin.resume();
}
function ready() {
function ready(blob) {
var valid_arguments = blob;
if (!valid_arguments) {
console.error('Invalid arguments\n');
print_usage();
} else {
decode();
}
decode(blob);
}
}
function print_usage() {
/* eslint-disable max-len */
console.log(
'Usage: decode_binary.js <hex_blob>\n\n',
'Example: decode_binary.js 120000240000000161D6871AFD498D00000000000000000000000000005553440000000000550FC62003E785DC231A1058A05E56E3F09CF4E668400000000000000A732102AE75B908F0A95F740A7BFA96057637E5C2170BC8DAD13B2F7B52AE75FAEBEFCF811450F97A072F1C4357F1AD84566A609479D927C9428314550FC62003E785DC231A1058A05E56E3F09CF4E6'
);
};
/* eslint-enable max-len */
}
function decode() {
buffer = new SerializedObject(blob);
function decode(blob) {
var buffer = new SerializedObject(blob);
console.log(buffer.to_json());
};
}
main();
// vim:sw=2:sts=2:ts=8:et

View File

@@ -1,62 +1,23 @@
#!/usr/bin/env node
var Transaction = require('../src/js/ripple/transaction').Transaction;
var argv = process.argv.slice(2);
var verbose;
var secret;
var tx_json;
if (~argv.indexOf('-v')){
argv.splice(argv.indexOf('-v'), 1);
verbose = true;
}
secret = argv.shift();
tx_json = argv.shift();
if (tx_json === '-') {
read_input(ready);
} else {
ready();
}
/* eslint-disable no-var */
'use strict';
var Transaction = require('..').Transaction;
function read_input(callback) {
tx_json = '';
process.stdin.on('data', function(data) { tx_json += data; });
process.stdin.on('end', callback);
var stdin = '';
process.stdin.on('data', function(data) {
stdin += data;
});
process.stdin.on('end', function() {
callback(stdin);
});
process.stdin.resume();
}
function ready() {
var valid_arguments = secret && tx_json;
if (!valid_arguments) {
console.error('Invalid arguments\n');
print_usage();
} else {
var valid_json = true;
try {
tx_json = JSON.parse(tx_json);
} catch(exception) {
valid_json = false;
}
if (!valid_json) {
console.error('Invalid JSON\n');
print_usage();
} else {
sign_transaction();
}
}
}
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,14 +25,14 @@ function print_usage() {
Amount: '200000000',
Fee: '10',
Sequence: 1
})+'\''
);
};
}) + '\''
);
}
function sign_transaction() {
function sign_transaction(tx_json_object, secret, verbose) {
var tx = new Transaction();
tx.tx_json = tx_json;
tx.tx_json = tx_json_object;
tx._secret = secret;
tx.complete();
@@ -81,16 +42,56 @@ function sign_transaction() {
if (verbose) {
var sim = { };
sim.tx_blob = tx.serialize().to_hex();
sim.tx_json = tx.tx_json;
sim.tx_blob = tx.serialize().to_hex();
sim.tx_json = tx.tx_json;
sim.tx_signing_hash = unsigned_hash;
sim.tx_unsigned = unsigned_blob;
sim.tx_unsigned = unsigned_blob;
console.log(JSON.stringify(sim, null, 2));
} else {
console.log(tx.serialize().to_hex());
}
};
}
function ready(tx_json, secret, verbose) {
if (!(tx_json && secret)) {
console.error('Invalid arguments\n');
print_usage();
return;
}
var tx_json_object;
try {
tx_json_object = JSON.parse(tx_json);
} catch(exception) {
console.error('Invalid JSON\n');
print_usage();
return;
}
sign_transaction(tx_json_object, secret, verbose);
}
function main() {
var argv = process.argv.slice(2);
var verbose;
var secret;
var tx_json;
if (~argv.indexOf('-v')) {
argv.splice(argv.indexOf('-v'), 1);
verbose = true;
}
secret = argv.shift();
tx_json = argv.shift();
if (tx_json === '-') {
read_input(function(stdin) {
ready(stdin, secret, verbose);
});
} else {
ready(tx_json, secret, verbose);
}
}
main();
// vim:sw=2:sts=2:ts=8:et

View File

@@ -1,12 +1,16 @@
#!/usr/bin/env node
/* eslint-disable no-var */
'use strict';
var UInt160 = require('..').UInt160;
var UInt160 = require('../').UInt160;
var address = process.argv[2];
function main() {
var address = process.argv[2];
if (address === '-') {
readInput(validateAddress);
} else {
validateAddress(address);
if (address === '-') {
readInput(validateAddress);
} else {
validateAddress(address);
}
}
function readInput(callback) {
@@ -19,8 +23,10 @@ function readInput(callback) {
process.stdin.on('end', function() {
callback(result);
});
};
}
function validateAddress(address) {
process.stdout.write((UInt160.is_valid(address.trim()) ? '0' : '1') + '\r\n');
};
}
main();

7
circle.yml Normal file
View File

@@ -0,0 +1,7 @@
machine:
node:
version: 0.12.0
test:
override:
- bin/ci.sh "$CIRCLE_NODE_INDEX" "$CIRCLE_NODE_TOTAL":
parallel: true

View File

@@ -1,3 +0,0 @@
start newcoin
sleep 4
start index.html

20
docs/BUILD.md Normal file
View File

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

View File

@@ -16,20 +16,9 @@ This file provides step-by-step walkthroughs for some of the most common usages
1. [The ripple-lib README](../README.md)
2. [The ripple-lib API Reference](REFERENCE.md)
##Generating a new Ripple Wallet
```js
var Wallet = require('ripple-lib').Wallet;
var wallet = Wallet.generate();
console.log(wallet);
// { address: 'rEf4sbVobiiDGExrNj2PkNHGMA8eS6jWh3',
// secret: 'shFh4a38EZpEdZxrLifEnVPAoBRce' }
```
##Connecting to the Ripple network
1. [Get ripple-lib](README.md#getting-ripple-lib)
1. [Get ripple-lib](../README.md#installation)
2. Load the ripple-lib module into a Node.js file or webpage:
```js
/* Loading ripple-lib with Node.js */
@@ -48,9 +37,9 @@ This file provides step-by-step walkthroughs for some of the most common usages
servers: [
{ host: 's-west.ripple.com', port: 443, secure: true }
]
}
};
var remote = new Remote({options});
var remote = new Remote(options);
remote.connect(function(err, res) {
/* remote connected, use some remote functions here */
@@ -60,10 +49,37 @@ This file provides step-by-step walkthroughs for some of the most common usages
4. You're connected! Read on to see what to do now.
##Generating a new Ripple Wallet
```js
var ripple = require('ripple-lib');
// subscribing to a server allows for more entropy
var remote = new ripple.Remote({
servers: [
{ host: 's1.ripple.com', port: 443, secure: true }
]
});
remote.connect(function(err, res) {
/* remote connected */
});
// Wait for randomness to have been added.
// The entropy of the random generator is increased
// by random data received from a rippled
remote.once('random', function(err, info) {
var wallet = ripple.Wallet.generate();
console.log(wallet);
// { address: 'rEf4sbVobiiDGExrNj2PkNHGMA8eS6jWh3',
// secret: 'shFh4a38EZpEdZxrLifEnVPAoBRce' }
});
```
##Sending rippled API requests
`Remote` contains functions for constructing a `Request` object.
`Remote` contains functions for constructing a `Request` object.
A `Request` is an `EventEmitter` so you can listen for success or failure events -- or, instead, you can provide a callback.
@@ -124,29 +140,29 @@ See the [wiki](https://ripple.com/wiki/JSON_Messages#subscribe) for details on s
'ledger',
'transactions'
];
var request = remote.requestSubscribe(streams);
request.on('error', function(error) {
console.log('request error: ', error);
});
// the `ledger_closed` and `transaction` will come in on the remote
// since the request for subscribe is finalized after the success return
// the streaming events will still come in, but not on the initial request
remote.on('ledger_closed', function(ledger) {
console.log('ledger_closed: ', JSON.stringify(ledger, null, 2));
});
remote.on('transaction', function(transaction) {
console.log('transaction: ', JSON.stringify(transaction, null, 2));
});
remote.on('error', function(error) {
console.log('remote error: ', error);
});
// fire the request
request.request();
});
@@ -157,10 +173,10 @@ See the [wiki](https://ripple.com/wiki/JSON_Messages#subscribe) for details on s
##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.
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 '1 XRP' or '10.50 USD' to the type of Amount object used by the Ripple network.
```js
/* Loading ripple-lib Remote and Amount modules in Node.js */
/* Loading ripple-lib Remote and Amount modules in Node.js */
var Remote = require('ripple-lib').Remote;
var Amount = require('ripple-lib').Amount;
@@ -171,7 +187,7 @@ var Amount = require('ripple-lib').Amount;
var MY_ADDRESS = 'rrrMyAddress';
var MY_SECRET = 'secret';
var RECIPIENT = 'rrrRecipient';
var AMOUNT = Amount.from_human('1XRP');
var AMOUNT = Amount.from_human('1 USD').set_issuer('rrrIssuer');
var remote = new Remote({ /* Remote options */ });
@@ -179,8 +195,8 @@ remote.connect(function() {
remote.setSecret(MY_ADDRESS, MY_SECRET);
var transaction = remote.createTransaction('Payment', {
account: MY_ADDRESS,
destination: RECIPIENT,
account: MY_ADDRESS,
destination: RECIPIENT,
amount: AMOUNT
});
@@ -201,12 +217,12 @@ Since the fee required for a transaction may change between the time when the or
The [`max_fee`](REFERENCE.md#1-remote-options) option can be used to avoid submitting a transaction to a server that is charging unreasonably high fees.
##4. Submitting a trade offer to the network
##Submitting a trade offer to the network
Submitting a trade offer to the network is similar to submitting a payment transaction. Here is an example for a trade that expires in 24 hours where you are offering to sell 1 USD in exchange for 100 XRP:
Submitting a trade offer to the network is similar to submitting a payment transaction. Here is an example offering to sell 1 USD in exchange for 100 XRP:
```js
/* Loading ripple-lib Remote and Amount modules in Node.js */
/* Loading ripple-lib Remote and Amount modules in Node.js */
var Remote = require('ripple-lib').Remote;
var Amount = require('ripple-lib').Amount;
@@ -225,7 +241,7 @@ remote.connect(function() {
var transaction = remote.createTransaction('OfferCreate', {
account: MY_ADDRESS,
taker_pays: '1',
taker_pays: '100',
taker_gets: '1/USD/' + GATEWAY
});

240
npm-shrinkwrap.json generated
View File

@@ -1,140 +1,164 @@
{
"name": "ripple-lib",
"version": "0.9.0-rc5",
"version": "0.13.0-rc1",
"npm-shrinkwrap-version": "5.4.0",
"node-version": "v0.12.6",
"dependencies": {
"async": {
"version": "0.8.0",
"from": "async@>=0.8.0 <0.9.0"
"version": "0.9.2",
"resolved": "https://registry.npmjs.org/async/-/async-0.9.2.tgz"
},
"babel-runtime": {
"version": "5.8.3",
"resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-5.8.3.tgz",
"dependencies": {
"core-js": {
"version": "0.9.18",
"resolved": "https://registry.npmjs.org/core-js/-/core-js-0.9.18.tgz"
}
}
},
"bignumber.js": {
"version": "2.0.7",
"resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-2.0.7.tgz"
},
"extend": {
"version": "1.2.1",
"from": "extend@>=1.2.1 <1.3.0"
"resolved": "https://registry.npmjs.org/extend/-/extend-1.2.1.tgz"
},
"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",
"https-proxy-agent": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-1.0.0.tgz",
"dependencies": {
"qs": {
"version": "0.6.6",
"from": "qs@0.6.6",
"resolved": "https://registry.npmjs.org/qs/-/qs-0.6.6.tgz"
},
"formidable": {
"version": "1.0.14",
"from": "formidable@1.0.14",
"resolved": "https://registry.npmjs.org/formidable/-/formidable-1.0.14.tgz"
},
"mime": {
"version": "1.2.11",
"from": "mime@1.2.11",
"resolved": "https://registry.npmjs.org/mime/-/mime-1.2.11.tgz"
},
"component-emitter": {
"version": "1.1.2",
"from": "component-emitter@1.1.2",
"resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.1.2.tgz"
},
"methods": {
"version": "1.0.1",
"from": "methods@1.0.1",
"resolved": "https://registry.npmjs.org/methods/-/methods-1.0.1.tgz"
},
"cookiejar": {
"version": "2.0.1",
"from": "cookiejar@2.0.1",
"resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.0.1.tgz"
"agent-base": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/agent-base/-/agent-base-2.0.0.tgz",
"dependencies": {
"semver": {
"version": "4.3.6",
"resolved": "https://registry.npmjs.org/semver/-/semver-4.3.6.tgz"
}
}
},
"debug": {
"version": "1.0.4",
"from": "debug@>=1.0.1 <1.1.0",
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz",
"dependencies": {
"ms": {
"version": "0.6.2",
"from": "ms@0.6.2",
"resolved": "https://registry.npmjs.org/ms/-/ms-0.6.2.tgz"
"version": "0.7.1",
"resolved": "https://registry.npmjs.org/ms/-/ms-0.7.1.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"
"extend": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/extend/-/extend-3.0.0.tgz"
}
}
},
"is-my-json-valid": {
"version": "2.12.0",
"resolved": "https://registry.npmjs.org/is-my-json-valid/-/is-my-json-valid-2.12.0.tgz",
"dependencies": {
"generate-function": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/generate-function/-/generate-function-2.0.0.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",
"generate-object-property": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/generate-object-property/-/generate-object-property-1.2.0.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"
"is-property": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz"
}
}
},
"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"
}
}
"jsonpointer": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/jsonpointer/-/jsonpointer-1.1.0.tgz"
},
"xtend": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.0.tgz"
}
}
},
"lodash": {
"version": "3.10.0",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-3.10.0.tgz"
},
"lru-cache": {
"version": "2.5.2",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.5.2.tgz"
},
"ripple-lib-transactionparser": {
"version": "0.3.2",
"resolved": "https://registry.npmjs.org/ripple-lib-transactionparser/-/ripple-lib-transactionparser-0.3.2.tgz",
"dependencies": {
"bignumber.js": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-1.4.1.tgz"
}
}
},
"ripple-wallet-generator": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/ripple-wallet-generator/-/ripple-wallet-generator-1.0.3.tgz"
},
"simple-asyncify": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/simple-asyncify/-/simple-asyncify-0.1.0.tgz"
},
"sjcl-extended": {
"version": "1.0.3",
"resolved": "git://github.com/ripple/sjcl-extended.git#d8cf8b22e7d97193c54e1f65113e3edcf200ca17",
"dependencies": {
"sjcl": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/sjcl/-/sjcl-1.0.3.tgz"
}
}
},
"ws": {
"version": "0.4.32",
"from": "ws@>=0.4.31 <0.5.0",
"version": "0.7.2",
"resolved": "https://registry.npmjs.org/ws/-/ws-0.7.2.tgz",
"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"
"bufferutil": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/bufferutil/-/bufferutil-1.1.0.tgz",
"dependencies": {
"bindings": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/bindings/-/bindings-1.2.1.tgz"
},
"nan": {
"version": "1.8.4",
"resolved": "https://registry.npmjs.org/nan/-/nan-1.8.4.tgz"
}
}
},
"options": {
"version": "0.0.6",
"from": "options@>=0.0.5"
"resolved": "https://registry.npmjs.org/options/-/options-0.0.6.tgz"
},
"ultron": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/ultron/-/ultron-1.0.2.tgz"
},
"utf-8-validate": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/utf-8-validate/-/utf-8-validate-1.1.0.tgz",
"dependencies": {
"bindings": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/bindings/-/bindings-1.2.1.tgz"
},
"nan": {
"version": "1.8.4",
"resolved": "https://registry.npmjs.org/nan/-/nan-1.8.4.tgz"
}
}
}
}
}

View File

@@ -1,47 +1,65 @@
{
"name": "ripple-lib",
"version": "0.9.2-rc6",
"version": "0.13.0-rc1",
"license": "ISC",
"description": "A JavaScript API for interacting with Ripple in Node.js and the browser",
"files": [
"src/js/*",
"dist/npm/*",
"bin/*",
"build/*",
"test/*",
"Makefile",
"Gulpfile.js"
],
"main": "src/js/ripple",
"main": "dist/npm/",
"directories": {
"test": "test"
},
"dependencies": {
"async": "~0.8.0",
"ws": "~0.4.31",
"async": "~0.9.0",
"babel-runtime": "^5.5.4",
"bignumber.js": "^2.0.3",
"extend": "~1.2.1",
"https-proxy-agent": "^1.0.0",
"is-my-json-valid": "^2.12.0",
"lodash": "^3.1.0",
"lru-cache": "~2.5.0",
"superagent": "^0.18.0",
"ripple-wallet-generator": "1.0.1"
"ripple-lib-transactionparser": "^0.3.2",
"ripple-wallet-generator": "^1.0.3",
"simple-asyncify": "^0.1.0",
"sjcl-extended": "ripple/sjcl-extended#1.0.3",
"ws": "~0.7.1"
},
"devDependencies": {
"mocha": "~1.14.0",
"gulp": "~3.6.2",
"gulp-concat": "~2.2.0",
"gulp-jshint": "~1.5.5",
"gulp-uglify": "~0.3.0",
"gulp-rename": "~1.2.0",
"gulp-bump": "~0.1.10",
"webpack": "~1.1.11",
"map-stream": "~0.1.0",
"istanbul": "~0.2.10",
"assert-diff": "^1.0.1",
"babel": "^5.5.4",
"babel-core": "^5.5.4",
"babel-eslint": "^3.1.23",
"babel-loader": "^5.0.0",
"coveralls": "~2.10.0",
"nock": "^0.34.1",
"eslint": "^0.24.0",
"eslint-plugin-flowtype": "^1.0.0",
"eventemitter2": "^0.4.14",
"flow-bin": "^0.13.1",
"gulp": "~3.8.10",
"gulp-bump": "~0.1.13",
"gulp-rename": "~1.2.0",
"gulp-uglify": "~1.1.0",
"istanbul": "~0.3.5",
"mocha": "~2.1.0",
"webpack": "~1.5.3",
"yargs": "~1.3.1"
},
"scripts": {
"build": "node_modules/.bin/gulp",
"pretest": "node_modules/.bin/gulp concat-sjcl",
"test": "./node_modules/.bin/istanbul test -x build/sjcl.js -x src/js/jsbn/* ./node_modules/mocha/bin/_mocha -- --reporter spec test/*-test.js",
"coveralls": "cat ./coverage/lcov.info | ./node_modules/.bin/coveralls"
"build": "gulp",
"clean": "rm -rf dist/npm && rm -rf build/flow",
"typecheck": "babel --optional runtime --blacklist flow -d build/flow/ src/ && flow check",
"compile": "babel --optional runtime -d dist/npm/ src/ && cp -r src/api/common/schemas/ dist/npm/api/common/schemas/",
"compile-with-source-maps": "babel --optional runtime -s -t -d dist/npm/ src/",
"prepublish": "npm run clean && npm run compile",
"test": "istanbul test _mocha",
"coveralls": "cat ./coverage/lcov.info | coveralls",
"lint": "if ! [ -f eslintrc ]; then curl -o eslintrc 'https://raw.githubusercontent.com/ripple/javascript-style-guide/es6/eslintrc'; echo 'plugins:\n - flowtype' >> eslintrc; fi; eslint --reset -c eslintrc src/",
"perf": "./scripts/perf_test.sh"
},
"repository": {
"type": "git",
@@ -49,6 +67,6 @@
},
"readmeFilename": "README.md",
"engines": {
"node": ">=0.10.0"
"node": ">=0.12.0"
}
}

11
scripts/perf_test.sh Executable file
View File

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

View File

@@ -17,15 +17,15 @@ echo "publish to npm"
npm publish
exit_on_error
rm -rf dist
rm -rf dist/bower
echo ""
echo "publish to bower"
git clone git@github.com:ripple/bower-ripple.git dist
git clone git@github.com:ripple/bower-ripple.git dist/bower
gulp bower
exit_on_error
cd dist
cd dist/bower
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
@@ -40,4 +40,4 @@ exit_on_error
git push origin master
git push --tags origin master
cd ..
cd ..

View File

@@ -17,15 +17,15 @@ echo "publish rc to npm"
npm publish --tag beta
exit_on_error
rm -rf dist
rm -rf dist/bower
echo ""
echo "publish to bower"
git clone git@github.com:ripple/bower-ripple.git dist
git clone git@github.com:ripple/bower-ripple.git dist/bower
gulp bower
exit_on_error
cd dist
cd dist/bower
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
@@ -40,4 +40,4 @@ exit_on_error
git push origin master
git push --tags origin master
cd ..
cd ..

View File

@@ -1,7 +1,7 @@
rm -rf dist
git clone git@github.com:ripple/bower-ripple.git dist
rm -rf dist/bower
git clone git@github.com:ripple/bower-ripple.git dist/bower
gulp bower
cd dist
cd dist/bower
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
@@ -9,4 +9,4 @@ git commit -m "[TASK] add v$version"
git tag "v$version"
git push origin master
git push --tags origin master
cd ..
cd ..

View File

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

View File

@@ -0,0 +1,44 @@
'use strict';
const core = require('./utils').core;
const flagIndices = core.Transaction.set_clear_flags.AccountSet;
const flags = core.Remote.flags.account_root;
const AccountFlags = {
passwordSpent: flags.PasswordSpent,
requireDestinationTag: flags.RequireDestTag,
requireAuthorization: flags.RequireAuth,
disallowIncomingXRP: flags.DisallowXRP,
disableMasterKey: flags.DisableMaster,
noFreeze: flags.NoFreeze,
globalFreeze: flags.GlobalFreeze,
defaultRipple: flags.DefaultRipple
};
const AccountFlagIndices = {
requireDestinationTag: flagIndices.asfRequireDest,
requireAuthorization: flagIndices.asfRequireAuth,
disallowIncomingXRP: flagIndices.asfDisallowXRP,
disableMasterKey: flagIndices.asfDisableMaster,
enableTransactionIDTracking: flagIndices.asfAccountTxnID,
noFreeze: flagIndices.asfNoFreeze,
globalFreeze: flagIndices.asfGlobalFreeze,
defaultRipple: flagIndices.asfDefaultRipple
};
const AccountFields = {
EmailHash: {name: 'emailHash', encoding: 'hex',
length: 32, defaults: '0'},
WalletLocator: {name: 'walletLocator', encoding: 'hex',
length: 64, defaults: '0'},
WalletSize: {name: 'walletSize', defaults: 0},
MessageKey: {name: 'messageKey'},
Domain: {name: 'domain', encoding: 'hex'},
TransferRate: {name: 'transferRate', defaults: 0},
Signers: {name: 'signers'}
};
module.exports = {
AccountFields,
AccountFlagIndices,
AccountFlags
};

89
src/api/common/errors.js Normal file
View File

@@ -0,0 +1,89 @@
/* eslint-disable valid-jsdoc */
'use strict';
/**
* Base class for all errors
*/
function RippleError(message) {
this.message = message;
}
RippleError.prototype = new Error();
RippleError.prototype.name = 'RippleError';
function ValidationError(message) {
this.message = message;
}
ValidationError.prototype = new RippleError();
ValidationError.prototype.name = 'ValidationError';
/**
* Timeout, disconnects and too busy
*/
function NetworkError(message) {
this.message = message;
}
NetworkError.prototype = new RippleError();
NetworkError.prototype.name = 'NetworkError';
/**
* Failed transactions, no paths found, not enough balance, etc.
*/
function RippledNetworkError(message) {
this.message = message !== undefined ? message : 'Cannot connect to rippled';
}
RippledNetworkError.prototype = new NetworkError();
/**
* Failed transactions, no paths found, not enough balance, etc.
*/
function TransactionError(message) {
this.message = message;
}
TransactionError.prototype = new RippleError();
TransactionError.prototype.name = 'TransactionError';
/**
* Asset could not be found
*/
function NotFoundError(message) {
this.message = message;
}
NotFoundError.prototype = new RippleError();
NotFoundError.prototype.name = 'NotFoundError';
function MissingLedgerHistoryError(message) {
this.message = message ||
'Server is missing ledger history in the specified range';
}
MissingLedgerHistoryError.prototype = new RippleError();
MissingLedgerHistoryError.prototype.name = 'MissingLedgerHistoryError';
/**
* Request timed out
*/
function TimeOutError(message) {
this.message = message;
}
TimeOutError.prototype = new RippleError();
TimeOutError.prototype.name = 'TimeOutError';
/**
* API logic failed to do what it intended
*/
function ApiError(message) {
this.message = message;
}
ApiError.prototype = new RippleError();
ApiError.prototype.name = 'ApiError';
module.exports = {
ValidationError: ValidationError,
NetworkError: NetworkError,
TransactionError: TransactionError,
RippledNetworkError: RippledNetworkError,
NotFoundError: NotFoundError,
MissingLedgerHistoryError: MissingLedgerHistoryError,
TimeOutError: TimeOutError,
ApiError: ApiError,
RippleError: RippleError
};

15
src/api/common/index.js Normal file
View File

@@ -0,0 +1,15 @@
'use strict';
const utils = require('./utils');
module.exports = {
core: utils.core,
constants: require('./constants'),
errors: require('./errors'),
validate: require('./validate'),
dropsToXrp: utils.dropsToXrp,
xrpToDrops: utils.xrpToDrops,
toRippledAmount: utils.toRippledAmount,
wrapCatch: utils.wrapCatch,
composeAsync: utils.composeAsync,
convertExceptions: utils.convertExceptions
};

View File

@@ -0,0 +1,63 @@
'use strict';
const _ = require('lodash');
const fs = require('fs');
const path = require('path');
const validator = require('is-my-json-valid');
const core = require('./utils').core;
const ValidationError = require('./errors').ValidationError;
let SCHEMAS = {};
function isValidAddress(address) {
return core.UInt160.is_valid(address);
}
function isValidLedgerHash(ledgerHash) {
return core.UInt256.is_valid(ledgerHash);
}
function loadSchema(filepath) {
try {
return JSON.parse(fs.readFileSync(filepath, 'utf8'));
} catch (e) {
throw new Error('Failed to parse schema: ' + filepath);
}
}
function endsWith(str, suffix) {
return str.indexOf(suffix, str.length - suffix.length) !== -1;
}
function loadSchemas(dir) {
const filenames = fs.readdirSync(dir).filter(name => endsWith(name, '.json'));
const schemas = filenames.map(name => loadSchema(path.join(dir, name)));
return _.indexBy(schemas, 'title');
}
function formatSchemaError(error) {
return error.field + ' ' + error.message
+ (error.value ? ' (' + JSON.stringify(error.value) + ')' : '');
}
function formatSchemaErrors(errors) {
return errors.map(formatSchemaError).join(', ');
}
function schemaValidate(schemaName, object) {
const formats = {address: isValidAddress,
ledgerHash: isValidLedgerHash};
const options = {schemas: SCHEMAS, formats: formats,
verbose: true, greedy: true};
const schema = SCHEMAS[schemaName];
if (schema === undefined) {
throw new Error('schema not found for: ' + schemaName);
}
const validate = validator(schema, options);
const isValid = validate(object);
if (!isValid) {
throw new ValidationError(formatSchemaErrors(validate.errors));
}
}
SCHEMAS = loadSchemas(path.join(__dirname, './schemas'));
module.exports = schemaValidate;

View File

@@ -0,0 +1,8 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "address",
"description": "A Ripple account address",
"type": "string",
"format": "address",
"pattern": "^r[1-9A-HJ-NP-Za-km-z]{25,34}$"
}

View File

@@ -0,0 +1,15 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "adjustment",
"type": "object",
"properties": {
"address": {"$ref": "address"},
"amount": {"$ref": "amount"},
"tag": {
"description": "A string representing an unsigned 32-bit integer most commonly used to refer to a sender's hosted account at a Ripple gateway",
"$ref": "uint32"
}
},
"required": ["address", "amount"],
"additionalProperties": false
}

View File

@@ -0,0 +1,9 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "amount",
"description": "An Amount on the Ripple Protocol, used also for XRP in the ripple-rest API",
"allOf": [
{"$ref": "amountbase"},
{"required": ["value"]}
]
}

View File

@@ -0,0 +1,44 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "amountbase",
"description": "Base class for amount and issue",
"type": "object",
"properties": {
"value": {
"description": "The quantity of the currency, denoted as a string to retain floating point precision",
"$ref": "value"
},
"currency": {
"description": "The three-character code or hex string used to denote currencies",
"$ref": "currency"
},
"counterparty": {
"description": "The Ripple account address of the currency's issuer or gateway",
"$ref": "address"
}
},
"additionalProperties": false,
"required": ["currency"],
"oneOf": [
{
"properties": {
"currency": {
"not": {
"enum": ["XRP"]
}
}
},
"required": ["counterparty"]
},
{
"properties": {
"currency": {
"enum": ["XRP"]
}
},
"not": {
"required": ["counterparty"]
}
}
]
}

View File

@@ -0,0 +1,8 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "blob",
"description": "An uppercase hex string representation of a transaction",
"type": "string",
"minLength": "1",
"pattern": "^[0-9A-F]*$"
}

View File

@@ -0,0 +1,10 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "cancellation",
"type": "object",
"properties": {
"orderSequence": {"$ref": "sequence"}
},
"required": ["orderSequence"],
"additionalProperties": false
}

View File

@@ -0,0 +1,7 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "currency",
"description": "The three-character code or hex string used to denote currencies",
"type": "string",
"pattern": "^([a-zA-Z0-9<>(){}[\\]|?!@#$%^&*]{3}|[A-F0-9]{40})$"
}

View File

@@ -0,0 +1,7 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "hash128",
"description": "The hex representation of a 128-bit hash",
"type": "string",
"pattern": "^[A-F0-9]{32}$"
}

View File

@@ -0,0 +1,7 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "hash256",
"description": "The hex representation of a 256-bit hash",
"type": "string",
"pattern": "^[A-F0-9]{64}$"
}

View File

@@ -0,0 +1,42 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "instructions",
"description": "Instructions for executing a transaction",
"type": "object",
"properties": {
"sequence": {
"description": "The sequence number, relative to the initiating account, of this transaction.",
"$ref": "sequence"
},
"fee": {
"description": "Fixed Fee",
"$ref": "value"
},
"maxFee": {
"description": "Max Fee",
"$ref": "value"
},
"maxLedgerVersion": {
"description": "Highest ledger version number that a transaction can appear in.",
"$ref": "ledgerVersion"
},
"maxLedgerVersionOffset": {
"description": "Offset from current legder version to highest ledger version that a transaction can appear in.",
"type": "integer",
"minimum": 0
}
},
"additionalProperties": false,
"not": {
"anyOf": [
{
"description": "fee and maxFee are mutually exclusive",
"required": ["fee", "maxFee"]
},
{
"description": "maxLedgerVersion and maxLedgerVersionOffset are mutually exclusive",
"required": ["maxLedgerVersion", "maxLedgerVersionOffset"]
}
]
}
}

View File

@@ -0,0 +1,9 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "issue",
"description": "A currency-counterparty pair, or just currency if it's XRP",
"allOf": [
{"$ref": "amountbase"},
{"not": {"required": ["value"]}}
]
}

View File

@@ -0,0 +1,7 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "ledgerVersion",
"description": "A ledger version number",
"type": "integer",
"minimum": 1
}

View File

@@ -0,0 +1,22 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "memo",
"description": "Memo objects represent arbitrary data that can be included in a transaction",
"type": "object",
"properties": {
"type": {
"pattern": "^[A-Za-z0-9\\-._~:/?#[\\]@!$&'()*+,;=%]*$"
},
"format": {
"pattern": "^[A-Za-z0-9\\-._~:/?#[\\]@!$&'()*+,;=%]*$"
},
"data": {
"type": "string"
}
},
"additionalProperties": false,
"anyOf": [
{"required": ["data"]},
{"required": ["type"]}
]
}

View File

@@ -0,0 +1,25 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "order",
"type": "object",
"properties": {
"direction": {
"type": "string",
"enum": ["buy", "sell"]
},
"quantity": {"$ref": "amount"},
"totalPrice": {"$ref": "amount"},
"immediateOrCancel": {"type": "boolean"},
"fillOrKill": {"type": "boolean"},
"passive": {
"description": "If enabled, the offer will not consume offers that exactly match it, and instead becomes an Offer node in the ledger. It will still consume offers that cross it.",
"type": "boolean"
}
},
"required": ["direction", "quantity", "totalPrice"],
"additionalProperties": false,
"not": {
"description": "immediateOrCancel and fillOrKill are mutually exclusive",
"required": ["immediateOrCancel", "fillOrKill"]
}
}

View File

@@ -0,0 +1,11 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "orderbook",
"type": "object",
"properties": {
"base": {"$ref": "issue"},
"counter": {"$ref": "issue"}
},
"required": ["base", "counter"],
"additionalProperties": false
}

View File

@@ -0,0 +1,14 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "orders-options",
"description": "Options for getOrders and getOrderbook",
"type": "object",
"properties": {
"limit": {
"type": "integer",
"minimum": 1
},
"ledgerVersion": {"$ref": "ledgerVersion"}
},
"additionalProperties": false
}

View File

@@ -0,0 +1,31 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "pathfind",
"type": "object",
"properties": {
"source": {
"type": "object",
"properties": {
"address": {"$ref": "address"},
"currencies": {
"type": "array",
"items": {
"type": "object",
"properties": {
"currency": {"$ref": "currency"},
"counterparty": {"$ref": "address"}
},
"required": ["currency"],
"additionalProperties": false
},
"uniqueItems": true
}
},
"additionalProperties": false,
"required": ["address"]
},
"destination": {"$ref": "adjustment"}
},
"required": ["source", "destination"],
"additionalProperties": false
}

View File

@@ -0,0 +1,35 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "payment",
"type": "object",
"properties": {
"source": {"$ref": "adjustment"},
"destination": {"$ref": "adjustment"},
"paths": {"type": "string"},
"slippage": {
"description": "An optional cushion for the source_amount to increase the likelihood that the payment will succeed. The source_account will never be charged more than source_amount.value + source_slippage",
"$ref": "value"
},
"memos": {
"type": "array",
"items": {
"$ref": "memo"
}
},
"invoiceID": {
"description": "A 256-bit hash that can be used to identify a particular payment",
"$ref": "hash256"
},
"allowPartialPayment": {
"description": "A boolean that, if set to true, indicates that this payment should go through even if the whole amount cannot be delivered because of a lack of liquidity or funds in the source_account account",
"type": "boolean"
},
"noDirectRipple": {
"description": "A boolean that can be set to true if paths are specified and the sender would like the Ripple Network to disregard any direct paths from the source_account to the destination_account. This may be used to take advantage of an arbitrage opportunity or by gateways wishing to issue balances from a hot wallet to a user who has mistakenly set a trustline directly to the hot wallet",
"type": "boolean"
},
"limitQuality": {"type": "boolean"}
},
"required": ["source", "destination"],
"additionalProperties": false
}

View File

@@ -0,0 +1,8 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "quality",
"description": "Ratio for incoming/outgoing transit fees represented in billionths. (For example, a value of 500 million represents a 0.5:1 ratio.) As a special case, 0 is treated as a 1:1 ratio.",
"type": "integer",
"minimum": 0,
"maximum": 1000000000
}

View File

@@ -0,0 +1,7 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "sequence",
"description": "An account transaction sequence number",
"type": "integer",
"minimum": 1
}

View File

@@ -0,0 +1,10 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "settings-options",
"description": "Options for getSettings and getAccountInfo",
"type": "object",
"properties": {
"ledgerVersion": {"$ref": "ledgerVersion"}
},
"additionalProperties": false
}

View File

@@ -0,0 +1,27 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "settings",
"type": "object",
"properties": {
"passwordSpent": {"type": "boolean"},
"requireDestinationTag": {"type": "boolean"},
"requireAuthorization": {"type": "boolean"},
"disallowIncomingXRP": {"type": "boolean"},
"disableMasterKey": {"type": "boolean"},
"enableTransactionIDTracking": {"type": "boolean"},
"noFreeze": {"type": "boolean"},
"globalFreeze": {"type": "boolean"},
"defaultRipple": {"type": "boolean"},
"emailHash": {"$ref": "hash128"},
"walletLocator": {"$ref": "hash256"},
"walletSize": {"type": "integer"},
"messageKey": {"type": "string"},
"domain": {"type": "string"},
"transferRate": {"type": "integer"},
"signers": {"type": "string"},
"regularKey": {"$ref": "address"}
},
"minProperties": 1,
"maxProperties": 1,
"additionalProperties": false
}

View File

@@ -0,0 +1,7 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "timestamp",
"description": "An ISO 8601 combined date and time timestamp",
"type": "string",
"pattern": "^$|^[0-9]{4}-[0-1][0-9]-[0-3][0-9]T(2[0-3]|[01][0-9]):[0-5][0-9]:[0-5][0-9](Z|[+](2[0-3]|[01][0-9]):[0-5][0-9])$"
}

View File

@@ -0,0 +1,11 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "transaction-options",
"description": "Options for getTransaction",
"type": "object",
"properties": {
"minLedgerVersion": {"$ref": "ledgerVersion"},
"maxLedgerVersion": {"$ref": "ledgerVersion"}
},
"additionalProperties": false
}

View File

@@ -0,0 +1,40 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "transactions-options",
"description": "Options for getTransactions",
"type": "object",
"properties": {
"start": {"$ref": "hash256"},
"limit": {
"type": "integer",
"minimum": 1
},
"minLedgerVersion": {"$ref": "ledgerVersion"},
"maxLedgerVersion": {"$ref": "ledgerVersion"},
"earliestFirst": {"type": "boolean"},
"excludeFailures": {"type": "boolean"},
"initiated": {"type": "boolean"},
"counterparty": {"$ref": "address"},
"types": {
"type": "array",
"items": {
"enum": [
"payment",
"trustline",
"order",
"orderCancellation",
"settings"
]
}
},
"binary": {"type": "boolean"}
},
"additionalProperties": false,
"not": {
"anyOf": [
{"required": ["incoming", "outgoing"]},
{"required": ["start", "minLedgerVersion"]},
{"required": ["start", "maxLedgerVersion"]}
]
}
}

View File

@@ -0,0 +1,17 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "trustline",
"type": "object",
"properties": {
"currency": {"$ref": "currency"},
"counterparty": {"$ref": "address"},
"limit": {"$ref": "value"},
"qualityIn": {"$ref": "quality"},
"qualityOut": {"$ref": "quality"},
"allowRippling": {"type": "boolean"},
"authorized": {"type": "boolean"},
"frozen": {"type": "boolean"}
},
"required": ["currency", "counterparty", "limit"],
"additionalProperties": false
}

View File

@@ -0,0 +1,16 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "trustlines-options",
"description": "Options for getTrustlines and getBalances",
"type": "object",
"properties": {
"counterparty": {"$ref": "address"},
"currency": {"$ref": "currency"},
"limit": {
"type": "integer",
"minimum": 1
},
"ledgerVersion": {"$ref": "ledgerVersion"}
},
"additionalProperties": false
}

View File

@@ -0,0 +1,10 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "tx",
"description": "An object in rippled txJSON format",
"type": "object",
"properties": {
"Account": {"$ref": "address"}
},
"required": ["Account"]
}

View File

@@ -0,0 +1,8 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "uint32",
"description": "A 32-bit unsigned integer",
"type": "integer",
"minimum": 0,
"maximum": 4294967295
}

View File

@@ -0,0 +1,7 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "value",
"description": "A string representation of a non-negative floating point number",
"type": "string",
"pattern": "^[0-9]*[.]?[0-9]+([eE][-+]?[0-9]+)?$"
}

71
src/api/common/utils.js Normal file
View File

@@ -0,0 +1,71 @@
'use strict';
const BigNumber = require('bignumber.js');
const core = require('../../core');
const errors = require('./errors');
function dropsToXrp(drops) {
return (new BigNumber(drops)).dividedBy(1000000.0).toString();
}
function xrpToDrops(xrp) {
return (new BigNumber(xrp)).times(1000000.0).floor().toString();
}
function toRippledAmount(amount) {
if (amount.currency === 'XRP') {
return xrpToDrops(amount.value);
}
return {
currency: amount.currency,
issuer: amount.counterparty ? amount.counterparty : amount.issuer,
value: amount.value
};
}
function wrapCatch(asyncFunction: () => void): () => void {
return function() {
try {
asyncFunction.apply(this, arguments);
} catch (error) {
const callback = arguments[arguments.length - 1];
callback(error);
}
};
}
function composeAsync(wrapper, callback) {
return function(error, data) {
if (error) {
callback(error);
return;
}
let result;
try {
result = wrapper(data);
} catch (exception) {
callback(exception);
return;
}
callback(null, result);
};
}
function convertExceptions(f) {
return function() {
try {
return f.apply(this, arguments);
} catch (error) {
throw new errors.ApiError(error.message);
}
};
}
module.exports = {
core,
dropsToXrp,
xrpToDrops,
toRippledAmount,
wrapCatch,
composeAsync,
convertExceptions
};

View File

@@ -0,0 +1,65 @@
'use strict';
const _ = require('lodash');
const core = require('./utils').core;
const ValidationError = require('./errors').ValidationError;
const schemaValidate = require('./schema-validator');
function error(text) {
return new ValidationError(text);
}
function validateAddressAndSecret(obj) {
const address = obj.address;
const secret = obj.secret;
schemaValidate('address', address);
if (!secret) {
throw error('Parameter missing: secret');
}
try {
if (!core.Seed.from_json(secret).get_key(address)) {
throw error('secret does not match address');
}
} catch (exception) {
throw error('secret does not match address');
}
}
function validateLedgerRange(options) {
if (!_.isUndefined(options.minLedgerVersion)
&& !_.isUndefined(options.maxLedgerVersion)) {
if (Number(options.minLedgerVersion) > Number(options.maxLedgerVersion)) {
throw error('minLedgerVersion must not be greater than maxLedgerVersion');
}
}
}
function validateOptions(schema, options) {
schemaValidate(schema, options);
validateLedgerRange(options);
}
module.exports = {
address: _.partial(schemaValidate, 'address'),
addressAndSecret: validateAddressAndSecret,
currency: _.partial(schemaValidate, 'currency'),
identifier: _.partial(schemaValidate, 'hash256'),
sequence: _.partial(schemaValidate, 'sequence'),
order: _.partial(schemaValidate, 'order'),
orderbook: _.partial(schemaValidate, 'orderbook'),
payment: _.partial(schemaValidate, 'payment'),
pathfind: _.partial(schemaValidate, 'pathfind'),
settings: _.partial(schemaValidate, 'settings'),
trustline: _.partial(schemaValidate, 'trustline'),
txJSON: _.partial(schemaValidate, 'tx'),
blob: _.partial(schemaValidate, 'blob'),
getTransactionsOptions: _.partial(validateOptions, 'transactions-options'),
getSettingsOptions: _.partial(validateOptions, 'settings-options'),
getAccountInfoOptions: _.partial(validateOptions, 'settings-options'),
getTrustlinesOptions: _.partial(validateOptions, 'trustlines-options'),
getBalancesOptions: _.partial(validateOptions, 'trustlines-options'),
getOrdersOptions: _.partial(validateOptions, 'orders-options'),
getOrderbookOptions: _.partial(validateOptions, 'orders-options'),
getTransactionOptions: _.partial(validateOptions, 'transaction-options'),
options: _.partial(validateOptions, 'options'),
instructions: _.partial(schemaValidate, 'instructions')
};

68
src/api/index.js Normal file
View File

@@ -0,0 +1,68 @@
/* @flow */
'use strict';
const _ = require('lodash');
const core = require('./common').core;
const server = require('./server/server');
const connect = server.connect;
const disconnect = server.disconnect;
const getServerInfo = server.getServerInfo;
const getFee = server.getFee;
const isConnected = server.isConnected;
const getLedgerVersion = server.getLedgerVersion;
const getTransaction = require('./ledger/transaction');
const getTransactions = require('./ledger/transactions');
const getTrustlines = require('./ledger/trustlines');
const getBalances = require('./ledger/balances');
const getPaths = require('./ledger/pathfind');
const getOrders = require('./ledger/orders');
const getOrderbook = require('./ledger/orderbook');
const getSettings = require('./ledger/settings');
const getAccountInfo = require('./ledger/accountinfo');
const preparePayment = require('./transaction/payment');
const prepareTrustline = require('./transaction/trustline');
const prepareOrder = require('./transaction/order');
const prepareOrderCancellation = require('./transaction/ordercancellation');
const prepareSettings = require('./transaction/settings');
const sign = require('./transaction/sign');
const submit = require('./transaction/submit');
const errors = require('./common').errors;
const convertExceptions = require('./common').convertExceptions;
const generateWallet = convertExceptions(core.Wallet.generate);
function RippleAPI(options: {}) {
const _options = _.assign({}, options, {automatic_resubmission: false});
this.remote = new core.Remote(_options);
}
RippleAPI.prototype = {
connect,
disconnect,
isConnected,
getServerInfo,
getFee,
getLedgerVersion,
getTransaction,
getTransactions,
getTrustlines,
getBalances,
getPaths,
getOrders,
getOrderbook,
getSettings,
getAccountInfo,
preparePayment,
prepareTrustline,
prepareOrder,
prepareOrderCancellation,
prepareSettings,
sign,
submit,
generateWallet,
errors
};
module.exports = RippleAPI;

View File

@@ -0,0 +1,34 @@
/* @flow */
'use strict';
const utils = require('./utils');
const removeUndefined = require('./parse/utils').removeUndefined;
const validate = utils.common.validate;
const composeAsync = utils.common.composeAsync;
function formatAccountInfo(response) {
const data = response.account_data;
return removeUndefined({
sequence: data.Sequence,
xrpBalance: utils.common.dropsToXrp(data.Balance),
ownerCount: data.OwnerCount,
previousInitiatedTransactionID: data.AccountTxnID,
previousAffectingTransactionID: data.PreviousTxnID,
previousAffectingTransactionLedgerVersion: data.PreviousTxnLgrSeq
});
}
function getAccountInfo(account, options, callback) {
validate.address(account);
validate.getAccountInfoOptions(options);
const request = {
account: account,
ledger: options.ledgerVersion
};
this.remote.requestAccountInfo(request,
composeAsync(formatAccountInfo, callback));
}
module.exports = utils.wrapCatch(getAccountInfo);

View File

@@ -0,0 +1,39 @@
/* @flow */
'use strict';
const _ = require('lodash');
const async = require('async');
const utils = require('./utils');
const getTrustlines = require('./trustlines');
const validate = utils.common.validate;
const composeAsync = utils.common.composeAsync;
function getTrustlineBalanceAmount(trustline) {
return {
currency: trustline.specification.currency,
counterparty: trustline.specification.counterparty,
value: trustline.state.balance
};
}
function formatBalances(balances) {
const xrpBalance = {
currency: 'XRP',
value: balances.xrp
};
return [xrpBalance].concat(
balances.trustlines.map(getTrustlineBalanceAmount));
}
function getBalances(account, options, callback) {
validate.address(account);
validate.getBalancesOptions(options);
const ledgerVersion = options.ledgerVersion
|| this.remote.getLedgerSequence();
async.parallel({
xrp: _.partial(utils.getXRPBalance, this.remote, account, ledgerVersion),
trustlines: _.partial(getTrustlines.bind(this), account, options)
}, composeAsync(formatBalances, callback));
}
module.exports = utils.wrapCatch(getBalances);

View File

@@ -0,0 +1,79 @@
/* @flow */
'use strict';
const _ = require('lodash');
const async = require('async');
const utils = require('./utils');
const parseOrderbookOrder = require('./parse/orderbook-order');
const validate = utils.common.validate;
const composeAsync = utils.common.composeAsync;
// account is to specify a "perspective", which affects which unfunded offers
// are returned
function getBookOffers(remote, account, ledgerVersion, limit,
takerGets, takerPays, callback) {
remote.requestBookOffers(utils.renameCounterpartyToIssuerInOrder({
taker_gets: takerGets,
taker_pays: takerPays,
ledger: ledgerVersion || 'validated',
limit: limit,
taker: account
}), composeAsync(data => data.offers, callback));
}
function isSameIssue(a, b) {
return a.currency === b.currency && a.counterparty === b.counterparty;
}
function directionFilter(direction, order) {
return order.specification.direction === direction;
}
function flipOrder(order) {
const specification = order.specification;
const flippedSpecification = {
quantity: specification.totalPrice,
totalPrice: specification.quantity,
direction: specification.direction === 'buy' ? 'sell' : 'buy'
};
const newSpecification = _.merge({}, specification, flippedSpecification);
return _.merge({}, order, {specification: newSpecification});
}
function alignOrder(base, order) {
const quantity = order.specification.quantity;
return isSameIssue(quantity, base) ? order : flipOrder(order);
}
function formatBidsAndAsks(orderbook, offers) {
// the "base" currency is the currency that you are buying or selling
// the "counter" is the currency that the "base" is priced in
// a "bid"/"ask" is an order to buy/sell the base, respectively
// for bids: takerGets = totalPrice = counter, takerPays = quantity = base
// for asks: takerGets = quantity = base, takerPays = totalPrice = counter
// quality = takerPays / takerGets; price = totalPrice / quantity
// for bids: lowest quality => lowest quantity/totalPrice => highest price
// for asks: lowest quality => lowest totalPrice/quantity => lowest price
// for both bids and asks, lowest quality is closest to mid-market
// we sort the orders so that earlier orders are closer to mid-market
const orders = _.sortBy(offers, 'quality').map(parseOrderbookOrder);
const alignedOrders = orders.map(_.partial(alignOrder, orderbook.base));
const bids = alignedOrders.filter(_.partial(directionFilter, 'buy'));
const asks = alignedOrders.filter(_.partial(directionFilter, 'sell'));
return {bids, asks};
}
function getOrderbook(account, orderbook, options, callback) {
validate.address(account);
validate.orderbook(orderbook);
validate.getOrderbookOptions(options);
const getter = _.partial(getBookOffers, this.remote, account,
options.ledgerVersion, options.limit);
const getOffers = _.partial(getter, orderbook.base, orderbook.counter);
const getReverseOffers = _.partial(getter, orderbook.counter, orderbook.base);
async.parallel([getOffers, getReverseOffers],
composeAsync((data) => formatBidsAndAsks(orderbook, _.flatten(data)),
callback));
}
module.exports = utils.wrapCatch(getOrderbook);

36
src/api/ledger/orders.js Normal file
View File

@@ -0,0 +1,36 @@
/* @flow */
'use strict';
const _ = require('lodash');
const utils = require('./utils');
const validate = utils.common.validate;
const composeAsync = utils.common.composeAsync;
const parseAccountOrder = require('./parse/account-order');
function requestAccountOffers(remote, address, ledgerVersion, options,
marker, limit, callback) {
remote.requestAccountOffers({
account: address,
marker: marker,
limit: utils.clamp(limit, 10, 400),
ledger: ledgerVersion
},
composeAsync((data) => ({
marker: data.marker,
results: data.offers.map(_.partial(parseAccountOrder, address))
}), callback));
}
function getOrders(account, options, callback) {
validate.address(account);
validate.getOrdersOptions(options);
const ledgerVersion = options.ledgerVersion
|| this.remote.getLedgerSequence();
const getter = _.partial(requestAccountOffers, this.remote, account,
ledgerVersion, options);
utils.getRecursive(getter, options.limit,
composeAsync((orders) => _.sortBy(orders,
(order) => order.properties.sequence), callback));
}
module.exports = utils.wrapCatch(getOrders);

View File

@@ -0,0 +1,41 @@
/* @flow */
'use strict';
const utils = require('./utils');
const flags = utils.core.Remote.flags.offer;
const parseAmount = require('./amount');
const BigNumber = require('bignumber.js');
// TODO: remove this function once rippled provides quality directly
function computeQuality(takerGets, takerPays) {
const quotient = new BigNumber(takerPays.value).dividedBy(takerGets.value);
return quotient.toDigits(16, BigNumber.ROUND_HALF_UP).toString();
}
// rippled 'account_offers' returns a different format for orders than 'tx'
// the flags are also different
function parseAccountOrder(address: string, order: Object): Object {
const direction = (order.flags & flags.Sell) === 0 ? 'buy' : 'sell';
const takerGetsAmount = parseAmount(order.taker_gets);
const takerPaysAmount = parseAmount(order.taker_pays);
const quantity = (direction === 'buy') ? takerPaysAmount : takerGetsAmount;
const totalPrice = (direction === 'buy') ? takerGetsAmount : takerPaysAmount;
// note: immediateOrCancel and fillOrKill orders cannot enter the order book
// so we can omit those flags here
const specification = utils.removeUndefined({
direction: direction,
quantity: quantity,
totalPrice: totalPrice,
passive: ((order.flags & flags.Passive) !== 0) || undefined
});
const properties = {
maker: address,
sequence: order.seq,
makerExchangeRate: computeQuality(takerGetsAmount, takerPaysAmount)
};
return {specification, properties};
}
module.exports = parseAccountOrder;

View File

@@ -0,0 +1,46 @@
/* @flow */
'use strict';
const utils = require('./utils');
type Trustline = {
account: string, limit: number, currency: string, quality_in: ?number,
quality_out: ?number, no_ripple: boolean, freeze: boolean, authorized: boolean,
limit_peer: string, no_ripple_peer: boolean, freeze_peer: boolean,
peer_authorized: boolean, balance: any
}
type TrustlineSpecification = {}
type TrustlineCounterParty = {}
type TrustlineState = {balance: number}
type AccountTrustline = {
specification: TrustlineSpecification, counterparty: TrustlineCounterParty,
state: TrustlineState
}
// rippled 'account_lines' returns a different format for
// trustlines than 'tx'
function parseAccountTrustline(trustline: Trustline): AccountTrustline {
const specification = utils.removeUndefined({
limit: trustline.limit,
currency: trustline.currency,
counterparty: trustline.account,
qualityIn: trustline.quality_in || undefined,
qualityOut: trustline.quality_out || undefined,
ripplingDisabled: trustline.no_ripple || undefined,
frozen: trustline.freeze || undefined,
authorized: trustline.authorized || undefined
});
// rippled doesn't provide the counterparty's qualities
const counterparty = utils.removeUndefined({
limit: trustline.limit_peer,
ripplingDisabled: trustline.no_ripple_peer || undefined,
frozen: trustline.freeze_peer || undefined,
authorized: trustline.peer_authorized || undefined
});
const state = {
balance: trustline.balance
};
return {specification, counterparty, state};
}
module.exports = parseAccountTrustline;

View File

@@ -0,0 +1,24 @@
/* @flow */
'use strict';
const utils = require('./utils');
type Amount = string | {currency: string, issuer: string, value: string}
type XRPAmount = {currency: string, value: string}
type IOUAmount = {currency: string, value: string, counterparty: string}
type Output = XRPAmount | IOUAmount
function parseAmount(amount: Amount): Output {
if (typeof amount === 'string') {
return {
currency: 'XRP',
value: utils.dropsToXrp(amount)
};
}
return {
currency: amount.currency,
value: amount.value,
counterparty: amount.issuer
};
}
module.exports = parseAmount;

View File

@@ -0,0 +1,12 @@
/* @flow */
'use strict';
const assert = require('assert');
function parseOrderCancellation(tx: Object): Object {
assert(tx.TransactionType === 'OfferCancel');
return {
orderSequence: tx.OfferSequence
};
}
module.exports = parseOrderCancellation;

View File

@@ -0,0 +1,24 @@
/* @flow */
'use strict';
const AccountFields = require('./utils').constants.AccountFields;
function parseField(info, value) {
if (info.encoding === 'hex' && !info.length) {
return new Buffer(value, 'hex').toString('ascii');
}
return value;
}
function parseFields(data: Object): Object {
const settings = {};
for (const fieldName in AccountFields) {
const fieldValue = data[fieldName];
if (fieldValue !== undefined) {
const info = AccountFields[fieldName];
settings[info.name] = parseField(info, fieldValue);
}
}
return settings;
}
module.exports = parseFields;

View File

@@ -0,0 +1,28 @@
/* @flow */
'use strict';
const assert = require('assert');
const utils = require('./utils');
const parseAmount = require('./amount');
const flags = utils.core.Transaction.flags.OfferCreate;
function parseOrder(tx: Object): Object {
assert(tx.TransactionType === 'OfferCreate');
const direction = (tx.Flags & flags.Sell) === 0 ? 'buy' : 'sell';
const takerGetsAmount = parseAmount(tx.TakerGets);
const takerPaysAmount = parseAmount(tx.TakerPays);
const quantity = (direction === 'buy') ? takerPaysAmount : takerGetsAmount;
const totalPrice = (direction === 'buy') ? takerGetsAmount : takerPaysAmount;
return utils.removeUndefined({
direction: direction,
quantity: quantity,
totalPrice: totalPrice,
passive: ((tx.Flags & flags.Passive) !== 0) || undefined,
immediateOrCancel: ((tx.Flags & flags.ImmediateOrCancel) !== 0)
|| undefined,
fillOrKill: ((tx.Flags & flags.FillOrKill) !== 0) || undefined
});
}
module.exports = parseOrder;

View File

@@ -0,0 +1,43 @@
/* @flow */
'use strict';
const _ = require('lodash');
const utils = require('./utils');
const flags = utils.core.Remote.flags.offer;
const parseAmount = require('./amount');
function parseOrderbookOrder(order: Object): Object {
const direction = (order.Flags & flags.Sell) === 0 ? 'buy' : 'sell';
const takerGetsAmount = parseAmount(order.TakerGets);
const takerPaysAmount = parseAmount(order.TakerPays);
const quantity = (direction === 'buy') ? takerPaysAmount : takerGetsAmount;
const totalPrice = (direction === 'buy') ? takerGetsAmount : takerPaysAmount;
// note: immediateOrCancel and fillOrKill orders cannot enter the order book
// so we can omit those flags here
const specification = utils.removeUndefined({
direction: direction,
quantity: quantity,
totalPrice: totalPrice,
passive: ((order.Flags & flags.Passive) !== 0) || undefined
});
const properties = {
maker: order.Account,
sequence: order.Sequence,
makerExchangeRate: utils.adjustQualityForXRP(order.quality,
takerGetsAmount.currency, takerPaysAmount.currency)
};
const takerGetsFunded = order.taker_gets_funded ?
parseAmount(order.taker_gets_funded) : undefined;
const takerPaysFunded = order.taker_pays_funded ?
parseAmount(order.taker_pays_funded) : undefined;
const available = utils.removeUndefined({
fundedAmount: takerGetsFunded,
priceOfFundedAmount: takerPaysFunded
});
const state = _.isEmpty(available) ? undefined : available;
return utils.removeUndefined({specification, properties, state});
}
module.exports = parseOrderbookOrder;

View File

@@ -0,0 +1,28 @@
/* @flow */
'use strict';
const _ = require('lodash');
const parseAmount = require('./amount');
function parsePaths(paths) {
return paths.map(steps => steps.map(step =>
_.omit(step, ['type', 'type_hex'])));
}
function parsePathfind(sourceAddress: string,
destinationAmount: Object, pathfindResult: Object): Object {
return pathfindResult.alternatives.map(function(alternative) {
return {
source: {
address: sourceAddress,
amount: parseAmount(alternative.source_amount)
},
destination: {
address: pathfindResult.destination_account,
amount: destinationAmount
},
paths: JSON.stringify(parsePaths(alternative.paths_computed))
};
});
}
module.exports = parsePathfind;

View File

@@ -0,0 +1,60 @@
/* @flow */
'use strict';
const assert = require('assert');
const utils = require('./utils');
const parseAmount = require('./amount');
const Transaction = utils.core.Transaction;
function isPartialPayment(tx) {
return (tx.Flags & Transaction.flags.Payment.PartialPayment) !== 0;
}
function isNoDirectRipple(tx) {
return (tx.Flags & Transaction.flags.Payment.NoRippleDirect) !== 0;
}
function isQualityLimited(tx) {
return (tx.Flags & Transaction.flags.Payment.LimitQuality) !== 0;
}
function parsePaymentMemos(tx) {
if (!Array.isArray(tx.Memos) || tx.Memos.length === 0) {
return undefined;
}
return tx.Memos.map((m) => {
return utils.removeUndefined({
type: m.Memo.parsed_memo_type,
format: m.Memo.parsed_memo_format,
data: m.Memo.parsed_memo_data
});
});
}
function parsePayment(tx: Object): Object {
assert(tx.TransactionType === 'Payment');
const source = {
address: tx.Account,
amount: parseAmount(tx.SendMax || tx.Amount),
tag: tx.SourceTag
};
const destination = {
address: tx.Destination,
amount: parseAmount(tx.Amount),
tag: tx.DestinationTag
};
return utils.removeUndefined({
source: utils.removeUndefined(source),
destination: utils.removeUndefined(destination),
memos: parsePaymentMemos(tx),
invoiceID: tx.InvoiceID,
paths: tx.Paths ? JSON.stringify(tx.Paths) : undefined,
allowPartialPayment: isPartialPayment(tx) || undefined,
noDirectRipple: isNoDirectRipple(tx) || undefined,
limitQuality: isQualityLimited(tx) || undefined
});
}
module.exports = parsePayment;

View File

@@ -0,0 +1,61 @@
/* @flow */
'use strict';
const _ = require('lodash');
const assert = require('assert');
const AccountFlags = require('./utils').constants.AccountFlags;
const parseFields = require('./fields');
function getAccountRootModifiedNode(tx: Object) {
const modifiedNodes = tx.meta.AffectedNodes.filter(node =>
node.ModifiedNode.LedgerEntryType === 'AccountRoot');
assert(modifiedNodes.length === 1);
return modifiedNodes[0].ModifiedNode;
}
function parseFlags(tx: Object) {
const settings = {};
if (tx.TransactionType !== 'AccountSet') {
return settings;
}
const node = getAccountRootModifiedNode(tx);
const oldFlags = _.get(node.PreviousFields, 'Flags');
const newFlags = _.get(node.FinalFields, 'Flags');
if (oldFlags !== undefined && newFlags !== undefined) {
const changedFlags = oldFlags ^ newFlags;
const setFlags = newFlags & changedFlags;
const clearedFlags = oldFlags & changedFlags;
_.forEach(AccountFlags, (flagValue, flagName) => {
if (setFlags & flagValue) {
settings[flagName] = true;
} else if (clearedFlags & flagValue) {
settings[flagName] = false;
}
});
}
// enableTransactionIDTracking requires a special case because it
// does not affect the Flags field; instead it adds/removes a field called
// "AccountTxnID" to/from the account root.
const oldField = _.get(node.PreviousFields, 'AccountTxnID');
const newField = _.get(node.FinalFields, 'AccountTxnID');
if (newField && !oldField) {
settings.enableTransactionIDTracking = true;
} else if (oldField && !newField) {
settings.enableTransactionIDTracking = false;
}
return settings;
}
function parseSettings(tx: Object) {
const txType = tx.TransactionType;
assert(txType === 'AccountSet' || txType === 'SetRegularKey');
const regularKey = tx.RegularKey ? {regularKey: tx.RegularKey} : {};
return _.assign(regularKey, parseFlags(tx), parseFields(tx));
}
module.exports = parseSettings;

View File

@@ -0,0 +1,46 @@
/* @flow */
'use strict';
const assert = require('assert');
const utils = require('./utils');
const parsePayment = require('./payment');
const parseTrustline = require('./trustline');
const parseOrder = require('./order');
const parseOrderCancellation = require('./cancellation');
const parseSettings = require('./settings');
function parseTransactionType(type) {
const mapping = {
Payment: 'payment',
TrustSet: 'trustline',
OfferCreate: 'order',
OfferCancel: 'orderCancellation',
AccountSet: 'settings',
SetRegularKey: 'settings'
};
return mapping[type] || null;
}
function parseTransaction(tx: Object): Object {
const type = parseTransactionType(tx.TransactionType);
const mapping = {
'payment': parsePayment,
'trustline': parseTrustline,
'order': parseOrder,
'orderCancellation': parseOrderCancellation,
'settings': parseSettings
};
const parser = mapping[type];
assert(parser !== undefined, 'Unrecognized transaction type');
const specification = parser(tx);
const outcome = utils.parseOutcome(tx);
return utils.removeUndefined({
type: type,
address: tx.Account,
sequence: tx.Sequence,
id: tx.hash,
specification: utils.removeUndefined(specification),
outcome: outcome ? utils.removeUndefined(outcome) : undefined
});
}
module.exports = parseTransaction;

View File

@@ -0,0 +1,33 @@
/* @flow */
'use strict';
const assert = require('assert');
const utils = require('./utils');
const flags = utils.core.Transaction.flags.TrustSet;
function parseFlag(flagsValue, trueValue, falseValue) {
if (flagsValue & trueValue) {
return true;
}
if (flagsValue & falseValue) {
return false;
}
return undefined;
}
function parseTrustline(tx: Object): Object {
assert(tx.TransactionType === 'TrustSet');
return utils.removeUndefined({
limit: tx.LimitAmount.value,
currency: tx.LimitAmount.currency,
counterparty: tx.LimitAmount.issuer,
qualityIn: tx.QualityIn,
qualityOut: tx.QualityOut,
ripplingDisabled: parseFlag(
tx.Flags, flags.SetNoRipple, flags.ClearNoRipple),
frozen: parseFlag(tx.Flags, flags.SetFreeze, flags.ClearFreeze),
authorized: parseFlag(tx.Flags, flags.SetAuth, 0)
});
}
module.exports = parseTrustline;

View File

@@ -0,0 +1,76 @@
/* @flow */
'use strict';
const _ = require('lodash');
const transactionParser = require('ripple-lib-transactionparser');
const toTimestamp = require('../../../core/utils').toTimestamp;
const utils = require('../utils');
const BigNumber = require('bignumber.js');
function adjustQualityForXRP(quality: string, takerGetsCurrency: string,
takerPaysCurrency: string) {
// quality = takerPays.value/takerGets.value
// using drops (1e-6 XRP) for XRP values
const numeratorShift = (takerPaysCurrency === 'XRP' ? -6 : 0);
const denominatorShift = (takerGetsCurrency === 'XRP' ? -6 : 0);
const shift = numeratorShift - denominatorShift;
return shift === 0 ? quality :
(new BigNumber(quality)).shift(shift).toString();
}
function parseTimestamp(tx: {date: string}): string | void {
return tx.date ? (new Date(toTimestamp(tx.date))).toISOString() : undefined;
}
function removeUndefined(obj: Object): Object {
return _.omit(obj, _.isUndefined);
}
function removeEmptyCounterparty(amount) {
if (amount.counterparty === '') {
delete amount.counterparty;
}
}
function removeEmptyCounterpartyInBalanceChanges(balanceChanges) {
_.forEach(balanceChanges, (changes) => {
_.forEach(changes, removeEmptyCounterparty);
});
}
function removeEmptyCounterpartyInOrderbookChanges(orderbookChanges) {
_.forEach(orderbookChanges, (changes) => {
_.forEach(changes, (change) => {
_.forEach(change, removeEmptyCounterparty);
});
});
}
function parseOutcome(tx: Object): ?Object {
if (!tx.validated) {
return undefined;
}
const balanceChanges = transactionParser.parseBalanceChanges(tx.meta);
const orderbookChanges = transactionParser.parseOrderBookChanges(tx.meta);
removeEmptyCounterpartyInBalanceChanges(balanceChanges);
removeEmptyCounterpartyInOrderbookChanges(orderbookChanges);
return {
result: tx.meta.TransactionResult,
timestamp: parseTimestamp(tx),
fee: utils.common.dropsToXrp(tx.Fee),
balanceChanges: balanceChanges,
orderbookChanges: orderbookChanges,
ledgerVersion: tx.ledger_index,
indexInLedger: tx.meta.TransactionIndex
};
}
module.exports = {
parseOutcome,
removeUndefined,
adjustQualityForXRP,
dropsToXrp: utils.common.dropsToXrp,
constants: utils.common.constants,
core: utils.common.core
};

116
src/api/ledger/pathfind.js Normal file
View File

@@ -0,0 +1,116 @@
/* @flow */
'use strict';
const _ = require('lodash');
const async = require('async');
const BigNumber = require('bignumber.js');
const utils = require('./utils');
const validate = utils.common.validate;
const parsePathfind = require('./parse/pathfind');
const NotFoundError = utils.common.errors.NotFoundError;
const composeAsync = utils.common.composeAsync;
type PathFindParams = {
src_currencies?: Array<string>, src_account: string, dst_amount: string,
dst_account?: string
}
function addParams(params: PathFindParams, result: {}) {
return _.assign({}, result, {
source_account: params.src_account,
source_currencies: params.src_currencies,
destination_amount: params.dst_amount
});
}
type PathFind = {
source: {address: string, currencies: Array<string>},
destination: {address: string, amount: string}
}
function requestPathFind(remote, pathfind: PathFind, callback) {
const params: PathFindParams = {
src_account: pathfind.source.address,
dst_account: pathfind.destination.address,
dst_amount: utils.common.toRippledAmount(pathfind.destination.amount)
};
if (typeof params.dst_amount === 'object' && !params.dst_amount.issuer) {
// Convert blank issuer to sender's address
// (Ripple convention for 'any issuer')
// https://ripple.com/build/transactions/
// #special-issuer-values-for-sendmax-and-amount
// https://ripple.com/build/ripple-rest/#counterparties-in-payments
params.dst_amount.issuer = params.dst_account;
}
if (pathfind.source.currencies && pathfind.source.currencies.length > 0) {
params.src_currencies = pathfind.source.currencies.map(amount =>
_.omit(utils.common.toRippledAmount(amount), 'value'));
}
remote.requestRipplePathFind(params,
composeAsync(_.partial(addParams, params), callback));
}
function addDirectXrpPath(paths, xrpBalance) {
// Add XRP "path" only if the source acct has enough XRP to make the payment
const destinationAmount = paths.destination_amount;
if ((new BigNumber(xrpBalance)).greaterThanOrEqualTo(destinationAmount)) {
paths.alternatives.unshift({
paths_computed: [],
source_amount: paths.destination_amount
});
}
return paths;
}
function isRippledIOUAmount(amount) {
// rippled XRP amounts are specified as decimal strings
return (typeof amount === 'object') &&
amount.currency && (amount.currency !== 'XRP');
}
function conditionallyAddDirectXRPPath(remote, address, paths, callback) {
if (isRippledIOUAmount(paths.destination_amount)
|| !_.includes(paths.destination_currencies, 'XRP')) {
callback(null, paths);
} else {
utils.getXRPBalance(remote, address, undefined,
composeAsync(_.partial(addDirectXrpPath, paths), callback));
}
}
function formatResponse(pathfind, paths) {
if (paths.alternatives && paths.alternatives.length > 0) {
const address = pathfind.source.address;
return parsePathfind(address, pathfind.destination.amount, paths);
}
if (!_.includes(paths.destination_currencies,
pathfind.destination.amount.currency)) {
throw new NotFoundError('No paths found. ' +
'The destination_account does not accept ' +
pathfind.destination.amount.currency + ', they only accept: ' +
paths.destination_currencies.join(', '));
} else if (paths.source_currencies && paths.source_currencies.length > 0) {
throw new NotFoundError('No paths found. Please ensure' +
' that the source_account has sufficient funds to execute' +
' the payment in one of the specified source_currencies. If it does' +
' there may be insufficient liquidity in the network to execute' +
' this payment right now');
} else {
throw new NotFoundError('No paths found.' +
' Please ensure that the source_account has sufficient funds to' +
' execute the payment. If it does there may be insufficient liquidity' +
' in the network to execute this payment right now');
}
}
function getPaths(pathfind, callback) {
validate.pathfind(pathfind);
const address = pathfind.source.address;
async.waterfall([
_.partial(requestPathFind, this.remote, pathfind),
_.partial(conditionallyAddDirectXRPPath, this.remote, address)
], composeAsync(_.partial(formatResponse, pathfind), callback));
}
module.exports = utils.wrapCatch(getPaths);

View File

@@ -0,0 +1,40 @@
/* @flow */
'use strict';
const _ = require('lodash');
const utils = require('./utils');
const validate = utils.common.validate;
const parseFields = require('./parse/fields');
const composeAsync = utils.common.composeAsync;
const AccountFlags = utils.common.constants.AccountFlags;
function parseFlags(value) {
const settings = {};
for (const flagName in AccountFlags) {
if (value & AccountFlags[flagName]) {
settings[flagName] = true;
}
}
return settings;
}
function formatSettings(response) {
const data = response.account_data;
const parsedFlags = parseFlags(data.Flags);
const parsedFields = parseFields(data);
return _.assign({}, parsedFlags, parsedFields);
}
function getSettings(account, options, callback) {
validate.address(account);
validate.getSettingsOptions(options);
const request = {
account: account,
ledger: options.ledgerVersion
};
this.remote.requestAccountInfo(request,
composeAsync(formatSettings, callback));
}
module.exports = utils.wrapCatch(getSettings);

View File

@@ -0,0 +1,67 @@
/* @flow */
'use strict';
const _ = require('lodash');
const async = require('async');
const utils = require('./utils');
const parseTransaction = require('./parse/transaction');
const validate = utils.common.validate;
const errors = utils.common.errors;
function attachTransactionDate(remote, tx, callback) {
if (tx.date) {
callback(null, tx);
return;
}
if (!tx.ledger_index) {
callback(new errors.NotFoundError('ledger_index not found in tx'));
return;
}
remote.requestLedger(tx.ledger_index, (error, data) => {
if (error) {
callback(new errors.NotFoundError('Transaction ledger not found'));
} else if (typeof data.ledger.close_time === 'number') {
callback(null, _.assign({date: data.ledger.close_time}, tx));
} else {
callback(new errors.ApiError('Ledger missing close_time'));
}
});
}
function isTransactionInRange(tx, options) {
return (!options.minLedgerVersion
|| tx.ledger_index >= options.minLedgerVersion)
&& (!options.maxLedgerVersion
|| tx.ledger_index <= options.maxLedgerVersion);
}
function getTransaction(identifier, options, callback) {
validate.identifier(identifier);
validate.getTransactionOptions(options);
const remote = this.remote;
const maxLedgerVersion = Math.min(options.maxLedgerVersion,
remote.getLedgerSequence());
function callbackWrapper(error, tx) {
if (error instanceof errors.NotFoundError
&& !utils.hasCompleteLedgerRange(remote,
options.minLedgerVersion, maxLedgerVersion)) {
callback(new errors.MissingLedgerHistoryError('Transaction not found,'
+ ' but the server\'s ledger history is incomplete'));
} else if (!error && !isTransactionInRange(tx, options)) {
callback(new errors.NotFoundError('Transaction not found'));
} else if (error) {
callback(error);
} else {
callback(error, parseTransaction(tx));
}
}
async.waterfall([
_.partial(remote.requestTx.bind(remote), {hash: identifier, binary: false}),
_.partial(attachTransactionDate, remote)
], callbackWrapper);
}
module.exports = utils.wrapCatch(getTransaction);

View File

@@ -0,0 +1,123 @@
/* @flow */
'use strict';
const _ = require('lodash');
const utils = require('./utils');
const parseTransaction = require('./parse/transaction');
const getTransaction = require('./transaction');
const validate = utils.common.validate;
const composeAsync = utils.common.composeAsync;
function parseAccountTxTransaction(tx) {
// rippled uses a different response format for 'account_tx' than 'tx'
tx.tx.meta = tx.meta;
tx.tx.validated = tx.validated;
return parseTransaction(tx.tx);
}
function transactionFilter(address, filters, tx) {
if (filters.excludeFailures && tx.outcome.result !== 'tesSUCCESS') {
return false;
}
if (filters.types && !_.includes(filters.types, tx.type)) {
return false;
}
if (filters.initiated === true && tx.address !== address) {
return false;
}
if (filters.initiated === false && tx.address === address) {
return false;
}
if (filters.counterparty && tx.address !== filters.counterparty
&& tx.Destination !== filters.counterparty) {
return false;
}
return true;
}
function orderFilter(options, tx) {
return !options.startTx || (options.earliestFirst ?
utils.compareTransactions(tx, options.startTx) > 0 :
utils.compareTransactions(tx, options.startTx) < 0);
}
function getAccountTx(remote, address, options, marker, limit, callback) {
const params = {
account: address,
ledger_index_min: options.minLedgerVersion || -1,
ledger_index_max: options.maxLedgerVersion || -1,
forward: options.earliestFirst,
binary: options.binary,
limit: utils.clamp(limit, 10, 400),
marker: marker
};
remote.requestAccountTx(params, (error, data) => {
return error ? callback(error) : callback(null, {
marker: data.marker,
results: data.transactions
.filter((tx) => tx.validated)
.map(parseAccountTxTransaction)
.filter(_.partial(transactionFilter, address, options))
.filter(_.partial(orderFilter, options))
});
});
}
function checkForLedgerGaps(remote, options, transactions) {
let {minLedgerVersion, maxLedgerVersion} = options;
// if we reached the limit on number of transactions, then we can shrink
// the required ledger range to only guarantee that there are no gaps in
// the range of ledgers spanned by those transactions
if (options.limit && transactions.length === options.limit) {
if (options.earliestFirst) {
maxLedgerVersion = _.last(transactions).outcome.ledgerVersion;
} else {
minLedgerVersion = _.last(transactions).outcome.ledgerVersion;
}
}
if (!utils.hasCompleteLedgerRange(remote, minLedgerVersion,
maxLedgerVersion)) {
throw new utils.common.errors.MissingLedgerHistoryError();
}
}
function formatResponse(remote, options, transactions) {
const compare = options.earliestFirst ? utils.compareTransactions :
_.rearg(utils.compareTransactions, 1, 0);
const sortedTransactions = transactions.sort(compare);
checkForLedgerGaps(remote, options, sortedTransactions);
return sortedTransactions;
}
function getTransactionsInternal(remote, address, options, callback) {
const getter = _.partial(getAccountTx, remote, address, options);
const format = _.partial(formatResponse, remote, options);
utils.getRecursive(getter, options.limit, composeAsync(format, callback));
}
function getTransactions(address, options, callback) {
validate.address(address);
validate.getTransactionsOptions(options);
const defaults = {maxLedgerVersion: this.remote.getLedgerSequence()};
if (options.start) {
getTransaction.bind(this)(options.start, {}, (error, tx) => {
if (error) {
callback(error);
return;
}
const ledgerVersion = tx.outcome.ledgerVersion;
const bound = options.earliestFirst ?
{minLedgerVersion: ledgerVersion} : {maxLedgerVersion: ledgerVersion};
const newOptions = _.assign(defaults, options, {startTx: tx}, bound);
getTransactionsInternal(this.remote, address, newOptions, callback);
});
} else {
const newOptions = _.assign(defaults, options);
getTransactionsInternal(this.remote, address, newOptions, callback);
}
}
module.exports = utils.wrapCatch(getTransactions);

View File

@@ -0,0 +1,45 @@
/* @flow */
'use strict';
const _ = require('lodash');
const utils = require('./utils');
const validate = utils.common.validate;
const parseAccountTrustline = require('./parse/account-trustline');
function currencyFilter(currency, trustline) {
return currency === null || trustline.specification.currency === currency;
}
function getAccountLines(remote, address, ledgerVersion, options, marker, limit,
callback) {
const requestOptions = {
account: address,
ledger: ledgerVersion,
marker: marker,
limit: utils.clamp(limit, 10, 400),
peer: options.counterparty
};
remote.requestAccountLines(requestOptions, (error, data) => {
return error ? callback(error) :
callback(null, {
marker: data.marker,
results: data.lines.map(parseAccountTrustline)
.filter(_.partial(currencyFilter, options.currency || null))
});
});
}
function getTrustlines(account: string, options: {currency: string,
counterparty: string, limit: number, ledgerVersion: number},
callback: () => void): void {
validate.address(account);
validate.getTrustlinesOptions(options);
const ledgerVersion = options.ledgerVersion
|| this.remote.getLedgerSequence();
const getter = _.partial(getAccountLines, this.remote, account,
ledgerVersion, options);
utils.getRecursive(getter, options.limit, callback);
}
module.exports = utils.wrapCatch(getTrustlines);

98
src/api/ledger/utils.js Normal file
View File

@@ -0,0 +1,98 @@
'use strict';
const _ = require('lodash');
const assert = require('assert');
const common = require('../common');
const dropsToXrp = common.dropsToXrp;
const composeAsync = common.composeAsync;
function clamp(value, min, max) {
assert(min <= max, 'Illegal clamp bounds');
return Math.min(Math.max(value, min), max);
}
function getXRPBalance(remote, address, ledgerVersion, callback) {
remote.requestAccountInfo({account: address, ledger: ledgerVersion},
composeAsync((data) => dropsToXrp(data.account_data.Balance), callback));
}
// If the marker is omitted from a response, you have reached the end
// getter(marker, limit, callback), callback(error, {marker, results})
function getRecursiveRecur(getter, marker, limit, callback) {
getter(marker, limit, (error, data) => {
if (error) {
return callback(error);
}
const remaining = limit - data.results.length;
if (remaining > 0 && data.marker !== undefined) {
getRecursiveRecur(getter, data.marker, remaining, (_error, results) => {
return _error ? callback(_error) :
callback(null, data.results.concat(results));
});
} else {
return callback(null, data.results.slice(0, limit));
}
});
}
function getRecursive(getter, limit, callback) {
getRecursiveRecur(getter, undefined, limit || Infinity, callback);
}
function renameCounterpartyToIssuer(amount) {
if (amount === undefined) {
return undefined;
}
const issuer = amount.counterparty === undefined ?
amount.issuer : amount.counterparty;
const withIssuer = _.assign({}, amount, {issuer: issuer});
return _.omit(withIssuer, 'counterparty');
}
function renameCounterpartyToIssuerInOrder(order) {
const taker_gets = renameCounterpartyToIssuer(order.taker_gets);
const taker_pays = renameCounterpartyToIssuer(order.taker_pays);
const changes = {taker_gets: taker_gets, taker_pays: taker_pays};
return _.assign({}, order, _.omit(changes, _.isUndefined));
}
function signum(num) {
return (num === 0) ? 0 : (num > 0 ? 1 : -1);
}
/**
* Order two rippled transactions based on their ledger_index.
* If two transactions took place in the same ledger, sort
* them based on TransactionIndex
* See: https://ripple.com/build/transactions/
*
* @param {Object} first
* @param {Object} second
* @returns {Number} [-1, 0, 1]
*/
function compareTransactions(first, second) {
if (first.ledgerVersion === second.ledgerVersion) {
return signum(Number(first.indexInLedger) - Number(second.indexInLedger));
}
return Number(first.ledgerVersion) < Number(second.ledgerVersion) ? -1 : 1;
}
function hasCompleteLedgerRange(remote, minLedgerVersion, maxLedgerVersion) {
const firstLedgerVersion = 32570; // earlier versions have been lost
return remote.getServer().hasLedgerRange(
minLedgerVersion || firstLedgerVersion,
maxLedgerVersion || remote.getLedgerSequence());
}
module.exports = {
getXRPBalance,
compareTransactions,
renameCounterpartyToIssuer,
renameCounterpartyToIssuerInOrder,
getRecursive,
hasCompleteLedgerRange,
wrapCatch: common.wrapCatch,
clamp: clamp,
common: common
};

53
src/api/server/server.js Normal file
View File

@@ -0,0 +1,53 @@
/* @flow */
'use strict';
const common = require('../common');
// If a ledger is not received in this time, consider the connection offline
const CONNECTION_TIMEOUT = 1000 * 30;
function connect(callback: (err: any, data: any) => void): void {
this.remote.connect(callback);
}
function disconnect(callback: (err: any, data: any) => void): void {
this.remote.disconnect(callback);
}
function isUpToDate(remote): boolean {
const server = remote.getServer();
return Boolean(server) && (remote._stand_alone
|| (Date.now() - server._lastLedgerClose) <= CONNECTION_TIMEOUT);
}
function isConnected(): boolean {
return Boolean(this.remote._ledger_current_index) && isUpToDate(this.remote);
}
function getServerInfo(callback: (err: any, data: any) => void): void {
this.remote.requestServerInfo((error, response) => {
if (error) {
callback(new common.errors.RippledNetworkError(error.message));
} else {
callback(null, response.info);
}
});
}
function getFee(): number {
return common.dropsToXrp(this.remote.createTransaction()._computeFee());
}
function getLedgerVersion(): number {
return this.remote.getLedgerSequence();
}
module.exports = {
connect,
disconnect,
isConnected,
getServerInfo,
getFee,
getLedgerVersion
};

View File

@@ -0,0 +1,38 @@
/* @flow */
'use strict';
const utils = require('./utils');
const validate = utils.common.validate;
const Transaction = utils.common.core.Transaction;
const OfferCreateFlags = {
passive: {set: 'Passive'},
immediateOrCancel: {set: 'ImmediateOrCancel'},
fillOrKill: {set: 'FillOrKill'}
};
function createOrderTransaction(account, order) {
validate.address(account);
validate.order(order);
const transaction = new Transaction();
const takerPays = utils.common.toRippledAmount(order.direction === 'buy' ?
order.quantity : order.totalPrice);
const takerGets = utils.common.toRippledAmount(order.direction === 'buy' ?
order.totalPrice : order.quantity);
transaction.offerCreate(account, takerPays, takerGets);
utils.setTransactionBitFlags(transaction, order, OfferCreateFlags);
if (order.direction === 'sell') {
transaction.setFlags('Sell');
}
return transaction;
}
function prepareOrder(account, order, instructions, callback) {
const transaction = createOrderTransaction(account, order);
utils.createTxJSON(transaction, this.remote, instructions, callback);
}
module.exports = utils.wrapCatch(prepareOrder);

View File

@@ -0,0 +1,21 @@
/* @flow */
'use strict';
const utils = require('./utils');
const validate = utils.common.validate;
const Transaction = utils.common.core.Transaction;
function createOrderCancellationTransaction(account, sequence) {
validate.address(account);
validate.sequence(sequence);
const transaction = new Transaction();
transaction.offerCancel(account, sequence);
return transaction;
}
function prepareOrderCancellation(account, sequence, instructions, callback) {
const transaction = createOrderCancellationTransaction(account, sequence);
utils.createTxJSON(transaction, this.remote, instructions, callback);
}
module.exports = utils.wrapCatch(prepareOrderCancellation);

View File

@@ -0,0 +1,93 @@
/* @flow */
'use strict';
const _ = require('lodash');
const BigNumber = require('bignumber.js');
const utils = require('./utils');
const validate = utils.common.validate;
const toRippledAmount = utils.common.toRippledAmount;
const Transaction = utils.common.core.Transaction;
function isSendMaxAllowed(payment) {
const srcAmt = payment.source.amount;
const dstAmt = payment.destination.amount;
// Don't set SendMax for XRP->XRP payment
// temREDUNDANT_SEND_MAX removed in:
// https://github.com/ripple/rippled/commit/
// c522ffa6db2648f1d8a987843e7feabf1a0b7de8/
return srcAmt && !(srcAmt.currency === 'XRP' && dstAmt.currency === 'XRP');
}
function isIOUWithoutCounterparty(amount) {
return amount && amount.currency !== 'XRP'
&& amount.counterparty === undefined;
}
function applyAnyCounterpartyEncoding(payment) {
// Convert blank counterparty to sender or receiver's address
// (Ripple convention for 'any counterparty')
// https://ripple.com/build/transactions/
// #special-issuer-values-for-sendmax-and-amount
// https://ripple.com/build/ripple-rest/#counterparties-in-payments
if (isIOUWithoutCounterparty(payment.source.amount)) {
payment.source.amount.counterparty = payment.source.address;
}
if (isIOUWithoutCounterparty(payment.destination.amount)) {
payment.destination.amount.counterparty = payment.destination.address;
}
}
function createPaymentTransaction(account, payment) {
applyAnyCounterpartyEncoding(payment);
validate.address(account);
validate.payment(payment);
const transaction = new Transaction();
transaction.payment({
from: payment.source.address,
to: payment.destination.address,
amount: toRippledAmount(payment.destination.amount)
});
if (payment.invoiceID) {
transaction.invoiceID(payment.invoiceID);
}
if (payment.source.tag) {
transaction.sourceTag(payment.source.tag);
}
if (payment.destination.tag) {
transaction.destinationTag(payment.destination.tag);
}
if (payment.paths) {
transaction.paths(JSON.parse(payment.paths));
}
if (payment.memos) {
_.forEach(payment.memos, memo =>
transaction.addMemo(memo.type, memo.format, memo.data)
);
}
if (payment.allowPartialPayment) {
transaction.setFlags(['PartialPayment']);
}
if (payment.noDirectRipple) {
transaction.setFlags(['NoRippleDirect']);
}
if (payment.limitQuality) {
transaction.setFlags(['LimitQuality']);
}
if (isSendMaxAllowed(payment)) {
const maxValue = new BigNumber(payment.source.amount.value)
.plus(payment.source.slippage || 0).toString();
const maxAmount = _.assign({}, payment.source.amount, {value: maxValue});
transaction.sendMax(toRippledAmount(maxAmount));
}
return transaction;
}
function preparePayment(account, payment, instructions, callback) {
const transaction = createPaymentTransaction(account, payment);
utils.createTxJSON(transaction, this.remote, instructions, callback);
}
module.exports = utils.wrapCatch(preparePayment);

View File

@@ -0,0 +1,98 @@
/* @flow */
'use strict';
const _ = require('lodash');
const assert = require('assert');
const utils = require('./utils');
const validate = utils.common.validate;
const AccountFlagIndices = utils.common.constants.AccountFlagIndices;
const AccountFields = utils.common.constants.AccountFields;
const Transaction = utils.common.core.Transaction;
// Emptry string passed to setting will clear it
const CLEAR_SETTING = '';
function setTransactionFlags(transaction, values) {
const keys = Object.keys(values);
assert(keys.length === 1, 'ERROR: can only set one setting per transaction');
const flagName = keys[0];
const value = values[flagName];
const index = AccountFlagIndices[flagName];
if (index !== undefined) {
if (value) {
transaction.tx_json.SetFlag = index;
} else {
transaction.tx_json.ClearFlag = index;
}
}
}
function setTransactionFields(transaction, input) {
const fieldSchema = AccountFields;
for (const fieldName in fieldSchema) {
const field = fieldSchema[fieldName];
let value = input[field.name];
if (value === undefined) {
continue;
}
// The value required to clear an account root field varies
if (value === CLEAR_SETTING && field.hasOwnProperty('defaults')) {
value = field.defaults;
}
if (field.encoding === 'hex' && !field.length) {
// This is currently only used for Domain field
value = new Buffer(value, 'ascii').toString('hex').toUpperCase();
}
transaction.tx_json[fieldName] = value;
}
}
/**
* Note: A fee of 1% requires 101% of the destination to be sent for the
* destination to receive 100%.
* The transfer rate is specified as the input amount as fraction of 1.
* To specify the default rate of 0%, a 100% input amount, specify 1.
* To specify a rate of 1%, a 101% input amount, specify 1.01
*
* @param {Number|String} transferRate
*
* @returns {Number|String} numbers will be converted while strings
* are returned
*/
function convertTransferRate(transferRate) {
return _.isNumber(transferRate) ? transferRate * 1e9 : transferRate;
}
function createSettingsTransaction(account, settings) {
validate.address(account);
validate.settings(settings);
const transaction = new Transaction();
if (settings.regularKey) {
return transaction.setRegularKey({
account: account,
regular_key: settings.regularKey
});
}
transaction.accountSet(account);
setTransactionFlags(transaction, settings);
setTransactionFields(transaction, settings);
if (transaction.tx_json.TransferRate !== undefined) {
transaction.tx_json.TransferRate = convertTransferRate(
transaction.tx_json.TransferRate);
}
return transaction;
}
function prepareSettings(account, settings, instructions, callback) {
const transaction = createSettingsTransaction(account, settings);
utils.createTxJSON(transaction, this.remote, instructions, callback);
}
module.exports = utils.wrapCatch(prepareSettings);

View File

@@ -0,0 +1,68 @@
/* @flow */
'use strict';
const utils = require('./utils');
const core = utils.common.core;
const validate = utils.common.validate;
/**
* These prefixes are inserted before the source material used to
* generate various hashes. This is done to put each hash in its own
* "space." This way, two different types of objects with the
* same binary data will produce different hashes.
*
* Each prefix is a 4-byte value with the last byte set to zero
* and the first three bytes formed from the ASCII equivalent of
* some arbitrary string. For example "TXN".
*/
const HASH_TX_ID = 0x54584E00; // 'TXN'
const HASH_TX_SIGN = 0x53545800; // 'STX'
const HASH_TX_SIGN_TESTNET = 0x73747800; // 'stx'
function getKeyPair(address, secret) {
return core.Seed.from_json(secret).get_key(address);
}
function getPublicKeyHex(keypair) {
return keypair.to_hex_pub();
}
function serialize(txJSON) {
return core.SerializedObject.from_json(txJSON);
}
function hashSerialization(serialized, prefix) {
return serialized.hash(prefix || HASH_TX_ID).to_hex();
}
function hashJSON(txJSON, prefix) {
return hashSerialization(serialize(txJSON), prefix);
}
function signingHash(txJSON, isTestNet=false) {
return hashJSON(txJSON, isTestNet ? HASH_TX_SIGN_TESTNET : HASH_TX_SIGN);
}
function computeSignature(txJSON, keypair) {
const signature = keypair.sign(signingHash(txJSON));
return core.sjcl.codec.hex.fromBits(signature).toUpperCase();
}
function sign(txJSON: {Account: string; SigningPubKey: string,
TxnSignature: string}, secret: string):
{signedTransaction: string; id: string} {
validate.txJSON(txJSON);
validate.addressAndSecret({address: txJSON.Account, secret: secret});
const keypair = getKeyPair(txJSON.Account, secret);
if (txJSON.SigningPubKey === undefined) {
txJSON.SigningPubKey = getPublicKeyHex(keypair);
}
txJSON.TxnSignature = computeSignature(txJSON, keypair);
const serialized = serialize(txJSON);
return {
signedTransaction: serialized.to_hex(),
id: hashSerialization(serialized, HASH_TX_ID)
};
}
module.exports = sign;

View File

@@ -0,0 +1,15 @@
/* @flow */
'use strict';
const utils = require('./utils');
const validate = utils.common.validate;
const Request = utils.common.core.Request;
function submit(tx_blob: string,
callback: (err: any, data: any) => void): void {
validate.blob(tx_blob);
const request = new Request(this.remote, 'submit');
request.message.tx_blob = tx_blob;
request.request(null, callback);
}
module.exports = submit;

View File

@@ -0,0 +1,35 @@
/* @flow */
'use strict';
const utils = require('./utils');
const validate = utils.common.validate;
const Transaction = utils.common.core.Transaction;
const TrustSetFlags = {
authorized: {set: 'SetAuth'},
allowRippling: {set: 'ClearNoRipple', unset: 'NoRipple'},
frozen: {set: 'SetFreeze', unset: 'ClearFreeze'}
};
function createTrustlineTransaction(account, trustline) {
validate.address(account);
validate.trustline(trustline);
const limit = {
currency: trustline.currency,
issuer: trustline.counterparty,
value: trustline.limit
};
const transaction = new Transaction();
transaction.trustSet(account, limit,
trustline.qualityIn, trustline.qualityOut);
utils.setTransactionBitFlags(transaction, trustline, TrustSetFlags);
return transaction;
}
function prepareTrustline(account, trustline, instructions, callback) {
const transaction = createTrustlineTransaction(account, trustline);
utils.createTxJSON(transaction, this.remote, instructions, callback);
}
module.exports = utils.wrapCatch(prepareTrustline);

View File

@@ -0,0 +1,70 @@
/* @flow */
'use strict';
const BigNumber = require('bignumber.js');
const common = require('../common');
function setTransactionBitFlags(transaction: any, values: any, flags: any):
void {
for (const flagName in flags) {
const flagValue = values[flagName];
const flagConversions = flags[flagName];
if (flagValue === true && flagConversions.set !== undefined) {
transaction.setFlags(flagConversions.set);
}
if (flagValue === false && flagConversions.unset !== undefined) {
transaction.setFlags(flagConversions.unset);
}
}
}
function getFeeDrops(remote) {
const feeUnits = 10; // all transactions currently have a fee of 10 fee units
return remote.feeTx(feeUnits).to_text();
}
function createTxJSON(transaction: any, remote: any, instructions: any,
callback: (err: ?(typeof Error), data: {tx_json: any}) => void): void {
common.validate.instructions(instructions);
transaction.complete();
const account = transaction.getAccount();
const txJSON = transaction.tx_json;
if (instructions.maxLedgerVersion !== undefined) {
txJSON.LastLedgerSequence = parseInt(instructions.maxLedgerVersion, 10);
} else {
const offset = instructions.maxLedgerVersionOffset !== undefined ?
parseInt(instructions.maxLedgerVersionOffset, 10) : 3;
txJSON.LastLedgerSequence = remote.getLedgerSequence() + offset;
}
if (instructions.fee !== undefined) {
txJSON.Fee = common.xrpToDrops(instructions.fee);
} else {
const serverFeeDrops = getFeeDrops(remote);
if (instructions.maxFee !== undefined) {
const maxFeeDrops = common.xrpToDrops(instructions.maxFee);
txJSON.Fee = BigNumber.min(serverFeeDrops, maxFeeDrops).toString();
} else {
txJSON.Fee = serverFeeDrops;
}
}
if (instructions.sequence !== undefined) {
txJSON.Sequence = parseInt(instructions.sequence, 10);
callback(null, txJSON);
} else {
remote.findAccount(account).getNextSequence(function(error, sequence) {
txJSON.Sequence = sequence;
callback(null, txJSON);
});
}
}
module.exports = {
setTransactionBitFlags: setTransactionBitFlags,
createTxJSON: createTxJSON,
wrapCatch: common.wrapCatch,
common: common
};

View File

@@ -1,3 +1,4 @@
'use strict';
// Routines for working with an account.
//
// You should not instantiate this class yourself, instead use Remote#account.
@@ -9,16 +10,15 @@
// balance_proposed
//
// var network = require('./network.js');
var async = require('async');
var util = require('util');
var extend = require('extend');
var EventEmitter = require('events').EventEmitter;
var Amount = require('./amount').Amount;
var UInt160 = require('./uint160').UInt160;
var TransactionManager = require('./transactionmanager').TransactionManager;
var sjcl = require('./utils').sjcl;
var Base = require('./base').Base;
const _ = require('lodash');
const async = require('async');
const util = require('util');
const extend = require('extend');
const EventEmitter = require('events').EventEmitter;
const UInt160 = require('./uint160').UInt160;
const TransactionManager = require('./transactionmanager').TransactionManager;
const sjcl = require('./utils').sjcl;
const Base = require('./base').Base;
/**
* @constructor Account
@@ -29,7 +29,7 @@ var Base = require('./base').Base;
function Account(remote, account) {
EventEmitter.call(this);
var self = this;
const self = this;
this._remote = remote;
this._account = UInt160.from_json(account);
@@ -40,29 +40,29 @@ function Account(remote, account) {
// Important: This must never be overwritten, only extend()-ed
this._entry = { };
function listenerAdded(type, listener) {
function listenerAdded(type) {
if (~Account.subscribeEvents.indexOf(type)) {
if (!self._subs && self._remote._connected) {
self._remote.request_subscribe()
.add_account(self._account_id)
.broadcast();
self._remote.requestSubscribe()
.addAccount(self._account_id)
.broadcast().request();
}
self._subs += 1;
}
};
}
this.on('newListener', listenerAdded);
function listenerRemoved(type, listener) {
function listenerRemoved(type) {
if (~Account.subscribeEvents.indexOf(type)) {
self._subs -= 1;
if (!self._subs && self._remote._connected) {
self._remote.request_unsubscribe()
.add_account(self._account_id)
.broadcast();
self._remote.requestUnsubscribe()
.addAccount(self._account_id)
.broadcast().request();
}
}
};
}
this.on('removeListener', listenerRemoved);
@@ -70,7 +70,7 @@ function Account(remote, account) {
if (self._account.is_valid() && self._subs) {
request.add_account(self._account_id);
}
};
}
this._remote.on('prepare_subscribe', attachAccount);
@@ -79,11 +79,11 @@ function Account(remote, account) {
return;
}
var changed = false;
let changed = false;
transaction.mmeta.each(function(an) {
var isAccount = an.fields.Account === self._account_id;
var isAccountRoot = isAccount && (an.entryType === 'AccountRoot');
const isAccount = an.fields.Account === self._account_id;
const isAccountRoot = isAccount && (an.entryType === 'AccountRoot');
if (isAccountRoot) {
extend(self._entry, an.fieldsNew, an.fieldsFinal);
@@ -94,14 +94,14 @@ function Account(remote, account) {
if (changed) {
self.emit('entry', self._entry);
}
};
}
this.on('transaction', handleTransaction);
this._transactionManager = new TransactionManager(this);
return this;
};
}
util.inherits(Account, EventEmitter);
@@ -109,7 +109,7 @@ util.inherits(Account, EventEmitter);
* List of events that require a remote subscription to the account.
*/
Account.subscribeEvents = [ 'transaction', 'entry' ];
Account.subscribeEvents = ['transaction', 'entry'];
Account.prototype.toJson = function() {
return this._account.to_json();
@@ -144,9 +144,9 @@ Account.prototype.getInfo = function(callback) {
* @param {Function} callback
*/
Account.prototype.entry = function(callback) {
var self = this;
var callback = typeof callback === 'function' ? callback : function(){};
Account.prototype.entry = function(callback_) {
const self = this;
const callback = typeof callback_ === 'function' ? callback_ : _.noop;
function accountInfo(err, info) {
if (err) {
@@ -156,21 +156,21 @@ Account.prototype.entry = function(callback) {
self.emit('entry', self._entry);
callback(null, info);
}
};
}
this.getInfo(accountInfo);
return this;
};
Account.prototype.getNextSequence = function(callback) {
var callback = typeof callback === 'function' ? callback : function(){};
Account.prototype.getNextSequence = function(callback_) {
const callback = typeof callback_ === 'function' ? callback_ : _.noop;
function isNotFound(err) {
return err && typeof err === 'object'
&& typeof err.remote === 'object'
&& err.remote.error === 'actNotFound';
};
}
function accountInfo(err, info) {
if (isNotFound(err)) {
@@ -181,7 +181,7 @@ Account.prototype.getNextSequence = function(callback) {
} else {
callback(null, info.account_data.Sequence);
}
};
}
this.getInfo(accountInfo);
@@ -197,9 +197,9 @@ Account.prototype.getNextSequence = function(callback) {
* @param {function(err, lines)} callback Called with the result
*/
Account.prototype.lines = function(callback) {
var self = this;
var callback = typeof callback === 'function' ? callback : function(){};
Account.prototype.lines = function(callback_) {
const self = this;
const callback = typeof callback_ === 'function' ? callback_ : _.noop;
function accountLines(err, res) {
if (err) {
@@ -225,23 +225,22 @@ Account.prototype.lines = function(callback) {
* @returns {Account}
*/
Account.prototype.line = function(currency, address, callback) {
var self = this;
var callback = typeof callback === 'function' ? callback : function(){};
Account.prototype.line = function(currency, address, callback_) {
const self = this;
const callback = typeof callback_ === 'function' ? callback_ : _.noop;
self.lines(function(err, data) {
if (err) {
return callback(err);
}
var line;
let line;
top:
for (var i=0; i<data.lines.length; i++) {
var l = data.lines[i];
for (let i = 0; i < data.lines.length; i++) {
const l = data.lines[i];
if (l.account === address && l.currency === currency) {
line = l;
break top;
break;
}
}
@@ -271,15 +270,16 @@ Account.prototype.notifyTx = function(transaction) {
this.emit('transaction', transaction);
var account = transaction.transaction.Account;
const account = transaction.transaction.Account;
if (!account) {
return;
}
var isThisAccount = (account === this._account_id);
const isThisAccount = (account === this._account_id);
this.emit(isThisAccount ? 'transaction-outbound' : 'transaction-inbound', transaction);
this.emit(isThisAccount ? 'transaction-outbound' : 'transaction-inbound',
transaction);
};
/**
@@ -297,16 +297,17 @@ Account.prototype.submit = function(transaction) {
/**
* Check whether the given public key is valid for this account
*
* @param {Hex-encoded String|RippleAddress} public_key
* @param {Function} callback
* @param {Hex-encoded_String|RippleAddress} public_key Public key
* @param {Function} callback Is a callback
* @returns {void}
*
* @callback
* @param {Error} err
* @param {Boolean} true if the public key is valid and active, false otherwise
* param {Error} err
* param {Boolean} true if the public key is valid and active, false otherwise
*/
Account.prototype.publicKeyIsActive = function(public_key, callback) {
var self = this;
var public_key_as_uint160;
const self = this;
let public_key_as_uint160;
try {
public_key_as_uint160 = Account._publicKeyToAddress(public_key);
@@ -315,7 +316,7 @@ Account.prototype.publicKeyIsActive = function(public_key, callback) {
}
function getAccountInfo(async_callback) {
self.getInfo(function(err, account_info_res){
self.getInfo(function(err, account_info_res) {
// If the remote responds with an Account Not Found error then the account
// is unfunded and thus we can assume that the master key is active
@@ -325,7 +326,7 @@ Account.prototype.publicKeyIsActive = function(public_key, callback) {
async_callback(err, account_info_res);
}
});
};
}
function publicKeyIsValid(account_info_res, async_callback) {
// Catch the case of unfunded accounts
@@ -340,10 +341,11 @@ Account.prototype.publicKeyIsActive = function(public_key, callback) {
return;
}
var account_info = account_info_res.account_data;
const account_info = account_info_res.account_data;
// Respond with true if the RegularKey is set and matches the given public key or
// if the public key matches the account address and the lsfDisableMaster is not set
// Respond with true if the RegularKey is set and matches the given
// public key or if the public key matches the account address and
// the lsfDisableMaster is not set
if (account_info.RegularKey &&
account_info.RegularKey === public_key_as_uint160) {
async_callback(null, true);
@@ -353,9 +355,9 @@ Account.prototype.publicKeyIsActive = function(public_key, callback) {
} else {
async_callback(null, false);
}
};
}
var steps = [
const steps = [
getAccountInfo,
publicKeyIsValid
];
@@ -368,25 +370,25 @@ Account.prototype.publicKeyIsActive = function(public_key, callback) {
*
* @static
*
* @param {Hex-encoded string|RippleAddress} public_key
* @returns {RippleAddress}
* @param {Hex-encoded_string|RippleAddress} public_key Public key
* @returns {RippleAddress} Ripple Address
*/
Account._publicKeyToAddress = function(public_key) {
// Based on functions in /src/js/ripple/keypair.js
function hexToUInt160(public_key) {
var bits = sjcl.codec.hex.toBits(public_key);
var hash = sjcl.hash.ripemd160.hash(sjcl.hash.sha256.hash(bits));
var address = UInt160.from_bits(hash);
function hexToUInt160(publicKey) {
const bits = sjcl.codec.hex.toBits(publicKey);
const hash = sjcl.hash.ripemd160.hash(sjcl.hash.sha256.hash(bits));
const address = UInt160.from_bits(hash);
address.set_version(Base.VER_ACCOUNT_ID);
return address.to_json();
};
}
if (UInt160.is_valid(public_key)) {
return public_key;
} else if (/^[0-9a-fA-F]+$/.test(public_key)) {
return hexToUInt160(public_key);
} else {
} else { // eslint-disable-line no-else-return
throw new Error('Public key is invalid. Must be a UInt160 or a hex string');
}
};

990
src/core/amount.js Normal file
View File

@@ -0,0 +1,990 @@
'use strict';
// Represent Ripple amounts and currencies.
// - Numbers in hex are big-endian.
const assert = require('assert');
const extend = require('extend');
const utils = require('./utils');
const UInt160 = require('./uint160').UInt160;
const Seed = require('./seed').Seed;
const Currency = require('./currency').Currency;
const Value = require('./value').Value;
const IOUValue = require('./iouvalue').IOUValue;
const XRPValue = require('./xrpvalue').XRPValue;
function Amount(value = new XRPValue(NaN)) {
// Json format:
// integer : XRP
// { 'value' : ..., 'currency' : ..., 'issuer' : ...}
assert(value instanceof Value);
this._value = value;
this._is_native = true; // Default to XRP. Only valid if value is not NaN.
this._currency = new Currency();
this._issuer = new UInt160();
}
/**
* Set strict_mode = false to disable amount range checking
*/
Amount.strict_mode = true;
const consts = {
currency_xns: 0,
currency_one: 1,
xns_precision: 6,
// bi_ prefix refers to "big integer"
// man refers to mantissa
bi_man_max_value: '9999999999999999',
bi_man_min_value: Number(1e15).toString(),
bi_xns_max: Number(1e17).toString(),
bi_xns_min: Number(-1e17).toString(),
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',
// Minimum possible amount for non-XRP currencies.
min_value: '-1000000000000000e-96'
};
const MAX_XRP_VALUE = new XRPValue(1e11);
const MAX_IOU_VALUE = new IOUValue(consts.max_value);
const MIN_IOU_VALUE = new IOUValue(consts.min_value).abs();
const bi_xns_unit = new IOUValue(1e6);
// Add constants to Amount class
extend(Amount, consts);
// DEPRECATED: Use Amount instead, e.g. Amount.currency_xns
exports.consts = consts;
// Given '100/USD/ISSUER' return the a string with ISSUER remapped.
Amount.text_full_rewrite = function(j) {
return Amount.from_json(j).to_text_full();
};
// Given '100/USD/ISSUER' return the json.
Amount.json_rewrite = function(j) {
return Amount.from_json(j).to_json();
};
Amount.from_number = function(n) {
return (new Amount()).parse_number(n);
};
Amount.from_json = function(j) {
return (new Amount()).parse_json(j);
};
Amount.from_quality = function(quality, currency, issuer, opts) {
return (new Amount()).parse_quality(quality, currency, issuer, opts);
};
Amount.from_human = function(j, opts) {
return (new Amount()).parse_human(j, opts);
};
Amount.is_valid = function(j) {
return Amount.from_json(j).is_valid();
};
Amount.is_valid_full = function(j) {
return Amount.from_json(j).is_valid_full();
};
Amount.NaN = function() {
const result = new Amount();
result._value = new IOUValue(NaN); // should have no effect
return result; // but let's be careful
};
// be sure that _is_native is set properly BEFORE calling _set_value
Amount.prototype._set_value = function(value: Value) {
this._value = value.isZero() && value.isNegative() ?
value.negate() : value;
this._check_limits();
};
// Returns a new value which is the absolute value of this.
Amount.prototype.abs = function() {
return this._copy(this._value.abs());
};
Amount.prototype.add = function(addend) {
const addendAmount = Amount.from_json(addend);
if (!this.is_comparable(addendAmount)) {
return new Amount();
}
return this._copy(this._value.add(addendAmount._value));
};
Amount.prototype.subtract = function(subtrahend) {
// Correctness over speed, less code has less bugs, reuse add code.
return this.add(Amount.from_json(subtrahend).negate());
};
// XXX Diverges from cpp.
Amount.prototype.multiply = function(multiplicand) {
const multiplicandAmount = Amount.from_json(multiplicand);
return this._copy(this._value.multiply(multiplicandAmount._value));
};
Amount.prototype.scale = function(scaleFactor) {
return this.multiply(scaleFactor);
};
Amount.prototype.divide = function(divisor) {
const divisorAmount = Amount.from_json(divisor);
return this._copy(this._value.divide(divisorAmount._value));
};
/**
* This function calculates a ratio - such as a price - between two Amount
* objects.
*
* The return value will have the same type (currency) as the numerator. This is
* a simplification, which should be sane in most cases. For example, a USD/XRP
* price would be rendered as USD.
*
* @example
* const price = buy_amount.ratio_human(sell_amount);
*
* @this {Amount} The numerator (top half) of the fraction.
* @param {Amount} denominator The denominator (bottom half) of the fraction.
* @param opts Options for the calculation.
* @param opts.reference_date {Date|Number} Date based on which
* demurrage/interest should be applied. Can be given as JavaScript Date or int
* for Ripple epoch.
* @return {Amount} The resulting ratio. Unit will be the same as numerator.
*/
Amount.prototype.ratio_human = function(denom, opts) {
const options = extend({ }, opts);
const numerator = this.clone();
let denominator = Amount.from_json(denom);
// If either operand is NaN, the result is NaN.
if (!numerator.is_valid() || !denominator.is_valid()) {
return new Amount(NaN);
}
if (denominator.is_zero()) {
return new Amount(NaN);
}
// Apply interest/demurrage
//
// We only need to apply it to the second factor, because the currency unit of
// the first factor will carry over into the result.
if (options.reference_date) {
denominator = denominator.applyInterest(options.reference_date);
}
// Special case: The denominator is a native (XRP) amount.
//
// In that case, it's going to be expressed as base units (1 XRP =
// 10^xns_precision base units).
//
// However, the unit of the denominator is lost, so when the resulting ratio
// is printed, the ratio is going to be too small by a factor of
// 10^xns_precision.
//
// To compensate, we multiply the numerator by 10^xns_precision.
if (denominator._is_native) {
numerator._set_value(numerator.multiply(bi_xns_unit));
}
return numerator.divide(denominator);
};
/**
* Calculate a product of two amounts.
*
* This function allows you to calculate a product between two amounts which
* retains XRPs human/external interpretation (i.e. 1 XRP = 1,000,000 base
* units).
*
* Intended use is to calculate something like: 10 USD * 10 XRP/USD = 100 XRP
*
* @example
* let sell_amount = buy_amount.product_human(price);
*
* @see Amount#ratio_human
*
* @param {Amount} factor The second factor of the product.
* @param {Object} opts Options for the calculation.
* @param {Date|Number} opts.reference_date Date based on which
* demurrage/interest should be applied. Can be given as JavaScript Date or int
* for Ripple epoch.
* @return {Amount} The product. Unit will be the same as the first factor.
*/
Amount.prototype.product_human = function(factor, options = {}) {
let fac = Amount.from_json(factor);
// If either operand is NaN, the result is NaN.
if (!this.is_valid() || !fac.is_valid()) {
return new Amount();
}
// Apply interest/demurrage
//
// We only need to apply it to the second factor, because the currency unit of
// the first factor will carry over into the result.
if (options.reference_date) {
fac = fac.applyInterest(options.reference_date);
}
const product = this.multiply(fac);
// Special case: The second factor is a native (XRP) amount expressed as base
// units (1 XRP = 10^xns_precision base units).
//
// See also Amount#ratio_human.
if (fac._is_native) {
const quotient = product.divide(bi_xns_unit.toString());
product._set_value(quotient._value);
}
return product;
};
/**
* Turn this amount into its inverse.
*
* @return {Amount} self
* @private
*/
Amount.prototype._invert = function() {
this._set_value(this._value.invert());
return this;
};
/**
* Return the inverse of this amount.
*
* @return {Amount} New Amount object with same currency and issuer, but the
* inverse of the value.
*/
Amount.prototype.invert = function() {
return this.clone()._invert();
};
/**
* Canonicalize amount value is now taken care of in the Value classes
*
* Mirrors rippled's internal Amount representation
* From https://github.com/ripple/rippled/blob/develop/src/ripple/data
* /protocol/STAmount.h#L31-L40
*
* Internal form:
* 1: If amount is zero, then value is zero and offset is -100
* 2: Otherwise:
* legal offset range is -96 to +80 inclusive
* value range is 10^15 to (10^16 - 1) inclusive
* amount = value * [10 ^ offset]
*
* -------------------
*
* The amount can be epxresses as A x 10^B
* Where:
* - A must be an integer between 10^15 and (10^16)-1 inclusive
* - B must be between -96 and 80 inclusive
*
* This results
* - minumum: 10^15 x 10^-96 -> 10^-81 -> -1e-81
* - maximum: (10^16)-1 x 10^80 -> 9999999999999999e80
*
* @returns {Amount}
* @throws {Error} if offset exceeds legal ranges, meaning the amount value is
* bigger than supported
*/
Amount.prototype._check_limits = function() {
if (!Amount.strict_mode) {
return this;
}
if (this._value.isNaN() || this._value.isZero()) {
return this;
}
const absval = this._value.abs();
if (this._is_native) {
if (absval.greaterThan(MAX_XRP_VALUE)) {
throw new Error('Exceeding max value of ' + MAX_XRP_VALUE.toString());
}
} else {
if (absval.lessThan(MIN_IOU_VALUE)) {
throw new Error('Exceeding min value of ' + MIN_IOU_VALUE.toString());
}
if (absval.greaterThan(MAX_IOU_VALUE)) {
throw new Error('Exceeding max value of ' + MAX_IOU_VALUE.toString());
}
}
return this;
};
Amount.prototype.clone = function(negate) {
return this.copyTo(new Amount(), negate);
};
Amount.prototype._copy = function(value) {
const copy = this.clone();
copy._set_value(value);
return copy;
};
Amount.prototype.compareTo = function(to) {
const toAmount = Amount.from_json(to);
if (!this.is_comparable(toAmount)) {
return new Amount();
}
return this._value.comparedTo(toAmount._value);
};
// Make d a copy of this. Returns d.
// Modification of objects internally refered to is not allowed.
Amount.prototype.copyTo = function(d, negate) {
d._value = negate ? this._value.negate() : this._value;
d._is_native = this._is_native;
d._currency = this._currency;
d._issuer = this._issuer;
return d;
};
Amount.prototype.currency = function() {
return this._currency;
};
Amount.prototype.equals = function(d, ignore_issuer) {
if (!(d instanceof Amount)) {
return this.equals(Amount.from_json(d));
}
return this.is_valid() && d.is_valid()
&& this._is_native === d._is_native
&& this._value.equals(d._value)
&& (this._is_native || (this._currency.equals(d._currency)
&& (ignore_issuer || this._issuer.equals(d._issuer))));
};
// True if Amounts are valid and both native or non-native.
Amount.prototype.is_comparable = function(v) {
return this.is_valid() && v.is_valid() && this._is_native === v._is_native;
};
Amount.prototype.is_native = function() {
return this._is_native;
};
Amount.prototype.is_negative = function() {
return this._value.isNegative();
};
Amount.prototype.is_positive = function() {
return !this.is_zero() && !this.is_negative();
};
// Only checks the value. Not the currency and issuer.
Amount.prototype.is_valid = function() {
return !this._value.isNaN();
};
Amount.prototype.is_valid_full = function() {
return this.is_valid()
&& this._currency.is_valid()
&& this._issuer.is_valid();
};
Amount.prototype.is_zero = function() {
return this._value.isZero();
};
Amount.prototype.issuer = function() {
return this._issuer;
};
// Return a new value.
Amount.prototype.negate = function() {
return this.clone('NEGATE');
};
/**
* Tries to correctly interpret an amount as entered by a user.
*
* Examples:
*
* XRP 250 => 250000000/XRP
* 25.2 XRP => 25200000/XRP
* USD 100.40 => 100.4/USD/?
* 100 => 100000000/XRP
*
*
* The regular expression below matches above cases, broken down for better
* understanding:
*
* // either 3 letter alphabetic currency-code or 3 digit numeric currency-code.
* // See ISO 4217
* ([A-z]{3}|[0-9]{3})
*
* // end of string
* $
*/
Amount.prototype.parse_human = function(j, options) {
const opts = options || {};
const hex_RE = /^[a-fA-F0-9]{40}$/;
const currency_RE = /^([a-zA-Z]{3}|[0-9]{3})$/;
let value;
let currency;
const words = j.split(' ').filter(function(word) {
return word !== '';
});
function isNumber(s) {
return isFinite(s) && s !== '' && s !== null;
}
if (words.length === 1) {
if (isNumber(words[0])) {
value = words[0];
currency = 'XRP';
} else {
value = words[0].slice(0, -3);
currency = words[0].slice(-3);
if (!(isNumber(value) && currency.match(currency_RE))) {
return new Amount();
}
}
} else if (words.length === 2) {
if (isNumber(words[0]) && words[1].match(hex_RE)) {
value = words[0];
currency = words[1];
} else if (words[0].match(currency_RE) && isNumber(words[1])) {
value = words[1];
currency = words[0];
} else if (isNumber(words[0]) && words[1].match(currency_RE)) {
value = words[0];
currency = words[1];
} else {
return new Amount();
}
} else {
return new Amount();
}
currency = currency.toUpperCase();
this.set_currency(currency);
this._is_native = (currency === 'XRP');
const newValue =
(this._is_native ? new XRPValue(value) :
new IOUValue(value));
this._set_value(newValue);
// Apply interest/demurrage
if (opts.reference_date && this._currency.has_interest()) {
const interest = this._currency.get_interest_at(opts.reference_date);
this._set_value(
this._value.divide(new IOUValue(interest.toString())));
}
return this;
};
Amount.prototype.parse_issuer = function(issuer) {
this._issuer = UInt160.from_json(issuer);
return this;
};
/**
* Decode a price from a BookDirectory index.
*
* BookDirectory ledger entries each encode the offer price in their index. This
* method can decode that information and populate an Amount object with it.
*
* It is possible not to provide a currency or issuer, but be aware that Amount
* objects behave differently based on the currency, so you may get incorrect
* results.
*
* Prices involving demurraging currencies are tricky, since they depend on the
* base and counter currencies.
*
* @param {String} quality 8 hex bytes quality or 32 hex bytes BookDirectory
* index.
* @param {Currency|String} counterCurrency currency of the resulting Amount
* object.
* @param {Issuer|String} counterIssuer Issuer of the resulting Amount object.
* @param {Object} opts Additional options
* @param {Boolean} opts.inverse If true, return the inverse of the price
* encoded in the quality.
* @param {Currency|String} opts.base_currency The other currency. This plays a
* role with interest-bearing or demurrage currencies. In that case the
* demurrage has to be applied when the quality is decoded, otherwise the
* price will be false.
* @param {Date|Number} opts.reference_date Date based on which
* demurrage/interest should be applied. Can be given as JavaScript Date or int
* for Ripple epoch.
* @param {Boolean} opts.xrp_as_drops Whether XRP amount should be treated as
* drops. When the base currency is XRP, the quality is calculated in drops.
* For human use however, we want to think of 1000000 drops as 1 XRP and
* prices as per-XRP instead of per-drop.
* @return {Amount} self
*/
Amount.prototype.parse_quality =
function(quality, counterCurrency, counterIssuer, opts) {
const options = opts || {};
const baseCurrency = Currency.from_json(options.base_currency);
const mantissa_hex = quality.substring(quality.length - 14);
const offset_hex = quality.substring(
quality.length - 16, quality.length - 14);
const mantissa = new IOUValue(mantissa_hex, null, 16);
const offset = parseInt(offset_hex, 16) - 100;
this._currency = Currency.from_json(counterCurrency);
this._issuer = UInt160.from_json(counterIssuer);
this._is_native = this._currency.is_native();
if (this._is_native && baseCurrency.is_native()) {
throw new Error('XRP/XRP quality is not allowed');
}
/*
The quality, as stored in the last 64 bits of a directory index, is stored as
the quotient of TakerPays/TakerGets.
When `opts.inverse` is true we are looking at a quality used for determining a
`bid` price and it must first be inverted, before our declared base/counter
currencies are in line with the price.
For example:
quality as stored : 5 USD / 3000000 drops
inverted : 3000000 drops / 5 USD
*/
const valueStr = mantissa.toString() + 'e' + offset.toString();
let nativeAdjusted = new IOUValue(valueStr);
nativeAdjusted = options.inverse ? nativeAdjusted.invert() : nativeAdjusted;
if (!options.xrp_as_drops) {
// `In a currency exchange, the exchange rate is quoted as the units of the
// counter currency in terms of a single unit of a base currency`. A
// quality is how much taker must `pay` to get ONE `gets` unit thus:
// pays ~= counterCurrency
// gets ~= baseCurrency.
if (this._is_native) {
// pay:$price drops get:1 X
// pay:($price / 1,000,000) XRP get:1 X
nativeAdjusted = nativeAdjusted.divide(bi_xns_unit);
} else if (baseCurrency.is_valid() && baseCurrency.is_native()) {
// pay:$price X get:1 drop
// pay:($price * 1,000,000) X get:1 XRP
nativeAdjusted = nativeAdjusted.multiply(bi_xns_unit);
}
}
if (this._is_native) {
this._set_value(
new XRPValue(nativeAdjusted.round(6, Value.getBNRoundDown()).toString()));
} else {
this._set_value(nativeAdjusted);
}
if (options.reference_date && baseCurrency.is_valid()
&& baseCurrency.has_interest()) {
const interest = baseCurrency.get_interest_at(options.reference_date);
this._set_value(
this._value.divide(new IOUValue(interest.toString())));
}
return this;
};
Amount.prototype.parse_number = function(n) {
this._is_native = false;
this._currency = Currency.from_json(1);
this._issuer = UInt160.from_json(1);
this._set_value(new IOUValue(n));
return this;
};
// <-> j
Amount.prototype.parse_json = function(j) {
switch (typeof j) {
case 'string':
// .../.../... notation is not a wire format. But allowed for easier
// testing.
const m = j.match(/^([^/]+)\/([^/]+)(?:\/(.+))?$/);
if (m) {
this._currency = Currency.from_json(m[2]);
if (m[3]) {
this._issuer = UInt160.from_json(m[3]);
} else {
this._issuer = UInt160.from_json('1');
}
this.parse_value(m[1]);
} else {
this.parse_native(j);
this._currency = Currency.from_json('0');
this._issuer = UInt160.from_json('0');
}
break;
case 'number':
this.parse_json(String(j));
break;
case 'object':
if (j === null) {
break;
}
if (j instanceof Amount) {
j.copyTo(this);
} else if (j.hasOwnProperty('value')) {
// Parse the passed value to sanitize and copy it.
this._currency.parse_json(j.currency, true); // Never XRP.
if (typeof j.issuer === 'string') {
this._issuer.parse_json(j.issuer);
}
this.parse_value(j.value);
}
break;
default:
this._set_value(new IOUValue(NaN));
}
return this;
};
// Parse a XRP value from untrusted input.
// - integer = raw units
// - float = with precision 6
// XXX Improvements: disallow leading zeros.
Amount.prototype.parse_native = function(j) {
if (j && typeof j === 'string' && !isNaN(j)) {
if (j.indexOf('.') >= 0) {
throw new Error('Native amounts must be specified in integer drops');
}
const value = new XRPValue(j);
this._is_native = true;
this._set_value(value.divide(bi_xns_unit));
} else {
this._set_value(new IOUValue(NaN));
}
return this;
};
// Parse a non-native value for the json wire format.
// Requires _currency to be set!
Amount.prototype.parse_value = function(j) {
this._is_native = false;
const newValue = new IOUValue(j, Value.getBNRoundDown());
this._set_value(newValue);
return this;
};
Amount.prototype.set_currency = function(c) {
this._currency = Currency.from_json(c);
this._is_native = this._currency.is_native();
return this;
};
Amount.prototype.set_issuer = function(issuer) {
if (issuer instanceof UInt160) {
this._issuer = issuer;
} else {
this._issuer = UInt160.from_json(issuer);
}
return this;
};
Amount.prototype.to_number = function() {
return Number(this.to_text());
};
// Convert only value to JSON wire format.
Amount.prototype.to_text = function() {
if (!this.is_valid()) {
return 'NaN';
}
if (this._is_native) {
return this._value.multiply(bi_xns_unit).toString();
}
// not native
const offset = this._value.getExponent() - 15;
const sign = this._value.isNegative() ? '-' : '';
const mantissa = utils.getMantissa16FromString(
this._value.abs().toString());
if (offset !== 0 && (offset < -25 || offset > -4)) {
// Use e notation.
// XXX Clamp output.
return sign + mantissa.toString() + 'e' + offset.toString();
}
const val = '000000000000000000000000000'
+ mantissa.toString()
+ '00000000000000000000000';
const pre = val.substring(0, offset + 43);
const post = val.substring(offset + 43);
const s_pre = pre.match(/[1-9].*$/); // Everything but leading zeros.
const s_post = post.match(/[1-9]0*$/); // Last non-zero plus trailing zeros.
return sign + (s_pre ? s_pre[0] : '0')
+ (s_post ? '.' + post.substring(0, 1 + post.length - s_post[0].length) : '');
};
/**
* Calculate present value based on currency and a reference date.
*
* This only affects demurraging and interest-bearing currencies.
*
* User should not store amount objects after the interest is applied. This is
* intended by display functions such as toHuman().
*
* @param {Date|Number} referenceDate Date based on which demurrage/interest
* should be applied. Can be given as JavaScript Date or int for Ripple epoch.
* @return {Amount} The amount with interest applied.
*/
Amount.prototype.applyInterest = function(referenceDate) {
if (!this._currency.has_interest()) {
return this;
}
const interest = this._currency.get_interest_at(referenceDate);
return this._copy(
this._value.multiply(new IOUValue(interest.toString())));
};
/**
* Format only value in a human-readable format.
*
* @example
* let pretty = amount.to_human({precision: 2});
*
* @param {Object} options Options for formatter.
* @param {Number} options.precision Max. number of digits after decimal point.
* @param {Number} options.min_precision Min. number of digits after dec. point.
* @param {Boolean} options.skip_empty_fraction Don't show fraction if it
* is zero, even if min_precision is set.
* @param {Number} options.max_sig_digits Maximum number of significant digits.
* Will cut fractional part, but never integer part.
* @param {Boolean|String} options.group_sep Whether to show a separator every n
* digits, if a string, that value will be used as the separator. Default: ','
* @param {Number} options.group_width How many numbers will be grouped
* together, default: 3.
* @param {Boolean|String} options.signed Whether negative numbers will have a
* prefix. If String, that string will be used as the prefix. Default: '-'
* @param {Date|Number} options.reference_date Date based on which
* demurrage/interest should be applied. Can be given as JavaScript Date or int
* for Ripple epoch.
* @return {String} amount string
*/
Amount.prototype.to_human = function(options) {
const opts = options || {};
if (!this.is_valid()) {
return 'NaN';
}
/* eslint-disable consistent-this */
// Apply demurrage/interest
let ref = this;
/* eslint-enable consistent-this */
if (opts.reference_date) {
ref = this.applyInterest(opts.reference_date);
}
const isNegative = ref._value.isNegative();
const valueString = ref._value.abs().toFixed();
const parts = valueString.split('.');
let int_part = parts[0];
let fraction_part = parts.length === 2 ? parts[1] : '';
int_part = int_part.replace(/^0*/, '');
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') {
let precision = Math.max(0, opts.precision);
precision = Math.min(precision, fraction_part.length);
const rounded = Number('0.' + fraction_part).toFixed(precision);
if (rounded < 1) {
fraction_part = rounded.substring(2);
} else {
int_part = (Number(int_part) + 1).toString();
fraction_part = '';
}
while (fraction_part.length < precision) {
fraction_part = '0' + fraction_part;
}
}
// Limit the number of significant digits (max_sig_digits)
if (typeof opts.max_sig_digits === 'number') {
// First, we count the significant digits we have.
// A zero in the integer part does not count.
const int_is_zero = Number(int_part) === 0;
let digits = int_is_zero ? 0 : int_part.length;
// Don't count leading zeros in the fractional part if the integer part is
// zero.
const sig_frac = int_is_zero
? fraction_part.replace(/^0*/, '')
: fraction_part;
digits += sig_frac.length;
// Now we calculate where we are compared to the maximum
let rounding = digits - opts.max_sig_digits;
// If we're under the maximum we want to cut no (=0) digits
rounding = Math.max(rounding, 0);
// If we're over the maximum we still only want to cut digits from the
// fractional part, not from the integer part.
rounding = Math.min(rounding, fraction_part.length);
// Now we cut `rounding` digits off the right.
if (rounding > 0) {
fraction_part = fraction_part.slice(0, -rounding);
}
}
// Enforce the minimum number of decimal digits (min_precision)
if (typeof opts.min_precision === 'number') {
opts.min_precision = Math.max(0, opts.min_precision);
while (fraction_part.length < opts.min_precision) {
fraction_part += '0';
}
}
}
if (opts.group_sep !== false) {
const sep = (typeof opts.group_sep === 'string') ? opts.group_sep : ',';
const groups = utils.chunkString(int_part, opts.group_width || 3, true);
int_part = groups.join(sep);
}
let formatted = '';
if (isNegative && opts.signed !== false) {
formatted += '-';
}
formatted += int_part.length ? int_part : '0';
formatted += fraction_part.length ? '.' + fraction_part : '';
return formatted;
};
Amount.prototype.to_human_full = function(options) {
const opts = options || {};
const value = this.to_human(opts);
const currency = this._currency.to_human();
const issuer = this._issuer.to_json(opts);
const base = value + '/' + currency;
return this.is_native() ? base : (base + '/' + issuer);
};
Amount.prototype.to_json = function() {
if (this._is_native) {
return this.to_text();
}
const amount_json = {
value: this.to_text(),
currency: this._currency.has_interest() ?
this._currency.to_hex() : this._currency.to_json()
};
if (this._issuer.is_valid()) {
amount_json.issuer = this._issuer.to_json();
}
return amount_json;
};
Amount.prototype.to_text_full = function(opts) {
if (!this.is_valid()) {
return 'NaN';
}
return this._is_native
? this.to_human() + '/XRP'
: this.to_text() + '/' + this._currency.to_json()
+ '/' + this._issuer.to_json(opts);
};
// For debugging.
Amount.prototype.not_equals_why = function(d, ignore_issuer) {
if (typeof d === 'string') {
return this.not_equals_why(Amount.from_json(d));
}
if (!(d instanceof Amount)) {
return 'Not an Amount';
}
if (!this.is_valid() || !d.is_valid()) {
return 'Invalid amount.';
}
if (this._is_native !== d._is_native) {
return 'Native mismatch.';
}
const type = this._is_native ? 'XRP' : 'Non-XRP';
if (!this._value.isZero() && this._value.negate().equals(d._value)) {
return type + ' sign differs.';
}
if (!this._value.equals(d._value)) {
return type + ' value differs.';
}
if (!this._is_native) {
if (!this._currency.equals(d._currency)) {
return 'Non-XRP currency differs.';
}
if (!ignore_issuer && !this._issuer.equals(d._issuer)) {
return 'Non-XRP issuer differs: '
+ d._issuer.to_json()
+ '/'
+ this._issuer.to_json();
}
}
};
exports.Amount = Amount;
// DEPRECATED: Include the corresponding files instead.
exports.Currency = Currency;
exports.Seed = Seed;
exports.UInt160 = UInt160;
// vim:sw=2:sts=2:ts=8:et

View File

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

154
src/core/base.js Normal file
View File

@@ -0,0 +1,154 @@
'use strict';
const _ = require('lodash');
const sjcl = require('./utils').sjcl;
const utils = require('./utils');
const extend = require('extend');
const convertBase = require('./baseconverter');
const Base = {};
const alphabets = Base.alphabets = {
ripple: 'rpshnaf39wBUDNEGHJKLM4PQRST7VWXYZ2bcdeCg65jkm8oFqi1tuvAxyz',
tipple: 'RPShNAF39wBUDnEGHJKLM4pQrsT7VWXYZ2bcdeCg65jkm8ofqi1tuvaxyz',
bitcoin: '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'
};
extend(Base, {
VER_NONE: 1,
VER_NODE_PUBLIC: 28,
VER_NODE_PRIVATE: 32,
VER_ACCOUNT_ID: 0,
VER_ACCOUNT_PUBLIC: 35,
VER_ACCOUNT_PRIVATE: 34,
VER_FAMILY_GENERATOR: 41,
VER_FAMILY_SEED: 33
});
function sha256(bytes) {
return sjcl.codec.bytes.fromBits(
sjcl.hash.sha256.hash(sjcl.codec.bytes.toBits(bytes)));
}
function encodeString(alphabet, input) {
if (input.length === 0) {
return '';
}
const leadingZeros = _.takeWhile(input, function(d) {
return d === 0;
});
const out = convertBase(input, 256, 58).map(function(digit) {
if (digit < 0 || digit >= alphabet.length) {
throw new Error('Value ' + digit + ' is out of bounds for encoding');
}
return alphabet[digit];
});
const prefix = leadingZeros.map(function() {
return alphabet[0];
});
return prefix.concat(out).join('');
}
function decodeString(indexes, input) {
if (input.length === 0) {
return [];
}
const input58 = input.split('').map(function(c) {
const charCode = c.charCodeAt(0);
if (charCode >= indexes.length || indexes[charCode] === -1) {
throw new Error('Character ' + c + ' is not valid for encoding');
}
return indexes[charCode];
});
const leadingZeros = _.takeWhile(input58, function(d) {
return d === 0;
});
const out = convertBase(input58, 58, 256);
return leadingZeros.concat(out);
}
function Base58(alphabet) {
const indexes = utils.arraySet(128, -1);
for (let i = 0; i < alphabet.length; i++) {
indexes[alphabet.charCodeAt(i)] = i;
}
return {
decode: decodeString.bind(null, indexes),
encode: encodeString.bind(null, alphabet)
};
}
Base.encoders = {};
Object.keys(alphabets).forEach(function(alphabet) {
Base.encoders[alphabet] = new Base58(alphabets[alphabet]);
});
// --> input: big-endian array of bytes.
// <-- string at least as long as input.
Base.encode = function(input, alpha) {
return this.encoders[alpha || 'ripple'].encode(input);
};
// --> input: String
// <-- array of bytes or undefined.
Base.decode = function(input, alpha) {
if (typeof input !== 'string') {
return undefined;
}
try {
return this.encoders[alpha || 'ripple'].decode(input);
} catch (e) {
return undefined;
}
};
Base.verify_checksum = function(bytes) {
const computed = sha256(sha256(bytes.slice(0, -4))).slice(0, 4);
const checksum = bytes.slice(-4);
return _.isEqual(computed, checksum);
};
// --> input: Array
// <-- String
Base.encode_check = function(version, input, alphabet) {
const buffer = [].concat(version, input);
const check = sha256(sha256(buffer)).slice(0, 4);
return Base.encode([].concat(buffer, check), alphabet);
};
// --> input : String
// <-- NaN || sjcl.bn
Base.decode_check = function(version, input, alphabet) {
const buffer = Base.decode(input, alphabet);
if (!buffer || buffer.length < 5) {
return NaN;
}
// Single valid version
if (typeof version === 'number' && buffer[0] !== version) {
return NaN;
}
// Multiple allowed versions
if (Array.isArray(version) && _.every(version, function(v) {
return v !== buffer[0];
})) {
return NaN;
}
if (!Base.verify_checksum(buffer)) {
return NaN;
}
// We'll use the version byte to add a leading zero, this ensures JSBN doesn't
// intrepret the value as a negative number
buffer[0] = 0;
return sjcl.bn.fromBits(
sjcl.codec.bytes.toBits(buffer.slice(0, -4)));
};
exports.Base = Base;

32
src/core/baseconverter.js Normal file
View File

@@ -0,0 +1,32 @@
'use strict';
function normalize(digitArray) {
while (digitArray[0] === 0) {
digitArray.shift();
}
return digitArray;
}
function divmod(digitArray, base, divisor) {
var remainder = 0;
var quotient = [];
for (var j = 0; j < digitArray.length; j++) {
var temp = remainder * base + parseInt(digitArray[j], 10);
quotient.push(Math.floor(temp / divisor));
remainder = temp % divisor;
}
return {quotient: normalize(quotient), remainder: remainder};
}
function convertBase(digitArray, fromBase, toBase) {
var result = [];
var dividend = digitArray;
while (dividend.length > 0) {
var qr = divmod(dividend, fromBase, toBase);
result.unshift(qr.remainder);
dividend = qr.quotient;
}
return normalize(result);
}
module.exports = convertBase;

View File

@@ -1,10 +1,19 @@
'use strict';
/*eslint no-multi-spaces:0,space-in-brackets:0,key-spacing:0,comma-spacing:0*/
/**
* Data type map.
*
* Mapping of type ids to data types. The type id is specified by the high
*
* For reference, see rippled's definition:
* https://github.com/ripple/rippled/blob/develop/src/ripple/data/protocol
* /SField.cpp
*/
var TYPES_MAP = exports.types = [
void(0),
exports.types = [
undefined,
// Common
'Int16', // 1
@@ -17,11 +26,11 @@ var TYPES_MAP = exports.types = [
'Account', // 8
// 9-13 reserved
void(0), // 9
void(0), // 10
void(0), // 11
void(0), // 12
void(0), // 13
undefined, // 9
undefined, // 10
undefined, // 11
undefined, // 12
undefined, // 13
'Object', // 14
'Array', // 15
@@ -39,7 +48,7 @@ var TYPES_MAP = exports.types = [
* Mapping of field type id to field type name.
*/
var FIELDS_MAP = exports.fields = {
const FIELDS_MAP = exports.fields = {
// Common types
1: { // Int16
1: 'LedgerEntryType',
@@ -106,7 +115,8 @@ var FIELDS_MAP = exports.fields = {
16: 'BookDirectory',
17: 'InvoiceID',
18: 'Nickname',
19: 'Feature'
19: 'Amendment',
20: 'TicketID'
},
6: { // Amount
1: 'Amount',
@@ -135,7 +145,8 @@ var FIELDS_MAP = exports.fields = {
10: 'ExpireCode',
11: 'CreateCode',
12: 'MemoType',
13: 'MemoData'
13: 'MemoData',
14: 'MemoFormat'
},
8: { // Account
1: 'Account',
@@ -146,7 +157,7 @@ var FIELDS_MAP = exports.fields = {
8: 'RegularKey'
},
14: { // Object
1: void(0), //end of Object
1: undefined, // end of Object
2: 'TransactionMetaData',
3: 'CreatedNode',
4: 'DeletedNode',
@@ -158,7 +169,7 @@ var FIELDS_MAP = exports.fields = {
10: 'Memo'
},
15: { // Array
1: void(0), //end of Array
1: undefined, // end of Array
2: 'SigningAccounts',
3: 'TxnSignatures',
4: 'Signatures',
@@ -187,11 +198,11 @@ var FIELDS_MAP = exports.fields = {
19: { // Vector256
1: 'Indexes',
2: 'Hashes',
3: 'Features'
3: 'Amendments'
}
};
var INVERSE_FIELDS_MAP = exports.fieldsInverseMap = { };
let INVERSE_FIELDS_MAP = exports.fieldsInverseMap = { };
Object.keys(FIELDS_MAP).forEach(function(k1) {
Object.keys(FIELDS_MAP[k1]).forEach(function(k2) {
@@ -199,12 +210,11 @@ Object.keys(FIELDS_MAP).forEach(function(k1) {
});
});
const REQUIRED = exports.REQUIRED = 0,
OPTIONAL = exports.OPTIONAL = 1,
DEFAULT = exports.DEFAULT = 2;
var REQUIRED = exports.REQUIRED = 0,
OPTIONAL = exports.OPTIONAL = 1,
DEFAULT = exports.DEFAULT = 2;
var base = [
const base = [
[ 'TransactionType' , REQUIRED ],
[ 'Flags' , OPTIONAL ],
[ 'SourceTag' , OPTIONAL ],
@@ -214,7 +224,9 @@ var base = [
[ 'Fee' , REQUIRED ],
[ 'OperationLimit' , OPTIONAL ],
[ 'SigningPubKey' , REQUIRED ],
[ 'TxnSignature' , OPTIONAL ]
[ 'TxnSignature' , OPTIONAL ],
[ 'AccountTxnID' , OPTIONAL ],
[ 'Memos' , OPTIONAL ]
];
exports.tx = {
@@ -224,7 +236,9 @@ exports.tx = {
[ 'WalletSize' , OPTIONAL ],
[ 'MessageKey' , OPTIONAL ],
[ 'Domain' , OPTIONAL ],
[ 'TransferRate' , OPTIONAL ]
[ 'TransferRate' , OPTIONAL ],
[ 'SetFlag' , OPTIONAL ],
[ 'ClearFlag' , OPTIONAL ]
]),
TrustSet: [20].concat(base, [
[ 'LimitAmount' , OPTIONAL ],
@@ -234,13 +248,14 @@ exports.tx = {
OfferCreate: [7].concat(base, [
[ 'TakerPays' , REQUIRED ],
[ 'TakerGets' , REQUIRED ],
[ 'Expiration' , OPTIONAL ]
[ 'Expiration' , OPTIONAL ],
[ 'OfferSequence' , OPTIONAL ]
]),
OfferCancel: [8].concat(base, [
[ 'OfferSequence' , REQUIRED ]
]),
SetRegularKey: [5].concat(base, [
[ 'RegularKey' , REQUIRED ]
[ 'RegularKey' , OPTIONAL ]
]),
Payment: [0].concat(base, [
[ 'Destination' , REQUIRED ],
@@ -266,16 +281,25 @@ exports.tx = {
EnableFeature: [100].concat(base, [
[ 'Feature' , REQUIRED ]
]),
EnableAmendment: [100].concat(base, [
[ 'Amendment' , REQUIRED ]
]),
SetFee: [101].concat(base, [
[ 'Features' , REQUIRED ],
[ 'BaseFee' , REQUIRED ],
[ 'ReferenceFeeUnits' , REQUIRED ],
[ 'ReserveBase' , REQUIRED ],
[ 'ReserveIncrement' , REQUIRED ]
]),
TicketCreate: [10].concat(base, [
[ 'Target' , OPTIONAL ],
[ 'Expiration' , OPTIONAL ]
]),
TicketCancel: [11].concat(base, [
[ 'TicketID' , REQUIRED ]
])
};
var sleBase = [
const sleBase = [
['LedgerIndex', OPTIONAL],
['LedgerEntryType', REQUIRED],
['Flags', REQUIRED]
@@ -373,7 +397,7 @@ exports.ledger = {
['Balance', REQUIRED],
['LowLimit', REQUIRED],
['HighLimit', REQUIRED]])
}
};
exports.metadata = [
[ 'TransactionIndex' , REQUIRED ],
@@ -409,5 +433,8 @@ exports.ter = {
tecNO_TARGET : 138,
tecNO_PERMISSION : 139,
tecNO_ENTRY : 140,
tecINSUFFICIENT_RESERVE : 141
tecINSUFFICIENT_RESERVE : 141,
tecNEED_MASTER_KEY : 142,
tecDST_TAG_NEEDED : 143,
tecINTERNAL : 144
};

View File

@@ -1,4 +1,6 @@
var extend = require('extend');
'use strict';
var extend = require('extend');
var UInt160 = require('./uint160').UInt160;
var utils = require('./utils');
var Float = require('./ieee754').Float;
@@ -16,8 +18,7 @@ var Currency = extend(function() {
// 3-letter code: ...
// XXX Should support hex, C++ doesn't currently allow it.
this._value = NaN;
this._value = NaN;
this._update();
}, UInt160);
@@ -32,25 +33,37 @@ Currency.HEX_CURRENCY_BAD = '0000000000000000000000005852500000000000';
* Examples:
*
* USD => currency
* USD - Dollar => currency with optional full currency name
* XAU (-0.5%pa) => XAU with 0.5% effective demurrage rate per year
* USD - Dollar => currency with optional full currency
* name
* XAU (-0.5%pa) => XAU with 0.5% effective demurrage rate
* per year
* XAU - Gold (-0.5%pa) => Optionally allowed full currency name
* USD (1%pa) => US dollars with 1% effective interest per year
* USD (1%pa) => US dollars with 1% effective interest
* per year
* INR - Indian Rupees => Optional full currency name with spaces
* TYX - 30-Year Treasuries => Optional full currency with numbers and a dash
* TYX - 30-Year Treasuries (1.5%pa) => Optional full currency with numbers, dash and interest rate
* TYX - 30-Year Treasuries => Optional full currency with numbers
* and a dash
* TYX - 30-Year Treasuries (1.5%pa) => Optional full currency with numbers,
* dash and interest rate
*
* The regular expression below matches above cases, broken down for better understanding:
* The regular expression below matches above cases, broken down for better
* understanding:
*
* ^\s* // start with any amount of whitespace
* ([a-zA-Z]{3}|[0-9]{3}) // either 3 letter alphabetic currency-code or 3 digit numeric currency-code. See ISO 4217
* (\s*-\s*[- \w]+) // optional full currency name following the dash after currency code,
* full currency code can contain letters, numbers and dashes
* (\s*\(-?\d+\.?\d*%pa\))? // optional demurrage rate, has optional - and . notation (-0.5%pa)
* ([a-zA-Z]{3}|[0-9]{3}) // either 3 letter alphabetic currency-code or 3
* digit numeric currency-code. See ISO 4217
* (\s*-\s*[- \w]+) // optional full currency name following the dash
* after currency code, full currency code can
* contain letters, numbers and dashes
* (\s*\(-?\d+\.?\d*%pa\))? // optional demurrage rate, has optional - and
* . notation (-0.5%pa)
* \s*$ // end with any amount of whitespace
*
*/
Currency.prototype.human_RE = /^\s*([a-zA-Z0-9]{3})(\s*-\s*[- \w]+)?(\s*\(-?\d+\.?\d*%pa\))?\s*$/;
/*eslint-disable max-len*/
Currency.prototype.human_RE = /^\s*([a-zA-Z0-9\<\>\(\)\{\}\[\]\|\?\!\@\#\$\%\^\&]{3})(\s*-\s*[- \w]+)?(\s*\(-?\d+\.?\d*%pa\))?\s*$/;
/*eslint-enable max-len*/
Currency.from_json = function(j, shouldInterpretXrpAsIou) {
return (new Currency()).parse_json(j, shouldInterpretXrpAsIou);
@@ -58,39 +71,65 @@ Currency.from_json = function(j, shouldInterpretXrpAsIou) {
Currency.from_human = function(j, opts) {
return (new Currency().parse_human(j, opts));
}
};
// this._value = NaN on error.
Currency.prototype.parse_json = function(j, shouldInterpretXrpAsIou) {
this._value = NaN;
switch (typeof j) {
case 'string':
if (j instanceof Currency) {
this._value = j.copyTo({})._value;
this._update();
return this;
}
// if an empty string is given, fall back to XRP
switch (typeof j) {
case 'number':
if (!isNaN(j)) {
this.parse_number(j);
}
break;
case 'string':
if (!j || j === '0') {
this.parse_hex(shouldInterpretXrpAsIou ? Currency.HEX_CURRENCY_BAD : Currency.HEX_ZERO);
// Empty string or XRP
this.parse_hex(shouldInterpretXrpAsIou
? Currency.HEX_CURRENCY_BAD
: Currency.HEX_ZERO);
break;
}
if (j === '1') {
// 'no currency'
this.parse_hex(Currency.HEX_ONE);
break;
}
if (/^[A-F0-9]{40}$/.test(j)) {
// Hex format
this.parse_hex(j);
break;
}
// match the given string to see if it's in an allowed format
var matches = String(j).match(this.human_RE);
var matches = j.match(this.human_RE);
if (matches) {
var currencyCode = matches[1];
// for the currency 'XRP' case
// we drop everything else that could have been provided
// e.g. 'XRP - Ripple'
if (!currencyCode || /^(0|XRP)$/.test(currencyCode)) {
this.parse_hex(shouldInterpretXrpAsIou ? Currency.HEX_CURRENCY_BAD : Currency.HEX_ZERO);
this.parse_hex(shouldInterpretXrpAsIou
? Currency.HEX_CURRENCY_BAD
: Currency.HEX_ZERO);
// early break, we can't have interest on XRP
break;
}
// the full currency is matched as it is part of the valid currency format, but not stored
// the full currency is matched as it is part of the valid currency
// format, but not stored
// var full_currency = matches[2] || '';
var interest = matches[3] || '';
@@ -117,25 +156,28 @@ Currency.prototype.parse_json = function(j, shouldInterpretXrpAsIou) {
currencyData[2] = currencyCode.charCodeAt(1) & 0xff;
currencyData[3] = currencyCode.charCodeAt(2) & 0xff;
// byte 5-8 are for reference date, but should always be 0 so we won't fill it
// byte 5-8 are for reference date, but should always be 0 so we
// won't fill it
// byte 9-16 are for the interest
percentage = parseFloat(percentage[0]);
// the interest or demurrage is expressed as a yearly (per annum) value
// the interest or demurrage is expressed as a yearly (per annum)
// value
var secondsPerYear = 31536000; // 60 * 60 * 24 * 365
// Calculating the interest e-fold
// 0.5% demurrage is expressed 0.995, 0.005 less than 1
// 0.5% interest is expressed as 1.005, 0.005 more than 1
var interestEfold = secondsPerYear / Math.log(1 + percentage/100);
var interestEfold = secondsPerYear / Math.log(1 + percentage / 100);
var bytes = Float.toIEEE754Double(interestEfold);
for (var i=0; i<=bytes.length; i++) {
for (var i = 0; i <= bytes.length; i++) {
currencyData[8 + i] = bytes[i] & 0xff;
}
// the last 4 bytes are reserved for future use, so we won't fill those
// the last 4 bytes are reserved for future use, so we won't fill
// those
} else {
currencyData[12] = currencyCode.charCodeAt(0) & 0xff;
@@ -144,21 +186,6 @@ Currency.prototype.parse_json = function(j, shouldInterpretXrpAsIou) {
}
this.parse_bytes(currencyData);
} else {
this.parse_hex(j);
}
break;
case 'number':
if (!isNaN(j)) {
this.parse_number(j);
}
break;
case 'object':
if (j instanceof Currency) {
this._value = j.copyTo({})._value;
this._update();
}
break;
}
@@ -166,7 +193,6 @@ Currency.prototype.parse_json = function(j, shouldInterpretXrpAsIou) {
return this;
};
Currency.prototype.parse_human = function(j) {
return this.parse_json(j);
};
@@ -176,6 +202,7 @@ Currency.prototype.parse_human = function(j) {
*
* You should never need to call this.
*/
Currency.prototype._update = function() {
var bytes = this.to_bytes();
@@ -183,7 +210,7 @@ Currency.prototype._update = function() {
var isZeroExceptInStandardPositions = true;
if (!bytes) {
return 'XRP';
return;
}
this._native = false;
@@ -192,8 +219,9 @@ Currency.prototype._update = function() {
this._interest_period = NaN;
this._iso_code = '';
for (var i=0; i<20; i++) {
isZeroExceptInStandardPositions = isZeroExceptInStandardPositions && (i===12 || i===13 || i===14 || bytes[i]===0);
for (var i = 0; i < 20; i++) {
isZeroExceptInStandardPositions = isZeroExceptInStandardPositions
&& (i === 12 || i === 13 || i === 14 || bytes[i] === 0);
}
if (isZeroExceptInStandardPositions) {
@@ -201,7 +229,7 @@ Currency.prototype._update = function() {
+ String.fromCharCode(bytes[13])
+ String.fromCharCode(bytes[14]);
if (this._iso_code === '\0\0\0') {
if (this._iso_code === '\u0000\u0000\u0000') {
this._native = true;
this._iso_code = 'XRP';
}
@@ -215,8 +243,8 @@ Currency.prototype._update = function() {
this._type = 1;
this._interest_start = (bytes[4] << 24) +
(bytes[5] << 16) +
(bytes[6] << 8) +
(bytes[7] );
(bytes[6] << 8) +
(bytes[7]);
this._interest_period = Float.fromIEEE754Double(bytes.slice(8, 16));
}
};
@@ -230,7 +258,8 @@ Currency.prototype.parse_bytes = function(byte_array) {
var isZeroExceptInStandardPositions = true;
for (var i=0; i<20; i++) {
isZeroExceptInStandardPositions = isZeroExceptInStandardPositions && (i===12 || i===13 || i===14 || byte_array[0]===0)
isZeroExceptInStandardPositions = isZeroExceptInStandardPositions
&& (i===12 || i===13 || i===14 || byte_array[0]===0)
}
if (isZeroExceptInStandardPositions) {
@@ -260,20 +289,25 @@ Currency.prototype.is_native = function() {
};
/**
* Whether this currency is an interest-bearing/demurring currency.
* @return {Boolean} whether this currency is an interest-bearing currency
*/
Currency.prototype.has_interest = function() {
return this._type === 1 && !isNaN(this._interest_start) && !isNaN(this._interest_period);
return this._type === 1
&& !isNaN(this._interest_start)
&& !isNaN(this._interest_period);
};
/**
*
* @param referenceDate - number of seconds since the Ripple Epoch (0:00 on January 1, 2000 UTC)
* used to calculate the interest over provided interval
* pass in one years worth of seconds to ge the yearly interest
* @returns {number} - interest for provided interval, can be negative for demurred currencies
* @param {number} referenceDate number of seconds since the Ripple Epoch
* (0:00 on January 1, 2000 UTC) used to calculate the
* interest over provided interval pass in one years
* worth of seconds to ge the yearly interest
* @returns {number} interest for provided interval, can be negative for
* demurred currencies
*/
Currency.prototype.get_interest_at = function(referenceDate, decimals) {
Currency.prototype.get_interest_at = function(referenceDate) {
if (!this.has_interest()) {
return 0;
}
@@ -288,18 +322,20 @@ Currency.prototype.get_interest_at = function(referenceDate, decimals) {
}
// calculate interest by e-fold number
return Math.exp((referenceDate - this._interest_start) / this._interest_period);
return Math.exp((referenceDate - this._interest_start)
/ this._interest_period);
};
Currency.prototype.get_interest_percentage_at = function(referenceDate, decimals) {
Currency.prototype.get_interest_percentage_at
= function(referenceDate, decimals) {
var interest = this.get_interest_at(referenceDate, decimals);
// convert to percentage
var interest = (interest*100)-100;
var decimalMultiplier = decimals ? Math.pow(10,decimals) : 100;
interest = (interest * 100) - 100;
var decimalMultiplier = decimals ? Math.pow(10, decimals) : 100;
// round to two decimals behind the dot
return Math.round(interest*decimalMultiplier) / decimalMultiplier;
return Math.round(interest * decimalMultiplier) / decimalMultiplier;
};
// XXX Currently we inherit UInt.prototype.is_valid, which is mostly fine.
@@ -307,9 +343,9 @@ Currency.prototype.get_interest_percentage_at = function(referenceDate, decimals
// We could be doing further checks into the internal format of the
// currency data, since there are some values that are invalid.
//
//Currency.prototype.is_valid = function() {
// return this._value instanceof BigInteger && ...;
//};
// Currency.prototype.is_valid = function() {
// return UInt.prototype.is_valid() && ...;
// };
Currency.prototype.to_json = function(opts) {
if (!this.is_valid()) {
@@ -317,28 +353,35 @@ Currency.prototype.to_json = function(opts) {
return 'XRP';
}
var opts = opts || {};
if (!opts) {
opts = {};
}
var currency;
var fullName = opts && opts.full_name ? ' - ' + opts.full_name : '';
opts.show_interest = opts.show_interest !== void(0) ? opts.show_interest : this.has_interest();
opts.show_interest = opts.show_interest !== undefined
? opts.show_interest
: this.has_interest();
if (!opts.force_hex && /^[A-Z0-9]{3}$/.test(this._iso_code)) {
currency = this._iso_code + fullName;
if (opts.show_interest) {
var decimals = !isNaN(opts.decimals) ? opts.decimals : void(0);
var interestPercentage = this.has_interest() ? this.get_interest_percentage_at(this._interest_start + 3600 * 24 * 365, decimals) : 0;
var decimals = !isNaN(opts.decimals) ? opts.decimals : undefined;
var interestPercentage = this.has_interest()
? this.get_interest_percentage_at(
this._interest_start + 3600 * 24 * 365, decimals
)
: 0;
currency += ' (' + interestPercentage + '%pa)';
}
} else {
// Fallback to returning the raw currency hex
currency = this.to_hex();
// XXX This is to maintain backwards compatibility, but it is very, very odd
// behavior, so we should deprecate it and get rid of it as soon as
// possible.
// XXX This is to maintain backwards compatibility, but it is very, very
// odd behavior, so we should deprecate it and get rid of it as soon as
// possible.
if (currency === Currency.HEX_ONE) {
currency = 1;
}
@@ -357,5 +400,3 @@ Currency.prototype.get_iso = function() {
};
exports.Currency = Currency;
// vim:sw=2:sts=2:ts=8:et

View File

@@ -1,3 +1,10 @@
'use strict';
// TODO: move in helpers from serializedtypes to utils
function toBytes(n) {
return [n >>> 24, (n >>> 16) & 0xff, (n >>> 8) & 0xff, n & 0xff];
}
/**
* Prefix for hashing functions.
*
@@ -12,14 +19,18 @@
*/
// transaction plus signature to give transaction ID
exports.HASH_TX_ID = 0x54584E00; // 'TXN'
exports.HASH_TX_ID = 0x54584E00; // 'TXN'
// transaction plus metadata
exports.HASH_TX_NODE = 0x534E4400; // 'TND'
exports.HASH_TX_NODE = 0x534E4400; // 'TND'
// inner node in tree
exports.HASH_INNER_NODE = 0x4D494E00; // 'MIN'
exports.HASH_INNER_NODE = 0x4D494E00; // 'MIN'
// leaf node in tree
exports.HASH_LEAF_NODE = 0x4D4C4E00; // 'MLN'
exports.HASH_LEAF_NODE = 0x4D4C4E00; // 'MLN'
// inner transaction to sign
exports.HASH_TX_SIGN = 0x53545800; // 'STX'
exports.HASH_TX_SIGN = 0x53545800; // 'STX'
// inner transaction to sign (TESTNET)
exports.HASH_TX_SIGN_TESTNET = 0x73747800; // 'stx'
Object.keys(exports).forEach(function(k) {
exports[k + '_BYTES'] = toBytes(exports[k]);
});

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