mirror of
https://github.com/Xahau/xahau.js.git
synced 2025-11-07 22:35:48 +00:00
Compare commits
157 Commits
0.13.0-rc1
...
0.13.0-rc5
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9c9be3e6e4 | ||
|
|
062561686e | ||
|
|
761682c206 | ||
|
|
39c48d631c | ||
|
|
a55d26a726 | ||
|
|
597ae157b3 | ||
|
|
c1c7458914 | ||
|
|
0e97f269ab | ||
|
|
bbe4cd63a1 | ||
|
|
2515d17a85 | ||
|
|
d8e95a3c3b | ||
|
|
98f6bed8c9 | ||
|
|
bd000c2662 | ||
|
|
a46141111a | ||
|
|
f57c89c6e9 | ||
|
|
6d4cac948d | ||
|
|
1f54b3a0cf | ||
|
|
2f8655dc23 | ||
|
|
d624923cd8 | ||
|
|
2180c076dd | ||
|
|
0dbdf0a21a | ||
|
|
5cb63a258c | ||
|
|
39ac6caaef | ||
|
|
de4ef8b2b4 | ||
|
|
99cba09a4a | ||
|
|
300967f0f3 | ||
|
|
52879febb9 | ||
|
|
f077a563c4 | ||
|
|
92fbc61f47 | ||
|
|
5ac1bcc414 | ||
|
|
5837aa23ea | ||
|
|
8c431b4ec3 | ||
|
|
25086a7944 | ||
|
|
b0889b4afe | ||
|
|
ed971bc41c | ||
|
|
728595dc96 | ||
|
|
7fc6adb776 | ||
|
|
002102ce62 | ||
|
|
bf9da80d46 | ||
|
|
dda9994869 | ||
|
|
4c76ad159e | ||
|
|
f76a8daca8 | ||
|
|
3263629ebe | ||
|
|
fcbe7d3c98 | ||
|
|
a6662ccdff | ||
|
|
854fe85151 | ||
|
|
2b2fdf1b11 | ||
|
|
cbe44d6a96 | ||
|
|
420346faea | ||
|
|
6220162852 | ||
|
|
37198bde66 | ||
|
|
281c056f6c | ||
|
|
49a513cd07 | ||
|
|
7a95aabbf4 | ||
|
|
83874ec096 | ||
|
|
9270d0a33d | ||
|
|
30295efdb4 | ||
|
|
f1342c1456 | ||
|
|
194b73c293 | ||
|
|
89e5f79bbb | ||
|
|
82d7ce7ac2 | ||
|
|
0cc4c704f8 | ||
|
|
dde762a1d6 | ||
|
|
1c86e246c7 | ||
|
|
2d173c8e69 | ||
|
|
600fd34d30 | ||
|
|
4cb9cf801c | ||
|
|
50fb8789b4 | ||
|
|
fb8dc44ec1 | ||
|
|
0781caa8bc | ||
|
|
2cdb23f0dd | ||
|
|
8e536c00b9 | ||
|
|
8ff154cc2d | ||
|
|
daaae6e01e | ||
|
|
a64a4e697a | ||
|
|
3f51d8cc12 | ||
|
|
9f9e76f8b9 | ||
|
|
5b51db158d | ||
|
|
a4d1509448 | ||
|
|
bbd51a03b6 | ||
|
|
b55b82b2fd | ||
|
|
fdb0f101bd | ||
|
|
0afca5633d | ||
|
|
7c0d9a7172 | ||
|
|
f6b7e27c67 | ||
|
|
b8624bc55f | ||
|
|
2eec30756d | ||
|
|
6b44ce8973 | ||
|
|
ed0b501716 | ||
|
|
0fd391af72 | ||
|
|
fe9c1ada88 | ||
|
|
4c1f4ef58c | ||
|
|
10afc770ff | ||
|
|
8543e60f86 | ||
|
|
116d7e0f29 | ||
|
|
68adaec55b | ||
|
|
03640efef5 | ||
|
|
c6f450842e | ||
|
|
e583eb4592 | ||
|
|
7fffbe0c64 | ||
|
|
63e3b71eb5 | ||
|
|
823ef738fe | ||
|
|
0977ef0ec2 | ||
|
|
cecf3f3d22 | ||
|
|
472fbce23a | ||
|
|
e44aea1767 | ||
|
|
d682d90d86 | ||
|
|
141aa17dfc | ||
|
|
0b09e53479 | ||
|
|
528d8bf25d | ||
|
|
03a2109e24 | ||
|
|
b38b9bced6 | ||
|
|
ea063d0c95 | ||
|
|
7f93929014 | ||
|
|
4cd10ecb87 | ||
|
|
6ef30debd2 | ||
|
|
4766bace4e | ||
|
|
261500a3a4 | ||
|
|
fae22b7023 | ||
|
|
4568b39997 | ||
|
|
4a218cacfa | ||
|
|
34a4dd3077 | ||
|
|
a383bd7e52 | ||
|
|
e76e693bdb | ||
|
|
2c52e4aa69 | ||
|
|
13dee36e93 | ||
|
|
6e180439d1 | ||
|
|
e8d0c1ae95 | ||
|
|
068bda0c95 | ||
|
|
ab694381d5 | ||
|
|
dc2a6c75cf | ||
|
|
98dbba8f27 | ||
|
|
9a1b80d77a | ||
|
|
a655be30d6 | ||
|
|
e5aabc3072 | ||
|
|
2cd32d58ad | ||
|
|
0c02b92717 | ||
|
|
c58a077a2f | ||
|
|
6e7dc9d7d3 | ||
|
|
572c945274 | ||
|
|
c605efab61 | ||
|
|
2695f4302a | ||
|
|
a17011243e | ||
|
|
9a533ab807 | ||
|
|
a037952493 | ||
|
|
dc96795a02 | ||
|
|
8d9746d7b1 | ||
|
|
00342c4239 | ||
|
|
e48df2c1fd | ||
|
|
6ade0f6554 | ||
|
|
a88157bb92 | ||
|
|
00f318284f | ||
|
|
2e12dc6d53 | ||
|
|
34435d4d05 | ||
|
|
a99452b773 | ||
|
|
a05cb39ab0 | ||
|
|
0c69f7f10e |
14
HISTORY.md
14
HISTORY.md
@@ -1,3 +1,17 @@
|
||||
##0.13.0 (release candidate)
|
||||
|
||||
+ [Deprecate core and remove snake case method copying](https://github.com/ripple/ripple-lib/commit/fb8dc44ec1d49bb05cd0cdbe6dd4ab211195868a)
|
||||
+ Add new RippleAPI interface
|
||||
- [RippleAPI README and samples](https://github.com/ripple/ripple-lib/tree/develop/docs/samples)
|
||||
- [Method documentation](https://rawgit.com/ripple/ripple-lib/develop/docs/api.html)
|
||||
+ [Fix RangeSet for validated_ledger as single ledger](https://github.com/ripple/ripple-lib/commit/9f9e76f8b933201651af59307135f67cfa7d60e8)
|
||||
+ [Fix bug where the paths would be set with an empty array](https://github.com/ripple/ripple-lib/commit/83874ec0962da311b76f2385623e51c68bc39035)
|
||||
+ [Fix reserve calculation](https://github.com/ripple/ripple-lib/commit/52879febb92d876f01f2e4d70871baa07af631fb)
|
||||
|
||||
##0.12.6
|
||||
|
||||
+ [Fix webpack require failure due to "./" notation](https://github.com/ripple/ripple-lib/commit/8d9746d7b10be203ee613df523c2522012ff1baf)
|
||||
|
||||
##0.12.15
|
||||
|
||||
+ [Add offer autobridging](https://github.com/ripple/ripple-lib/commit/c7bbce83719c1e8c6a4fae5ca850e7515db1a4a5)
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
A JavaScript API for interacting with Ripple in Node.js and the browser
|
||||
|
||||
[](https://travis-ci.org/ripple/ripple-lib) [](https://coveralls.io/r/ripple/ripple-lib?branch=develop)
|
||||
[](https://circleci.com/gh/ripple/ripple-lib/tree/develop) [](https://coveralls.io/r/ripple/ripple-lib?branch=develop)
|
||||
|
||||
[](https://www.npmjs.org/package/ripple-lib)
|
||||
|
||||
|
||||
13
bin/ci.sh
13
bin/ci.sh
@@ -5,19 +5,28 @@ TOTAL_NODES="$2"
|
||||
|
||||
typecheck() {
|
||||
npm install -g flow-bin
|
||||
flow --version
|
||||
npm run typecheck
|
||||
}
|
||||
|
||||
lint() {
|
||||
echo "eslint $(node_modules/.bin/eslint --version)"
|
||||
npm list babel-eslint | grep babel-eslint
|
||||
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$")
|
||||
echo "parser: babel-eslint" >> ./eslintrc
|
||||
node_modules/.bin/eslint -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() {
|
||||
# test "src"
|
||||
npm test --coverage
|
||||
npm run coveralls
|
||||
|
||||
# test compiled version in "dist/npm"
|
||||
ln -nfs ../../dist/npm/core test/node_modules/ripple-lib
|
||||
ln -nfs ../../dist/npm test/node_modules/ripple-api
|
||||
npm test
|
||||
}
|
||||
|
||||
oneNode() {
|
||||
|
||||
358
docs/api.html
Normal file
358
docs/api.html
Normal file
@@ -0,0 +1,358 @@
|
||||
<html>
|
||||
<head>
|
||||
<style>
|
||||
a {
|
||||
text-decoration: none;
|
||||
}
|
||||
.method {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.details {
|
||||
font-family: "Courier New", monospace;
|
||||
white-space: pre;
|
||||
display: none;
|
||||
}
|
||||
</style>
|
||||
<script type="text/javascript">
|
||||
function toggle(element) {
|
||||
var results = element.parentElement.getElementsByClassName('details');
|
||||
if (results.length > 0) {
|
||||
var style = results[0].style;
|
||||
style.display = (style.display === 'block') ? 'none' : 'block';
|
||||
}
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<h2><a href="https://github.com/ripple/ripple-lib/blob/develop/src/api/index.js">RippleAPI</a></h2>
|
||||
|
||||
<div class="method">
|
||||
connect()
|
||||
</div>
|
||||
|
||||
<div class="method">
|
||||
disconnect()
|
||||
</div>
|
||||
|
||||
<div class="method">
|
||||
isConnected()
|
||||
</div>
|
||||
|
||||
<div class="method">
|
||||
getServerInfo()
|
||||
</div>
|
||||
|
||||
<div class="method">
|
||||
getFee()
|
||||
</div>
|
||||
|
||||
<div class="method">
|
||||
getLedgerVersion()
|
||||
</div>
|
||||
|
||||
<div class="method">
|
||||
<a href="https://github.com/ripple/ripple-lib/blob/develop/src/api/common/schemas/get-transaction.json">getTransaction</a>(
|
||||
<a href="https://github.com/ripple/ripple-lib/blob/develop/src/api/common/schemas/hash256.json">identifier</a>[,
|
||||
<a href="https://github.com/ripple/ripple-lib/blob/develop/src/api/common/schemas/transaction-options.json">options</a>]
|
||||
)
|
||||
<a href="#" onclick="javascript:toggle(this)">+</a>
|
||||
<div class="details">
|
||||
identifier: txhash
|
||||
options: {minLedgerVersion: int, maxLedgerVersion: int}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="method">
|
||||
<a href="https://github.com/ripple/ripple-lib/blob/develop/src/api/common/schemas/get-transactions.json">getTransactions</a>(
|
||||
<a href="https://github.com/ripple/ripple-lib/blob/develop/src/api/common/schemas/address.json">account</a>[,
|
||||
<a href="https://github.com/ripple/ripple-lib/blob/develop/src/api/common/schemas/transactions-options.json">options</a>]
|
||||
)
|
||||
<a href="#" onclick="javascript:toggle(this)">+</a>
|
||||
<div class="details">
|
||||
options: {
|
||||
start: txhash,
|
||||
limit: int,
|
||||
minLedgerVersion: int,
|
||||
maxLedgerVersion: int,
|
||||
earliestFirst: bool,
|
||||
excludeFailures: bool,
|
||||
initiated: bool,
|
||||
counterparty: address,
|
||||
types: [string],
|
||||
binary: bool
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="method">
|
||||
<a href="https://github.com/ripple/ripple-lib/blob/develop/src/api/common/schemas/get-trustlines.json">getTrustlines</a>(
|
||||
<a href="https://github.com/ripple/ripple-lib/blob/develop/src/api/common/schemas/address.json">account</a>[,
|
||||
<a href="https://github.com/ripple/ripple-lib/blob/develop/src/api/common/schemas/trustlines-options.json">options</a>]
|
||||
)
|
||||
<a href="#" onclick="javascript:toggle(this)">+</a>
|
||||
<div class="details">
|
||||
options: {counterparty: address, currency: string, limit: int, ledgerVersion: int}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="method">
|
||||
<a href="https://github.com/ripple/ripple-lib/blob/develop/src/api/common/schemas/get-balances.json">getBalances</a>(
|
||||
<a href="https://github.com/ripple/ripple-lib/blob/develop/src/api/common/schemas/address.json">account</a>[,
|
||||
<a href="https://github.com/ripple/ripple-lib/blob/develop/src/api/common/schemas/trustlines-options.json">options</a>]
|
||||
)
|
||||
<a href="#" onclick="javascript:toggle(this)">+</a>
|
||||
<div class="details">
|
||||
options: {counterparty: address, currency: string, limit: int, ledgerVersion: int}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="method">
|
||||
<a href="https://github.com/ripple/ripple-lib/blob/develop/src/api/common/schemas/get-paths.json">getPaths</a>(
|
||||
<a href="https://github.com/ripple/ripple-lib/blob/develop/src/api/common/schemas/pathfind.json">pathfind</a>
|
||||
)
|
||||
<a href="#" onclick="javascript:toggle(this)">+</a>
|
||||
<div class="details">
|
||||
pathfind: {
|
||||
source: {
|
||||
address: address,
|
||||
currencies: [{
|
||||
currency: string,
|
||||
counterparty: address
|
||||
}]
|
||||
},
|
||||
destination: adjustment
|
||||
}
|
||||
|
||||
adjustment = {address: address, amount: amount, tag?: int}
|
||||
amount = {currency: string, counterparty: address, value: floatstr}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="method">
|
||||
<a href="https://github.com/ripple/ripple-lib/blob/develop/src/api/common/schemas/get-orders.json">getOrders</a>(
|
||||
<a href="https://github.com/ripple/ripple-lib/blob/develop/src/api/common/schemas/address.json">account</a>[,
|
||||
<a href="https://github.com/ripple/ripple-lib/blob/develop/src/api/common/schemas/orders-options.json">options</a>]
|
||||
)
|
||||
<a href="#" onclick="javascript:toggle(this)">+</a>
|
||||
<div class="details">
|
||||
options: {limit: int, ledverVersion: int}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="method">
|
||||
<a href="https://github.com/ripple/ripple-lib/blob/develop/src/api/common/schemas/get-orderbook.json">getOrderbook</a>(
|
||||
<a href="https://github.com/ripple/ripple-lib/blob/develop/src/api/common/schemas/address.json">account</a>,
|
||||
<a href="https://github.com/ripple/ripple-lib/blob/develop/src/api/common/schemas/orderbook.json">orderbook</a>[,
|
||||
<a href="https://github.com/ripple/ripple-lib/blob/develop/src/api/common/schemas/orders-options.json">options</a>]
|
||||
)
|
||||
<a href="#" onclick="javascript:toggle(this)">+</a>
|
||||
<div class="details">
|
||||
orderbook: {
|
||||
base: {
|
||||
currency,
|
||||
counterparty
|
||||
},
|
||||
counter: {
|
||||
currency,
|
||||
counterparty
|
||||
}
|
||||
}
|
||||
options: {limit: int, ledverVersion: int}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="method">
|
||||
<a href="https://github.com/ripple/ripple-lib/blob/develop/src/api/common/schemas/get-settings.json">getSettings</a>(
|
||||
<a href="https://github.com/ripple/ripple-lib/blob/develop/src/api/common/schemas/address.json">account</a>[,
|
||||
<a href="https://github.com/ripple/ripple-lib/blob/develop/src/api/common/schemas/settings-options.json">options</a>]
|
||||
)
|
||||
<a href="#" onclick="javascript:toggle(this)">+</a>
|
||||
<div class="details">
|
||||
options: {ledgerVersion: int}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="method">
|
||||
<a href="https://github.com/ripple/ripple-lib/blob/develop/src/api/common/schemas/get-account-info.json">getAccountInfo</a>(
|
||||
<a href="https://github.com/ripple/ripple-lib/blob/develop/src/api/common/schemas/address.json">account</a>[,
|
||||
<a href="https://github.com/ripple/ripple-lib/blob/develop/src/api/common/schemas/settings-options.json">options</a>]
|
||||
)
|
||||
<a href="#" onclick="javascript:toggle(this)">+</a>
|
||||
<div class="details">
|
||||
options: {ledgerVersion: int}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="method">
|
||||
<a href="https://github.com/ripple/ripple-lib/blob/develop/src/api/common/schemas/tx.json">preparePayment</a>(
|
||||
<a href="https://github.com/ripple/ripple-lib/blob/develop/src/api/common/schemas/address.json">account</a>,
|
||||
<a href="https://github.com/ripple/ripple-lib/blob/develop/src/api/common/schemas/payment.json">payment</a>[,
|
||||
<a href="https://github.com/ripple/ripple-lib/blob/develop/src/api/common/schemas/instructions.json">instructions</a>]
|
||||
)
|
||||
<a href="#" onclick="javascript:toggle(this)">+</a>
|
||||
<div class="details">
|
||||
payment: {
|
||||
source: adjustment,
|
||||
destination: adjustment,
|
||||
paths?: string,
|
||||
slippage?: strfloat,
|
||||
memos?: [{
|
||||
type: string,
|
||||
format: string,
|
||||
data: string
|
||||
}],
|
||||
invoiceID?: hash256,
|
||||
allowPartialPayment?: bool,
|
||||
noDirectRipple?: bool,
|
||||
limitQuality?: bool
|
||||
}
|
||||
|
||||
adjustment = {address: address, amount: amount, tag?: int}
|
||||
amount = {currency: string, counterparty: address, value: floatstr}
|
||||
|
||||
instructions: {
|
||||
sequence: int,
|
||||
fee: floatstr,
|
||||
maxFee: floatstr,
|
||||
maxLedgerVersion: int,
|
||||
maxLedgerVersionOffset: int
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="method">
|
||||
<a href="https://github.com/ripple/ripple-lib/blob/develop/src/api/common/schemas/tx.json">prepareTrustline</a>(
|
||||
<a href="https://github.com/ripple/ripple-lib/blob/develop/src/api/common/schemas/address.json">account</a>,
|
||||
<a href="https://github.com/ripple/ripple-lib/blob/develop/src/api/common/schemas/trustline.json">trustline</a>[,
|
||||
<a href="https://github.com/ripple/ripple-lib/blob/develop/src/api/common/schemas/instructions.json">instructions</a>]
|
||||
)
|
||||
<a href="#" onclick="javascript:toggle(this)">+</a>
|
||||
<div class="details">
|
||||
trustline: {
|
||||
currency: string,
|
||||
counterparty: address,
|
||||
limit: strfloat,
|
||||
qualityIn?: float,
|
||||
qualityOut?: float,
|
||||
allowRippling?: bool,
|
||||
authorized?: bool,
|
||||
frozen?: bool
|
||||
}
|
||||
|
||||
instructions: {
|
||||
sequence: int,
|
||||
fee: floatstr,
|
||||
maxFee: floatstr,
|
||||
maxLedgerVersion: int,
|
||||
maxLedgerVersionOffset: int
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="method">
|
||||
<a href="https://github.com/ripple/ripple-lib/blob/develop/src/api/common/schemas/tx.json">prepareOrder</a>(
|
||||
<a href="https://github.com/ripple/ripple-lib/blob/develop/src/api/common/schemas/address.json">account</a>,
|
||||
<a href="https://github.com/ripple/ripple-lib/blob/develop/src/api/common/schemas/order.json">order</a>[,
|
||||
<a href="https://github.com/ripple/ripple-lib/blob/develop/src/api/common/schemas/instructions.json">instructions</a>]
|
||||
)
|
||||
<a href="#" onclick="javascript:toggle(this)">+</a>
|
||||
<div class="details">
|
||||
order: {
|
||||
direction: ("buy"|"sell"),
|
||||
quantity: amount,
|
||||
totalPrice: amount,
|
||||
immediateOrCancel?: bool,
|
||||
fillOrKill?: bool,
|
||||
passive?: bool
|
||||
}
|
||||
|
||||
amount = {currency: string, counterparty: address, value: floatstr}
|
||||
|
||||
instructions: {
|
||||
sequence: int,
|
||||
fee: floatstr,
|
||||
maxFee: floatstr,
|
||||
maxLedgerVersion: int,
|
||||
maxLedgerVersionOffset: int
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="method">
|
||||
<a href="https://github.com/ripple/ripple-lib/blob/develop/src/api/common/schemas/tx.json">prepareOrderCancellation</a>(
|
||||
<a href="https://github.com/ripple/ripple-lib/blob/develop/src/api/common/schemas/address.json">account</a>,
|
||||
<a href="https://github.com/ripple/ripple-lib/blob/develop/src/api/common/schemas/sequence.json">sequence</a>[,
|
||||
<a href="https://github.com/ripple/ripple-lib/blob/develop/src/api/common/schemas/instructions.json">instructions</a>]
|
||||
)
|
||||
<a href="#" onclick="javascript:toggle(this)">+</a>
|
||||
<div class="details">
|
||||
sequence: int
|
||||
|
||||
instructions: {
|
||||
sequence: int,
|
||||
fee: floatstr,
|
||||
maxFee: floatstr,
|
||||
maxLedgerVersion: int,
|
||||
maxLedgerVersionOffset: int
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="method">
|
||||
<a href="https://github.com/ripple/ripple-lib/blob/develop/src/api/common/schemas/tx.json">prepareSettings</a>(
|
||||
<a href="https://github.com/ripple/ripple-lib/blob/develop/src/api/common/schemas/address.json">account</a>,
|
||||
<a href="https://github.com/ripple/ripple-lib/blob/develop/src/api/common/schemas/settings.json">settings</a>[,
|
||||
<a href="https://github.com/ripple/ripple-lib/blob/develop/src/api/common/schemas/instructions.json">instructions</a>]
|
||||
)
|
||||
<a href="#" onclick="javascript:toggle(this)">+</a>
|
||||
<div class="details">
|
||||
settings: {
|
||||
passwordSpent: bool,
|
||||
requireDestinationTag: bool,
|
||||
requireAuthorization: bool,
|
||||
disallowIncomingXRP: bool,
|
||||
disableMasterKey: bool,
|
||||
enableTransactionIDTracking: bool,
|
||||
noFreeze: bool,
|
||||
globalFreeze: bool,
|
||||
defaultRipple: bool,
|
||||
emailHash: hash128,
|
||||
walletLocator: hash256,
|
||||
walletSize: int,
|
||||
messageKey: string,
|
||||
domain: string,
|
||||
transferRate: float,
|
||||
signers: string,
|
||||
regularKey: address
|
||||
}
|
||||
|
||||
instructions: {
|
||||
sequence: int,
|
||||
fee: floatstr,
|
||||
maxFee: floatstr,
|
||||
maxLedgerVersion: int,
|
||||
maxLedgerVersionOffset: int
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="method">
|
||||
<a href="https://github.com/ripple/ripple-lib/blob/develop/src/api/common/schemas/sign.json">sign</a>(
|
||||
<a href="https://github.com/ripple/ripple-lib/blob/develop/src/api/common/schemas/tx.json">txJSON</a>,
|
||||
secret)
|
||||
</div>
|
||||
|
||||
<div class="method">
|
||||
submit(
|
||||
<a href="https://github.com/ripple/ripple-lib/blob/develop/src/api/common/schemas/blob.json">txBlob</a>
|
||||
)
|
||||
</div>
|
||||
|
||||
<div class="method">
|
||||
generateWallet()
|
||||
</div>
|
||||
|
||||
<a href="https://github.com/ripple/ripple-lib/blob/develop/src/api/common/errors.js">errors</a>
|
||||
</body>
|
||||
</html>
|
||||
3
docs/samples/README
Normal file
3
docs/samples/README
Normal file
@@ -0,0 +1,3 @@
|
||||
Usage:
|
||||
babel-node balances.js
|
||||
babel-node payment.js (requires setting address and secret in source file first)
|
||||
12
docs/samples/balances.js
Normal file
12
docs/samples/balances.js
Normal file
@@ -0,0 +1,12 @@
|
||||
'use strict';
|
||||
const RippleAPI = require('../../src').RippleAPI; // require('ripple-lib')
|
||||
|
||||
const api = new RippleAPI({servers: ['wss://s1.ripple.com:443']});
|
||||
const address = 'r3kmLJN5D28dHuH8vZNUZpMC43pEHpaocV';
|
||||
|
||||
api.connect().then(() => {
|
||||
api.getBalances(address).then(balances => {
|
||||
console.log(JSON.stringify(balances, null, 2));
|
||||
process.exit();
|
||||
});
|
||||
});
|
||||
41
docs/samples/payment.js
Normal file
41
docs/samples/payment.js
Normal file
@@ -0,0 +1,41 @@
|
||||
'use strict';
|
||||
const RippleAPI = require('../../src').RippleAPI; // require('ripple-lib')
|
||||
|
||||
const address = 'INSERT ADDRESS HERE';
|
||||
const secret = 'INSERT SECRET HERE';
|
||||
|
||||
const api = new RippleAPI({servers: ['wss://s1.ripple.com:443']});
|
||||
const instructions = {maxLedgerVersionOffset: 5};
|
||||
|
||||
const payment = {
|
||||
source: {
|
||||
address: address,
|
||||
amount: {
|
||||
value: '0.01',
|
||||
currency: 'XRP'
|
||||
}
|
||||
},
|
||||
destination: {
|
||||
address: 'rKmBGxocj9Abgy25J51Mk1iqFzW9aVF9Tc',
|
||||
amount: {
|
||||
value: '0.01',
|
||||
currency: 'XRP'
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
api.connect().then(() => {
|
||||
console.log('Connected...');
|
||||
api.preparePayment(address, payment, instructions).then(txJSON => {
|
||||
console.log('Payment transaction prepared...');
|
||||
const signedTransaction = api.sign(txJSON, secret).signedTransaction;
|
||||
console.log('Payment transaction signed...');
|
||||
api.submit(signedTransaction).then(response => {
|
||||
console.log(response);
|
||||
process.exit(0);
|
||||
}).catch(error => {
|
||||
console.log(error);
|
||||
process.exit(1);
|
||||
});
|
||||
});
|
||||
});
|
||||
102
npm-shrinkwrap.json
generated
102
npm-shrinkwrap.json
generated
@@ -1,20 +1,20 @@
|
||||
{
|
||||
"name": "ripple-lib",
|
||||
"version": "0.13.0-rc1",
|
||||
"version": "0.13.0-rc5",
|
||||
"npm-shrinkwrap-version": "5.4.0",
|
||||
"node-version": "v0.12.6",
|
||||
"node-version": "v0.12.7",
|
||||
"dependencies": {
|
||||
"async": {
|
||||
"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",
|
||||
"version": "5.8.20",
|
||||
"resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-5.8.20.tgz",
|
||||
"dependencies": {
|
||||
"core-js": {
|
||||
"version": "0.9.18",
|
||||
"resolved": "https://registry.npmjs.org/core-js/-/core-js-0.9.18.tgz"
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/core-js/-/core-js-1.0.1.tgz"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -22,10 +22,34 @@
|
||||
"version": "2.0.7",
|
||||
"resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-2.0.7.tgz"
|
||||
},
|
||||
"bn.js": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/bn.js/-/bn.js-3.1.1.tgz"
|
||||
},
|
||||
"es6-promisify": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/es6-promisify/-/es6-promisify-2.0.0.tgz",
|
||||
"dependencies": {
|
||||
"es6-promise": {
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-2.3.0.tgz"
|
||||
}
|
||||
}
|
||||
},
|
||||
"extend": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/extend/-/extend-1.2.1.tgz"
|
||||
},
|
||||
"hash.js": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.0.3.tgz",
|
||||
"dependencies": {
|
||||
"inherits": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz"
|
||||
}
|
||||
}
|
||||
},
|
||||
"https-proxy-agent": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-1.0.0.tgz",
|
||||
@@ -57,8 +81,8 @@
|
||||
}
|
||||
},
|
||||
"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",
|
||||
"version": "2.12.1",
|
||||
"resolved": "https://registry.npmjs.org/is-my-json-valid/-/is-my-json-valid-2.12.1.tgz",
|
||||
"dependencies": {
|
||||
"generate-function": {
|
||||
"version": "2.0.0",
|
||||
@@ -85,16 +109,52 @@
|
||||
}
|
||||
},
|
||||
"lodash": {
|
||||
"version": "3.10.0",
|
||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-3.10.0.tgz"
|
||||
"version": "3.10.1",
|
||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-3.10.1.tgz"
|
||||
},
|
||||
"lru-cache": {
|
||||
"version": "2.5.2",
|
||||
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.5.2.tgz"
|
||||
},
|
||||
"ripple-address-codec": {
|
||||
"version": "1.6.0",
|
||||
"resolved": "https://registry.npmjs.org/ripple-address-codec/-/ripple-address-codec-1.6.0.tgz",
|
||||
"dependencies": {
|
||||
"x-address-codec": {
|
||||
"version": "0.6.0",
|
||||
"resolved": "https://registry.npmjs.org/x-address-codec/-/x-address-codec-0.6.0.tgz",
|
||||
"dependencies": {
|
||||
"base-x": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/base-x/-/base-x-1.0.1.tgz"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"ripple-keypairs": {
|
||||
"version": "0.8.0",
|
||||
"resolved": "https://registry.npmjs.org/ripple-keypairs/-/ripple-keypairs-0.8.0.tgz",
|
||||
"dependencies": {
|
||||
"brorand": {
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/brorand/-/brorand-1.0.5.tgz"
|
||||
},
|
||||
"elliptic": {
|
||||
"version": "5.1.0",
|
||||
"resolved": "https://registry.npmjs.org/elliptic/-/elliptic-5.1.0.tgz",
|
||||
"dependencies": {
|
||||
"inherits": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"ripple-lib-transactionparser": {
|
||||
"version": "0.3.2",
|
||||
"resolved": "https://registry.npmjs.org/ripple-lib-transactionparser/-/ripple-lib-transactionparser-0.3.2.tgz",
|
||||
"version": "0.5.0",
|
||||
"resolved": "https://registry.npmjs.org/ripple-lib-transactionparser/-/ripple-lib-transactionparser-0.5.0.tgz",
|
||||
"dependencies": {
|
||||
"bignumber.js": {
|
||||
"version": "1.4.1",
|
||||
@@ -102,23 +162,9 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"ripple-wallet-generator": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/ripple-wallet-generator/-/ripple-wallet-generator-1.0.3.tgz"
|
||||
},
|
||||
"simple-asyncify": {
|
||||
"sjcl-codec": {
|
||||
"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"
|
||||
}
|
||||
}
|
||||
"resolved": "https://registry.npmjs.org/sjcl-codec/-/sjcl-codec-0.1.0.tgz"
|
||||
},
|
||||
"ws": {
|
||||
"version": "0.7.2",
|
||||
|
||||
32
package.json
32
package.json
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "ripple-lib",
|
||||
"version": "0.13.0-rc1",
|
||||
"version": "0.13.0-rc5",
|
||||
"license": "ISC",
|
||||
"description": "A JavaScript API for interacting with Ripple in Node.js and the browser",
|
||||
"files": [
|
||||
@@ -18,28 +18,31 @@
|
||||
"async": "~0.9.0",
|
||||
"babel-runtime": "^5.5.4",
|
||||
"bignumber.js": "^2.0.3",
|
||||
"bn.js": "^3.1.1",
|
||||
"es6-promisify": "^2.0.0",
|
||||
"extend": "~1.2.1",
|
||||
"hash.js": "^1.0.3",
|
||||
"https-proxy-agent": "^1.0.0",
|
||||
"is-my-json-valid": "^2.12.0",
|
||||
"lodash": "^3.1.0",
|
||||
"lru-cache": "~2.5.0",
|
||||
"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",
|
||||
"ripple-address-codec": "^1.6.0",
|
||||
"ripple-keypairs": "^0.8.0",
|
||||
"ripple-lib-transactionparser": "^0.5.0",
|
||||
"sjcl-codec": "0.1.0",
|
||||
"ws": "~0.7.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"assert-diff": "^1.0.1",
|
||||
"babel": "^5.5.4",
|
||||
"babel-core": "^5.5.4",
|
||||
"babel-eslint": "^3.1.23",
|
||||
"babel-loader": "^5.0.0",
|
||||
"babel": "^5.8.21",
|
||||
"babel-core": "^5.8.22",
|
||||
"babel-eslint": "^4.0.5",
|
||||
"babel-loader": "^5.3.2",
|
||||
"coveralls": "~2.10.0",
|
||||
"eslint": "^0.24.0",
|
||||
"eslint": "^1.2.0",
|
||||
"eslint-plugin-flowtype": "^1.0.0",
|
||||
"eventemitter2": "^0.4.14",
|
||||
"flow-bin": "^0.13.1",
|
||||
"flow-bin": "^0.14",
|
||||
"gulp": "~3.8.10",
|
||||
"gulp-bump": "~0.1.13",
|
||||
"gulp-rename": "~1.2.0",
|
||||
@@ -53,12 +56,13 @@
|
||||
"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/",
|
||||
"compile": "babel -D --optional runtime -d dist/npm/ src/",
|
||||
"watch": "babel -w -D --optional runtime -d dist/npm/ src/",
|
||||
"compile-with-source-maps": "babel -D --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/",
|
||||
"lint": "if ! [ -f eslintrc ]; then curl -o eslintrc 'https://raw.githubusercontent.com/ripple/javascript-style-guide/es6/eslintrc'; echo 'parser: babel-eslint' >> eslintrc; fi; eslint -c eslintrc src/",
|
||||
"perf": "./scripts/perf_test.sh"
|
||||
},
|
||||
"repository": {
|
||||
|
||||
@@ -2,12 +2,13 @@
|
||||
'use strict';
|
||||
|
||||
var fs = require('fs');
|
||||
var Amount = require('../dist/npm').Amount;
|
||||
var Ledger = require('../dist/npm').Ledger;
|
||||
var ripple = require('../dist/npm')._DEPRECATED;
|
||||
var Amount = ripple.Amount;
|
||||
var Ledger = ripple.Ledger;
|
||||
|
||||
function parse_options(from, flags) {
|
||||
var argv = from.slice(),
|
||||
opts_ = {argv: argv};
|
||||
var argv = from.slice();
|
||||
var opts_ = {argv: argv};
|
||||
|
||||
flags.forEach(function(f) {
|
||||
// Do we have the flag?
|
||||
|
||||
@@ -33,7 +33,7 @@ const AccountFields = {
|
||||
WalletSize: {name: 'walletSize', defaults: 0},
|
||||
MessageKey: {name: 'messageKey'},
|
||||
Domain: {name: 'domain', encoding: 'hex'},
|
||||
TransferRate: {name: 'transferRate', defaults: 0},
|
||||
TransferRate: {name: 'transferRate', defaults: 0, shift: 9},
|
||||
Signers: {name: 'signers'}
|
||||
};
|
||||
|
||||
|
||||
@@ -9,7 +9,12 @@ module.exports = {
|
||||
dropsToXrp: utils.dropsToXrp,
|
||||
xrpToDrops: utils.xrpToDrops,
|
||||
toRippledAmount: utils.toRippledAmount,
|
||||
wrapCatch: utils.wrapCatch,
|
||||
generateAddress: utils.generateAddress,
|
||||
composeAsync: utils.composeAsync,
|
||||
convertExceptions: utils.convertExceptions
|
||||
wrapCatch: utils.wrapCatch,
|
||||
convertExceptions: utils.convertExceptions,
|
||||
convertKeysFromSnakeCaseToCamelCase:
|
||||
utils.convertKeysFromSnakeCaseToCamelCase,
|
||||
promisify: utils.promisify,
|
||||
isValidAddress: require('./schema-validator').isValidAddress
|
||||
};
|
||||
|
||||
@@ -1,22 +1,27 @@
|
||||
/* @flow */
|
||||
'use strict';
|
||||
|
||||
const _ = require('lodash');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const assert = require('assert');
|
||||
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 isValidAddress(address: string): boolean {
|
||||
return typeof address === 'string' && address.length > 0 &&
|
||||
address[0] === 'r' &&
|
||||
core.UInt160.is_valid(address);
|
||||
}
|
||||
|
||||
function isValidLedgerHash(ledgerHash) {
|
||||
return core.UInt256.is_valid(ledgerHash);
|
||||
}
|
||||
|
||||
function loadSchema(filepath) {
|
||||
function loadSchema(filepath: string): {} {
|
||||
try {
|
||||
return JSON.parse(fs.readFileSync(filepath, 'utf8'));
|
||||
} catch (e) {
|
||||
@@ -31,6 +36,9 @@ function endsWith(str, suffix) {
|
||||
function loadSchemas(dir) {
|
||||
const filenames = fs.readdirSync(dir).filter(name => endsWith(name, '.json'));
|
||||
const schemas = filenames.map(name => loadSchema(path.join(dir, name)));
|
||||
const titles = _.map(schemas, schema => schema.title);
|
||||
const duplicates = _.keys(_.pick(_.countBy(titles), count => count > 1));
|
||||
assert(duplicates.length === 0, 'Duplicate schemas for: ' + duplicates);
|
||||
return _.indexBy(schemas, 'title');
|
||||
}
|
||||
|
||||
@@ -43,7 +51,7 @@ function formatSchemaErrors(errors) {
|
||||
return errors.map(formatSchemaError).join(', ');
|
||||
}
|
||||
|
||||
function schemaValidate(schemaName, object) {
|
||||
function schemaValidate(schemaName: string, object: any): void {
|
||||
const formats = {address: isValidAddress,
|
||||
ledgerHash: isValidLedgerHash};
|
||||
const options = {schemas: SCHEMAS, formats: formats,
|
||||
@@ -60,4 +68,9 @@ function schemaValidate(schemaName, object) {
|
||||
}
|
||||
|
||||
SCHEMAS = loadSchemas(path.join(__dirname, './schemas'));
|
||||
module.exports = schemaValidate;
|
||||
module.exports = {
|
||||
schemaValidate: schemaValidate,
|
||||
isValidAddress: isValidAddress,
|
||||
loadSchema: loadSchema,
|
||||
SCHEMAS: SCHEMAS
|
||||
};
|
||||
|
||||
@@ -4,7 +4,16 @@
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"address": {"$ref": "address"},
|
||||
"amount": {"$ref": "amount"},
|
||||
"amount": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"currency": {"$ref": "currency"},
|
||||
"counterparty": {"$ref": "address"},
|
||||
"value": {"$ref": "value"}
|
||||
},
|
||||
"required": ["currency", "value"],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"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"
|
||||
|
||||
44
src/api/common/schemas/balance.json
Normal file
44
src/api/common/schemas/balance.json
Normal file
@@ -0,0 +1,44 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-04/schema#",
|
||||
"title": "balance",
|
||||
"description": "Balance amount",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"value": {
|
||||
"description": "The balance on the trustline",
|
||||
"$ref": "signedValue"
|
||||
},
|
||||
"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", "value"],
|
||||
"oneOf": [
|
||||
{
|
||||
"properties": {
|
||||
"currency": {
|
||||
"not": {
|
||||
"enum": ["XRP"]
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": ["counterparty"]
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"currency": {
|
||||
"enum": ["XRP"]
|
||||
}
|
||||
},
|
||||
"not": {
|
||||
"required": ["counterparty"]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
21
src/api/common/schemas/get-account-info.json
Normal file
21
src/api/common/schemas/get-account-info.json
Normal file
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-04/schema#",
|
||||
"title": "getAccountInfo",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"sequence": {"$ref": "sequence"},
|
||||
"xrpBalance": {"$ref": "value"},
|
||||
"ownerCount": {"type": "integer", "minimum": 0},
|
||||
"previousInitiatedTransactionID": {"$ref": "hash256"},
|
||||
"previousAffectingTransactionID": {"$ref": "hash256"},
|
||||
"previousAffectingTransactionLedgerVersion": {"$ref": "ledgerVersion"}
|
||||
},
|
||||
"required": [
|
||||
"sequence",
|
||||
"xrpBalance",
|
||||
"ownerCount",
|
||||
"previousAffectingTransactionID",
|
||||
"previousAffectingTransactionLedgerVersion"
|
||||
],
|
||||
"additionalProperties": false
|
||||
}
|
||||
6
src/api/common/schemas/get-balances.json
Normal file
6
src/api/common/schemas/get-balances.json
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-04/schema#",
|
||||
"title": "getBalances",
|
||||
"type": "array",
|
||||
"items": {"$ref": "balance"}
|
||||
}
|
||||
39
src/api/common/schemas/get-ledger.json
Normal file
39
src/api/common/schemas/get-ledger.json
Normal file
@@ -0,0 +1,39 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-04/schema#",
|
||||
"title": "getLedger",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"accepted": {"type": "boolean"},
|
||||
"closed": {"type": "boolean"},
|
||||
"stateHash": {"$ref": "hash256"},
|
||||
"closeTime": {"type": "integer", "minimum": 0},
|
||||
"closeTimeResolution": {"type": "integer", "minimum": 1},
|
||||
"closeFlags": {"type": "integer", "minimum": 0},
|
||||
"ledgerHash": {"$ref": "hash256"},
|
||||
"ledgerVersion": {"$ref": "ledgerVersion"},
|
||||
"parentLedgerHash": {"$ref": "hash256"},
|
||||
"parentCloseTime": {"type": "integer", "minimum": 0},
|
||||
"totalDrops": {"$ref": "value"},
|
||||
"transactionHash": {"$ref": "hash256"},
|
||||
"transactions": {"type": "array", "items": {"type": "object"}},
|
||||
"rawTransactions": {"type": "string"},
|
||||
"transactionHashes": {"type": "array", "items": {"$ref": "hash256"}},
|
||||
"rawState": {"type": "string"},
|
||||
"stateHashes": {"type": "array", "items": {"$ref": "hash256"}}
|
||||
},
|
||||
"required": [
|
||||
"accepted",
|
||||
"closed",
|
||||
"stateHash",
|
||||
"closeTime",
|
||||
"closeTimeResolution",
|
||||
"closeFlags",
|
||||
"ledgerHash",
|
||||
"ledgerVersion",
|
||||
"parentLedgerHash",
|
||||
"parentCloseTime",
|
||||
"totalDrops",
|
||||
"transactionHash"
|
||||
],
|
||||
"additionalProperties": false
|
||||
}
|
||||
11
src/api/common/schemas/get-orderbook.json
Normal file
11
src/api/common/schemas/get-orderbook.json
Normal file
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-04/schema#",
|
||||
"title": "getOrderbook",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"bids": {"$ref": "orderbookOrders"},
|
||||
"asks": {"$ref": "orderbookOrders"}
|
||||
},
|
||||
"required": ["bids", "asks"],
|
||||
"additionalProperties": false
|
||||
}
|
||||
23
src/api/common/schemas/get-orders.json
Normal file
23
src/api/common/schemas/get-orders.json
Normal file
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-04/schema#",
|
||||
"title": "getOrders",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"specification": {"$ref": "order"},
|
||||
"properties": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"maker": {"$ref": "address"},
|
||||
"sequence": {"$ref": "sequence"},
|
||||
"makerExchangeRate": {"$ref": "value"}
|
||||
},
|
||||
"required": ["maker", "sequence", "makerExchangeRate"],
|
||||
"addtionalProperties": false
|
||||
}
|
||||
},
|
||||
"required": ["specification", "properties"],
|
||||
"additionalProperties": false
|
||||
}
|
||||
}
|
||||
15
src/api/common/schemas/get-paths.json
Normal file
15
src/api/common/schemas/get-paths.json
Normal file
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-04/schema#",
|
||||
"title": "getPaths",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"source": {"$ref": "adjustment"},
|
||||
"destination": {"$ref": "adjustment"},
|
||||
"paths": {"type": "string"}
|
||||
},
|
||||
"required": ["source", "destination", "paths"],
|
||||
"additionalProperties": false
|
||||
}
|
||||
}
|
||||
49
src/api/common/schemas/get-server-info.json
Normal file
49
src/api/common/schemas/get-server-info.json
Normal file
@@ -0,0 +1,49 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-04/schema#",
|
||||
"title": "getServerInfo",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"buildVersion": {"type": "string"},
|
||||
"completeLedgers": {"type": "string", "pattern": "[0-9,-]+"},
|
||||
"hostid": {"type": "string"},
|
||||
"ioLatencyMs": {"type": "number"},
|
||||
"load": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"job_types": {
|
||||
"type": "array",
|
||||
"items": {"type": "object"}
|
||||
},
|
||||
"threads": {"type": "number"}
|
||||
}
|
||||
},
|
||||
"lastClose": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"convergeTimeS": {"type": "number"},
|
||||
"proposers": {"type": "integer", "minimum": 0}
|
||||
}
|
||||
},
|
||||
"loadFactor": {"type": "number"},
|
||||
"peers": {"type": "integer", "minimum": 0},
|
||||
"pubkeyNode": {"type": "string"},
|
||||
"pubkeyValidator": {"type": "string"},
|
||||
"serverState": {
|
||||
"type": "string",
|
||||
"enum": ["disconnected", "connected", "syncing", "tracking", "full", "validating", "proposing"]
|
||||
},
|
||||
"validatedLedger": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"age": {"type": "integer", "minimum": 0},
|
||||
"baseFeeXrp": {"type": "number"},
|
||||
"hash": {"$ref": "hash256"},
|
||||
"reserveBaseXrp": {"type": "integer", "minimum": 0},
|
||||
"reserveIncXrp": {"type": "integer", "minimum": 0},
|
||||
"seq": {"type": "integer", "minimum": 0}
|
||||
}
|
||||
},
|
||||
"validationQuorum": {"type": "number"}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
40
src/api/common/schemas/get-settings.json
Normal file
40
src/api/common/schemas/get-settings.json
Normal file
@@ -0,0 +1,40 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-04/schema#",
|
||||
"title": "getSettings",
|
||||
"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": {
|
||||
"oneOf": [
|
||||
{"type": "null"},
|
||||
{"$ref": "hash128"}
|
||||
]
|
||||
},
|
||||
"walletLocator": {
|
||||
"oneOf": [
|
||||
{"type": "null"},
|
||||
{"$ref": "hash256"}
|
||||
]
|
||||
},
|
||||
"walletSize": {"type": ["integer", "null"]},
|
||||
"messageKey": {"type": "string"},
|
||||
"domain": {"type": "string"},
|
||||
"transferRate": {
|
||||
"oneOf": [
|
||||
{"type": "null"},
|
||||
{"type": "number", "minimum": 1, "maximum": 4.294967295}
|
||||
]
|
||||
},
|
||||
"signers": {"type": "string"},
|
||||
"regularKey": {"$ref": "address"}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
11
src/api/common/schemas/get-transaction.json
Normal file
11
src/api/common/schemas/get-transaction.json
Normal file
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-04/schema#",
|
||||
"title": "getTransaction",
|
||||
"oneOf": [
|
||||
{"$ref": "paymentTransaction"},
|
||||
{"$ref": "orderTransaction"},
|
||||
{"$ref": "orderCancellationTransaction"},
|
||||
{"$ref": "trustlineTransaction"},
|
||||
{"$ref": "settingsTransaction"}
|
||||
]
|
||||
}
|
||||
6
src/api/common/schemas/get-transactions.json
Normal file
6
src/api/common/schemas/get-transactions.json
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-04/schema#",
|
||||
"title": "getTransactions",
|
||||
"type": "array",
|
||||
"items": {"$ref": "getTransaction"}
|
||||
}
|
||||
29
src/api/common/schemas/get-trustlines.json
Normal file
29
src/api/common/schemas/get-trustlines.json
Normal file
@@ -0,0 +1,29 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-04/schema#",
|
||||
"title": "getTrustlines",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"properties": {
|
||||
"specification": {"$ref": "trustline"},
|
||||
"counterparty": {
|
||||
"properties": {
|
||||
"limit": {"$ref": "value"},
|
||||
"ripplingDisabled": {"type": "boolean"},
|
||||
"frozen": {"type": "boolean"},
|
||||
"authorized": {"type": "boolean"}
|
||||
},
|
||||
"required": ["limit"],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"state": {
|
||||
"properties": {
|
||||
"balance": {"$ref": "value"}
|
||||
},
|
||||
"required": ["balance"],
|
||||
"additionalProperties": false
|
||||
}
|
||||
},
|
||||
"required": ["specification", "counterparty", "state"],
|
||||
"additionalProperties": false
|
||||
}
|
||||
}
|
||||
13
src/api/common/schemas/ledger-options.json
Normal file
13
src/api/common/schemas/ledger-options.json
Normal file
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-04/schema#",
|
||||
"title": "ledger-options",
|
||||
"description": "Options for getLedger",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"ledgerVersion": {"$ref": "ledgerVersion"},
|
||||
"includeAllData": {"type": "boolean"},
|
||||
"includeTransactions": {"type": "boolean"},
|
||||
"includeState": {"type": "boolean"}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
24
src/api/common/schemas/max-adjustment.json
Normal file
24
src/api/common/schemas/max-adjustment.json
Normal file
@@ -0,0 +1,24 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-04/schema#",
|
||||
"title": "maxAdjustment",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"address": {"$ref": "address"},
|
||||
"maxAmount": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"currency": {"$ref": "currency"},
|
||||
"counterparty": {"$ref": "address"},
|
||||
"value": {"$ref": "value"}
|
||||
},
|
||||
"required": ["currency", "value"],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"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", "maxAmount"],
|
||||
"additionalProperties": false
|
||||
}
|
||||
15
src/api/common/schemas/order-cancellation-transaction.json
Normal file
15
src/api/common/schemas/order-cancellation-transaction.json
Normal file
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-04/schema#",
|
||||
"title": "orderCancellationTransaction",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"type": {"enum": ["orderCancellation"]},
|
||||
"specification": {"$ref": "orderCancellation"},
|
||||
"outcome": {"$ref": "outcome"},
|
||||
"id": {"$ref": "hash256"},
|
||||
"address": {"$ref": "address"},
|
||||
"sequence": {"$ref": "sequence"}
|
||||
},
|
||||
"required": ["type", "id", "address", "sequence", "specification", "outcome"],
|
||||
"additionalProperties": false
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-04/schema#",
|
||||
"title": "cancellation",
|
||||
"title": "orderCancellation",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"orderSequence": {"$ref": "sequence"}
|
||||
17
src/api/common/schemas/order-change.json
Normal file
17
src/api/common/schemas/order-change.json
Normal file
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-04/schema#",
|
||||
"title": "orderChange",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"direction": {
|
||||
"type": "string",
|
||||
"enum": ["buy", "sell"]
|
||||
},
|
||||
"quantity": {"$ref": "balance"},
|
||||
"totalPrice": {"$ref": "balance"},
|
||||
"sequence": {"$ref": "sequence"},
|
||||
"status": {"enum": ["created", "open", "closed", "canceled"]}
|
||||
},
|
||||
"required": ["direction", "quantity", "totalPrice", "sequence", "status"],
|
||||
"additionalProperties": false
|
||||
}
|
||||
15
src/api/common/schemas/order-transaction.json
Normal file
15
src/api/common/schemas/order-transaction.json
Normal file
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-04/schema#",
|
||||
"title": "orderTransaction",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"type": {"enum": ["order"]},
|
||||
"specification": {"$ref": "order"},
|
||||
"outcome": {"$ref": "outcome"},
|
||||
"id": {"$ref": "hash256"},
|
||||
"address": {"$ref": "address"},
|
||||
"sequence": {"$ref": "sequence"}
|
||||
},
|
||||
"required": ["type", "id", "address", "sequence", "specification", "outcome"],
|
||||
"additionalProperties": false
|
||||
}
|
||||
32
src/api/common/schemas/orderbook-orders.json
Normal file
32
src/api/common/schemas/orderbook-orders.json
Normal file
@@ -0,0 +1,32 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-04/schema#",
|
||||
"title": "orderbookOrders",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"specification": {"$ref": "order"},
|
||||
"properties": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"maker": {"$ref": "address"},
|
||||
"sequence": {"$ref": "sequence"},
|
||||
"makerExchangeRate": {"$ref": "value"}
|
||||
},
|
||||
"required": ["maker", "sequence", "makerExchangeRate"],
|
||||
"addtionalProperties": false
|
||||
},
|
||||
"state": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"fundedAmount": {"$ref": "amount"},
|
||||
"priceOfFundedAmount": {"$ref": "amount"}
|
||||
},
|
||||
"required": ["fundedAmount", "priceOfFundedAmount"],
|
||||
"additionalProperties": false
|
||||
}
|
||||
},
|
||||
"required": ["specification", "properties"],
|
||||
"additionalProperties": false
|
||||
}
|
||||
}
|
||||
31
src/api/common/schemas/outcome.json
Normal file
31
src/api/common/schemas/outcome.json
Normal file
@@ -0,0 +1,31 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-04/schema#",
|
||||
"title": "outcome",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"result": {"type": "string"},
|
||||
"timestamp": {"type": "string"},
|
||||
"fee": {"$ref": "value"},
|
||||
"balanceChanges": {
|
||||
"type": "object",
|
||||
"description": "Key is the ripple address; value is an array of changes",
|
||||
"additionalProperties": {
|
||||
"type": "array",
|
||||
"items": {"$ref": "balance"}
|
||||
}
|
||||
},
|
||||
"orderbookChanges": {
|
||||
"type": "object",
|
||||
"description": "Key is the maker's ripple address; value is an array of changes",
|
||||
"additionalProperties": {
|
||||
"type": "array",
|
||||
"items": {"$ref": "orderChange"}
|
||||
}
|
||||
},
|
||||
"ledgerVersion": {"$ref": "ledgerVersion"},
|
||||
"indexInLedger": {"type": "integer", "minimum": 0}
|
||||
},
|
||||
"required": ["result", "fee", "balanceChanges",
|
||||
"orderbookChanges", "ledgerVersion", "indexInLedger"],
|
||||
"additionalProperties": false
|
||||
}
|
||||
15
src/api/common/schemas/payment-transaction.json
Normal file
15
src/api/common/schemas/payment-transaction.json
Normal file
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-04/schema#",
|
||||
"title": "paymentTransaction",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"type": {"enum": ["payment"]},
|
||||
"specification": {"$ref": "payment"},
|
||||
"outcome": {"$ref": "outcome"},
|
||||
"id": {"$ref": "hash256"},
|
||||
"address": {"$ref": "address"},
|
||||
"sequence": {"$ref": "sequence"}
|
||||
},
|
||||
"required": ["type", "id", "address", "sequence", "specification", "outcome"],
|
||||
"additionalProperties": false
|
||||
}
|
||||
@@ -3,13 +3,9 @@
|
||||
"title": "payment",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"source": {"$ref": "adjustment"},
|
||||
"source": {"$ref": "maxAdjustment"},
|
||||
"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": {
|
||||
|
||||
@@ -1,8 +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
|
||||
"description": "Ratio for incoming/outgoing transit fees.",
|
||||
"type": "number",
|
||||
"minimum": 0.000000001,
|
||||
"maximum": 4.294967295
|
||||
}
|
||||
|
||||
15
src/api/common/schemas/settings-transaction.json
Normal file
15
src/api/common/schemas/settings-transaction.json
Normal file
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-04/schema#",
|
||||
"title": "settingsTransaction",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"type": {"enum": ["settings"]},
|
||||
"specification": {"$ref": "getSettings"},
|
||||
"outcome": {"$ref": "outcome"},
|
||||
"id": {"$ref": "hash256"},
|
||||
"address": {"$ref": "address"},
|
||||
"sequence": {"$ref": "sequence"}
|
||||
},
|
||||
"required": ["type", "id", "address", "sequence", "specification"],
|
||||
"additionalProperties": false
|
||||
}
|
||||
@@ -1,27 +1,13 @@
|
||||
{
|
||||
"$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
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "getSettings"
|
||||
},
|
||||
{
|
||||
"minProperties": 1,
|
||||
"maxProperties": 1
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
14
src/api/common/schemas/sign.json
Normal file
14
src/api/common/schemas/sign.json
Normal file
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-04/schema#",
|
||||
"title": "sign",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"signedTransaction": {
|
||||
"type": "string",
|
||||
"pattern": "^[A-F0-9]+$"
|
||||
},
|
||||
"id": {"$ref": "hash256"}
|
||||
},
|
||||
"required": ["signedTransaction", "id"],
|
||||
"additionalProperties": false
|
||||
}
|
||||
7
src/api/common/schemas/signed-value.json
Normal file
7
src/api/common/schemas/signed-value.json
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-04/schema#",
|
||||
"title": "signedValue",
|
||||
"description": "A string representation of a floating point number",
|
||||
"type": "string",
|
||||
"pattern": "^[-]?[0-9]*[.]?[0-9]+([eE][-+]?[0-9]+)?$"
|
||||
}
|
||||
15
src/api/common/schemas/submit.json
Normal file
15
src/api/common/schemas/submit.json
Normal file
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-04/schema#",
|
||||
"title": "submit",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"success": {"type": "boolean"},
|
||||
"engineResult": {"type": "string"},
|
||||
"engineResultCode": {"type": "integer"},
|
||||
"engineResultMessage": {"type": "string"},
|
||||
"txBlob": {"type": "string"},
|
||||
"txJson": {"type": "object"}
|
||||
},
|
||||
"required": ["success", "engineResult", "engineResultCode"],
|
||||
"additionalProperties": false
|
||||
}
|
||||
15
src/api/common/schemas/trustline-transaction.json
Normal file
15
src/api/common/schemas/trustline-transaction.json
Normal file
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-04/schema#",
|
||||
"title": "trustlineTransaction",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"type": {"enum": ["trustline"]},
|
||||
"specification": {"$ref": "trustline"},
|
||||
"outcome": {"$ref": "outcome"},
|
||||
"id": {"$ref": "hash256"},
|
||||
"address": {"$ref": "address"},
|
||||
"sequence": {"$ref": "sequence"}
|
||||
},
|
||||
"required": ["type", "id", "address", "sequence", "specification", "outcome"],
|
||||
"additionalProperties": false
|
||||
}
|
||||
@@ -8,7 +8,7 @@
|
||||
"limit": {"$ref": "value"},
|
||||
"qualityIn": {"$ref": "quality"},
|
||||
"qualityOut": {"$ref": "quality"},
|
||||
"allowRippling": {"type": "boolean"},
|
||||
"ripplingDisabled": {"type": "boolean"},
|
||||
"authorized": {"type": "boolean"},
|
||||
"frozen": {"type": "boolean"}
|
||||
},
|
||||
|
||||
@@ -1,17 +1,23 @@
|
||||
/* @flow */
|
||||
'use strict';
|
||||
const _ = require('lodash');
|
||||
const BigNumber = require('bignumber.js');
|
||||
const core = require('../../core');
|
||||
const errors = require('./errors');
|
||||
const es6promisify = require('es6-promisify');
|
||||
const keypairs = require('ripple-keypairs');
|
||||
|
||||
function dropsToXrp(drops) {
|
||||
type Amount = {currency: string, issuer: string, value: string}
|
||||
|
||||
function dropsToXrp(drops: string): string {
|
||||
return (new BigNumber(drops)).dividedBy(1000000.0).toString();
|
||||
}
|
||||
|
||||
function xrpToDrops(xrp) {
|
||||
function xrpToDrops(xrp: string): string {
|
||||
return (new BigNumber(xrp)).times(1000000.0).floor().toString();
|
||||
}
|
||||
|
||||
function toRippledAmount(amount) {
|
||||
function toRippledAmount(amount: Amount): string|Amount {
|
||||
if (amount.currency === 'XRP') {
|
||||
return xrpToDrops(amount.value);
|
||||
}
|
||||
@@ -22,7 +28,14 @@ function toRippledAmount(amount) {
|
||||
};
|
||||
}
|
||||
|
||||
function wrapCatch(asyncFunction: () => void): () => void {
|
||||
function generateAddress(options?: Object): Object {
|
||||
const {accountID, seed} = keypairs.generateWallet(options);
|
||||
return {secret: seed, address: accountID};
|
||||
}
|
||||
|
||||
type AsyncFunction = (...x: any) => void
|
||||
|
||||
function wrapCatch(asyncFunction: AsyncFunction): AsyncFunction {
|
||||
return function() {
|
||||
try {
|
||||
asyncFunction.apply(this, arguments);
|
||||
@@ -33,7 +46,10 @@ function wrapCatch(asyncFunction: () => void): () => void {
|
||||
};
|
||||
}
|
||||
|
||||
function composeAsync(wrapper, callback) {
|
||||
type Callback = (err: any, data: any) => void
|
||||
type Wrapper = (data: any) => any
|
||||
|
||||
function composeAsync(wrapper: Wrapper, callback: Callback): Callback {
|
||||
return function(error, data) {
|
||||
if (error) {
|
||||
callback(error);
|
||||
@@ -50,7 +66,7 @@ function composeAsync(wrapper, callback) {
|
||||
};
|
||||
}
|
||||
|
||||
function convertExceptions(f) {
|
||||
function convertExceptions<T>(f: () => T): () => T {
|
||||
return function() {
|
||||
try {
|
||||
return f.apply(this, arguments);
|
||||
@@ -60,12 +76,35 @@ function convertExceptions(f) {
|
||||
};
|
||||
}
|
||||
|
||||
const FINDSNAKE = /([a-zA-Z]_[a-zA-Z])/g;
|
||||
function convertKeysFromSnakeCaseToCamelCase(obj: any): any {
|
||||
if (typeof obj === 'object') {
|
||||
let newKey;
|
||||
return _.reduce(obj, (result, value, key) => {
|
||||
newKey = key;
|
||||
if (FINDSNAKE.test(key)) {
|
||||
newKey = key.replace(FINDSNAKE, r => r[0] + r[2].toUpperCase());
|
||||
}
|
||||
result[newKey] = convertKeysFromSnakeCaseToCamelCase(value);
|
||||
return result;
|
||||
}, {});
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
||||
function promisify(asyncFunction: AsyncFunction): Function {
|
||||
return es6promisify(wrapCatch(asyncFunction));
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
core,
|
||||
dropsToXrp,
|
||||
xrpToDrops,
|
||||
toRippledAmount,
|
||||
wrapCatch,
|
||||
generateAddress,
|
||||
composeAsync,
|
||||
convertExceptions
|
||||
wrapCatch,
|
||||
convertExceptions,
|
||||
convertKeysFromSnakeCaseToCamelCase,
|
||||
promisify
|
||||
};
|
||||
|
||||
@@ -1,26 +1,38 @@
|
||||
/* @flow */
|
||||
'use strict';
|
||||
const _ = require('lodash');
|
||||
const core = require('./utils').core;
|
||||
const ValidationError = require('./errors').ValidationError;
|
||||
const schemaValidate = require('./schema-validator');
|
||||
const schemaValidate = require('./schema-validator').schemaValidate;
|
||||
|
||||
function error(text) {
|
||||
return new ValidationError(text);
|
||||
}
|
||||
|
||||
function validateAddressAndSecret(obj) {
|
||||
function validateAddressAndSecret(obj: {address: string, secret: string}
|
||||
): void {
|
||||
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');
|
||||
if (!core.Seed.from_json(secret).is_valid()) {
|
||||
throw error('secret is invalid');
|
||||
}
|
||||
}
|
||||
|
||||
function validateSecret(secret: string): void {
|
||||
if (!secret) {
|
||||
throw error('Parameter missing: secret');
|
||||
}
|
||||
if (typeof secret !== 'string' || secret[0] !== 's') {
|
||||
throw error('Invalid parameter');
|
||||
}
|
||||
|
||||
const seed = new core.Seed().parse_base58(secret);
|
||||
if (!seed.is_valid()) {
|
||||
throw error('invalid seed');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,8 +53,10 @@ function validateOptions(schema, options) {
|
||||
module.exports = {
|
||||
address: _.partial(schemaValidate, 'address'),
|
||||
addressAndSecret: validateAddressAndSecret,
|
||||
secret: validateSecret,
|
||||
currency: _.partial(schemaValidate, 'currency'),
|
||||
identifier: _.partial(schemaValidate, 'hash256'),
|
||||
ledgerVersion: _.partial(schemaValidate, 'ledgerVersion'),
|
||||
sequence: _.partial(schemaValidate, 'sequence'),
|
||||
order: _.partial(schemaValidate, 'order'),
|
||||
orderbook: _.partial(schemaValidate, 'orderbook'),
|
||||
@@ -60,6 +74,7 @@ module.exports = {
|
||||
getOrdersOptions: _.partial(validateOptions, 'orders-options'),
|
||||
getOrderbookOptions: _.partial(validateOptions, 'orders-options'),
|
||||
getTransactionOptions: _.partial(validateOptions, 'transaction-options'),
|
||||
getLedgerOptions: _.partial(validateOptions, 'ledger-options'),
|
||||
options: _.partial(validateOptions, 'options'),
|
||||
instructions: _.partial(schemaValidate, 'instructions')
|
||||
};
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
'use strict';
|
||||
const _ = require('lodash');
|
||||
const core = require('./common').core;
|
||||
const common = require('./common');
|
||||
const server = require('./server/server');
|
||||
const connect = server.connect;
|
||||
const disconnect = server.disconnect;
|
||||
@@ -28,11 +28,14 @@ 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);
|
||||
const generateAddress = convertExceptions(common.generateAddress);
|
||||
const computeLedgerHash = require('./offline/ledgerhash');
|
||||
const getLedger = require('./ledger/ledger');
|
||||
const isValidAddress = common.isValidAddress;
|
||||
|
||||
function RippleAPI(options: {}) {
|
||||
const _options = _.assign({}, options, {automatic_resubmission: false});
|
||||
this.remote = new core.Remote(_options);
|
||||
this.remote = new common.core.Remote(_options);
|
||||
}
|
||||
|
||||
RippleAPI.prototype = {
|
||||
@@ -52,6 +55,7 @@ RippleAPI.prototype = {
|
||||
getOrderbook,
|
||||
getSettings,
|
||||
getAccountInfo,
|
||||
getLedger,
|
||||
|
||||
preparePayment,
|
||||
prepareTrustline,
|
||||
@@ -61,8 +65,17 @@ RippleAPI.prototype = {
|
||||
sign,
|
||||
submit,
|
||||
|
||||
generateWallet,
|
||||
computeLedgerHash,
|
||||
isValidAddress,
|
||||
generateAddress,
|
||||
errors
|
||||
};
|
||||
|
||||
// these are exposed only for use by unit tests; they are not part of the API
|
||||
RippleAPI._PRIVATE = {
|
||||
common: common,
|
||||
ledgerUtils: require('./ledger/utils'),
|
||||
schemaValidator: require('./common/schema-validator')
|
||||
};
|
||||
|
||||
module.exports = RippleAPI;
|
||||
|
||||
@@ -6,7 +6,43 @@ const removeUndefined = require('./parse/utils').removeUndefined;
|
||||
const validate = utils.common.validate;
|
||||
const composeAsync = utils.common.composeAsync;
|
||||
|
||||
function formatAccountInfo(response) {
|
||||
type AccountData = {
|
||||
Sequence: number,
|
||||
Account: string,
|
||||
Balance: string,
|
||||
Flags: number,
|
||||
LedgerEntryType: string,
|
||||
OwnerCount: number,
|
||||
PreviousTxnID: string,
|
||||
AccountTxnID?: string,
|
||||
PreviousTxnLgrSeq: number,
|
||||
index: string
|
||||
}
|
||||
|
||||
type AccountDataResponse = {
|
||||
account_data: AccountData,
|
||||
ledger_current_index?: number,
|
||||
ledger_hash?: string,
|
||||
ledger_index: number,
|
||||
validated: boolean
|
||||
}
|
||||
|
||||
type AccountInfoOptions = {
|
||||
ledgerVersion?: number
|
||||
}
|
||||
|
||||
type AccountInfoCallback = (err: any, data: AccountInfoResponse) => void
|
||||
|
||||
type AccountInfoResponse = {
|
||||
sequence: number,
|
||||
xrpBalance: string,
|
||||
ownerCount: number,
|
||||
previousInitiatedTransactionID: string,
|
||||
previousAffectingTransactionID: string,
|
||||
previousAffectingTransactionLedgerVersion: number
|
||||
}
|
||||
|
||||
function formatAccountInfo(response: AccountDataResponse) {
|
||||
const data = response.account_data;
|
||||
return removeUndefined({
|
||||
sequence: data.Sequence,
|
||||
@@ -18,17 +54,24 @@ function formatAccountInfo(response) {
|
||||
});
|
||||
}
|
||||
|
||||
function getAccountInfo(account, options, callback) {
|
||||
function getAccountInfoAsync(account: string, options: AccountInfoOptions,
|
||||
callback: AccountInfoCallback
|
||||
) {
|
||||
validate.address(account);
|
||||
validate.getAccountInfoOptions(options);
|
||||
|
||||
const request = {
|
||||
account: account,
|
||||
ledger: options.ledgerVersion
|
||||
ledger: options.ledgerVersion || 'validated'
|
||||
};
|
||||
|
||||
this.remote.requestAccountInfo(request,
|
||||
composeAsync(formatAccountInfo, callback));
|
||||
}
|
||||
|
||||
module.exports = utils.wrapCatch(getAccountInfo);
|
||||
function getAccountInfo(account: string, options: AccountInfoOptions={}
|
||||
): Promise<AccountInfoResponse> {
|
||||
return utils.promisify(getAccountInfoAsync).call(this, account, options);
|
||||
}
|
||||
|
||||
module.exports = getAccountInfo;
|
||||
|
||||
@@ -24,7 +24,13 @@ function formatBalances(balances) {
|
||||
balances.trustlines.map(getTrustlineBalanceAmount));
|
||||
}
|
||||
|
||||
function getBalances(account, options, callback) {
|
||||
function getTrustlinesAsync(account, options, callback) {
|
||||
getTrustlines.call(this, account, options)
|
||||
.then(data => callback(null, data))
|
||||
.catch(callback);
|
||||
}
|
||||
|
||||
function getBalancesAsync(account, options, callback) {
|
||||
validate.address(account);
|
||||
validate.getBalancesOptions(options);
|
||||
|
||||
@@ -32,8 +38,12 @@ function getBalances(account, options, callback) {
|
||||
|| this.remote.getLedgerSequence();
|
||||
async.parallel({
|
||||
xrp: _.partial(utils.getXRPBalance, this.remote, account, ledgerVersion),
|
||||
trustlines: _.partial(getTrustlines.bind(this), account, options)
|
||||
trustlines: _.partial(getTrustlinesAsync.bind(this), account, options)
|
||||
}, composeAsync(formatBalances, callback));
|
||||
}
|
||||
|
||||
module.exports = utils.wrapCatch(getBalances);
|
||||
function getBalances(account: string, options={}) {
|
||||
return utils.promisify(getBalancesAsync).call(this, account, options);
|
||||
}
|
||||
|
||||
module.exports = getBalances;
|
||||
|
||||
26
src/api/ledger/ledger.js
Normal file
26
src/api/ledger/ledger.js
Normal file
@@ -0,0 +1,26 @@
|
||||
/* @flow */
|
||||
'use strict';
|
||||
const utils = require('./utils');
|
||||
const validate = utils.common.validate;
|
||||
const composeAsync = utils.common.composeAsync;
|
||||
const parseLedger = require('./parse/ledger');
|
||||
|
||||
function getLedgerAsync(options, callback) {
|
||||
validate.getLedgerOptions(options);
|
||||
|
||||
const request = {
|
||||
ledger: options.ledgerVersion || 'validated',
|
||||
expand: options.includeAllData,
|
||||
transactions: options.includeTransactions,
|
||||
accounts: options.includeState
|
||||
};
|
||||
|
||||
this.remote.requestLedger(request,
|
||||
composeAsync(response => parseLedger(response.ledger), callback));
|
||||
}
|
||||
|
||||
function getLedger(options={}) {
|
||||
return utils.promisify(getLedgerAsync).call(this, options);
|
||||
}
|
||||
|
||||
module.exports = getLedger;
|
||||
@@ -10,7 +10,8 @@ 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) {
|
||||
takerGets, takerPays, callback
|
||||
) {
|
||||
remote.requestBookOffers(utils.renameCounterpartyToIssuerInOrder({
|
||||
taker_gets: takerGets,
|
||||
taker_pays: takerPays,
|
||||
@@ -62,7 +63,7 @@ function formatBidsAndAsks(orderbook, offers) {
|
||||
return {bids, asks};
|
||||
}
|
||||
|
||||
function getOrderbook(account, orderbook, options, callback) {
|
||||
function getOrderbookAsync(account, orderbook, options, callback) {
|
||||
validate.address(account);
|
||||
validate.orderbook(orderbook);
|
||||
validate.getOrderbookOptions(options);
|
||||
@@ -76,4 +77,9 @@ function getOrderbook(account, orderbook, options, callback) {
|
||||
callback));
|
||||
}
|
||||
|
||||
module.exports = utils.wrapCatch(getOrderbook);
|
||||
function getOrderbook(account: string, orderbook: Object, options={}) {
|
||||
return utils.promisify(getOrderbookAsync).call(this,
|
||||
account, orderbook, options);
|
||||
}
|
||||
|
||||
module.exports = getOrderbook;
|
||||
|
||||
@@ -7,7 +7,8 @@ const composeAsync = utils.common.composeAsync;
|
||||
const parseAccountOrder = require('./parse/account-order');
|
||||
|
||||
function requestAccountOffers(remote, address, ledgerVersion, options,
|
||||
marker, limit, callback) {
|
||||
marker, limit, callback
|
||||
) {
|
||||
remote.requestAccountOffers({
|
||||
account: address,
|
||||
marker: marker,
|
||||
@@ -20,7 +21,7 @@ function requestAccountOffers(remote, address, ledgerVersion, options,
|
||||
}), callback));
|
||||
}
|
||||
|
||||
function getOrders(account, options, callback) {
|
||||
function getOrdersAsync(account, options, callback) {
|
||||
validate.address(account);
|
||||
validate.getOrdersOptions(options);
|
||||
|
||||
@@ -33,4 +34,8 @@ function getOrders(account, options, callback) {
|
||||
(order) => order.properties.sequence), callback));
|
||||
}
|
||||
|
||||
module.exports = utils.wrapCatch(getOrders);
|
||||
function getOrders(account: string, options={}) {
|
||||
return utils.promisify(getOrdersAsync).call(this, account, options);
|
||||
}
|
||||
|
||||
module.exports = getOrders;
|
||||
|
||||
@@ -1,11 +1,15 @@
|
||||
/* @flow */
|
||||
'use strict';
|
||||
const BigNumber = require('bignumber.js');
|
||||
const AccountFields = require('./utils').constants.AccountFields;
|
||||
|
||||
function parseField(info, value) {
|
||||
if (info.encoding === 'hex' && !info.length) {
|
||||
return new Buffer(value, 'hex').toString('ascii');
|
||||
}
|
||||
if (info.shift) {
|
||||
return (new BigNumber(value)).shift(-info.shift).toNumber();
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
|
||||
50
src/api/ledger/parse/ledger.js
Normal file
50
src/api/ledger/parse/ledger.js
Normal file
@@ -0,0 +1,50 @@
|
||||
/* @flow */
|
||||
'use strict';
|
||||
const _ = require('lodash');
|
||||
const removeUndefined = require('./utils').removeUndefined;
|
||||
const parseTransaction = require('./transaction');
|
||||
|
||||
function parseTransactions(transactions) {
|
||||
if (_.isEmpty(transactions)) {
|
||||
return {};
|
||||
}
|
||||
if (_.isString(transactions[0])) {
|
||||
return {transactionHashes: transactions};
|
||||
}
|
||||
return {
|
||||
transactions: _.map(transactions, parseTransaction),
|
||||
rawTransactions: JSON.stringify(transactions)
|
||||
};
|
||||
}
|
||||
|
||||
function parseState(state) {
|
||||
if (_.isEmpty(state)) {
|
||||
return {};
|
||||
}
|
||||
if (_.isString(state[0])) {
|
||||
return {stateHashes: state};
|
||||
}
|
||||
return {rawState: JSON.stringify(state)};
|
||||
}
|
||||
|
||||
function parseLedger(ledger: Object): Object {
|
||||
return removeUndefined(_.assign({
|
||||
accepted: ledger.accepted,
|
||||
closed: ledger.closed,
|
||||
stateHash: ledger.account_hash,
|
||||
closeTime: ledger.close_time,
|
||||
closeTimeResolution: ledger.close_time_resolution,
|
||||
closeFlags: ledger.close_flags,
|
||||
ledgerHash: ledger.hash || ledger.ledger_hash,
|
||||
ledgerVersion: parseInt(ledger.ledger_index || ledger.seqNum, 10),
|
||||
parentLedgerHash: ledger.parent_hash,
|
||||
parentCloseTime: ledger.parent_close_time,
|
||||
totalDrops: ledger.total_coins || ledger.totalCoins,
|
||||
transactionHash: ledger.transaction_hash
|
||||
},
|
||||
parseTransactions(ledger.transactions),
|
||||
parseState(ledger.accountState)
|
||||
));
|
||||
}
|
||||
|
||||
module.exports = parseLedger;
|
||||
@@ -1,5 +1,6 @@
|
||||
/* @flow */
|
||||
'use strict';
|
||||
const _ = require('lodash');
|
||||
const assert = require('assert');
|
||||
const utils = require('./utils');
|
||||
const parseAmount = require('./amount');
|
||||
@@ -30,18 +31,24 @@ function parsePaymentMemos(tx) {
|
||||
});
|
||||
}
|
||||
|
||||
function removeGenericCounterparty(amount, address) {
|
||||
return amount.counterparty === address ?
|
||||
_.omit(amount, 'counterparty') : amount;
|
||||
}
|
||||
|
||||
function parsePayment(tx: Object): Object {
|
||||
assert(tx.TransactionType === 'Payment');
|
||||
|
||||
const source = {
|
||||
address: tx.Account,
|
||||
amount: parseAmount(tx.SendMax || tx.Amount),
|
||||
maxAmount: removeGenericCounterparty(
|
||||
parseAmount(tx.SendMax || tx.Amount), tx.Account),
|
||||
tag: tx.SourceTag
|
||||
};
|
||||
|
||||
const destination = {
|
||||
address: tx.Destination,
|
||||
amount: parseAmount(tx.Amount),
|
||||
amount: removeGenericCounterparty(parseAmount(tx.Amount), tx.Destination),
|
||||
tag: tx.DestinationTag
|
||||
};
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
const assert = require('assert');
|
||||
const utils = require('./utils');
|
||||
const flags = utils.core.Transaction.flags.TrustSet;
|
||||
const BigNumber = require('bignumber.js');
|
||||
|
||||
function parseFlag(flagsValue, trueValue, falseValue) {
|
||||
if (flagsValue & trueValue) {
|
||||
@@ -14,6 +15,13 @@ function parseFlag(flagsValue, trueValue, falseValue) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function parseQuality(quality?: number) {
|
||||
if (typeof quality === 'number') {
|
||||
return (new BigNumber(quality)).shift(-9).toNumber();
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function parseTrustline(tx: Object): Object {
|
||||
assert(tx.TransactionType === 'TrustSet');
|
||||
|
||||
@@ -21,8 +29,8 @@ function parseTrustline(tx: Object): Object {
|
||||
limit: tx.LimitAmount.value,
|
||||
currency: tx.LimitAmount.currency,
|
||||
counterparty: tx.LimitAmount.issuer,
|
||||
qualityIn: tx.QualityIn,
|
||||
qualityOut: tx.QualityOut,
|
||||
qualityIn: parseQuality(tx.QualityIn),
|
||||
qualityOut: parseQuality(tx.QualityOut),
|
||||
ripplingDisabled: parseFlag(
|
||||
tx.Flags, flags.SetNoRipple, flags.ClearNoRipple),
|
||||
frozen: parseFlag(tx.Flags, flags.SetFreeze, flags.ClearFreeze),
|
||||
|
||||
@@ -6,8 +6,9 @@ const toTimestamp = require('../../../core/utils').toTimestamp;
|
||||
const utils = require('../utils');
|
||||
const BigNumber = require('bignumber.js');
|
||||
|
||||
function adjustQualityForXRP(quality: string, takerGetsCurrency: string,
|
||||
takerPaysCurrency: string) {
|
||||
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);
|
||||
@@ -51,7 +52,7 @@ function parseOutcome(tx: Object): ?Object {
|
||||
}
|
||||
|
||||
const balanceChanges = transactionParser.parseBalanceChanges(tx.meta);
|
||||
const orderbookChanges = transactionParser.parseOrderBookChanges(tx.meta);
|
||||
const orderbookChanges = transactionParser.parseOrderbookChanges(tx.meta);
|
||||
removeEmptyCounterpartyInBalanceChanges(balanceChanges);
|
||||
removeEmptyCounterpartyInOrderbookChanges(orderbookChanges);
|
||||
|
||||
|
||||
@@ -46,7 +46,7 @@ function requestPathFind(remote, pathfind: PathFind, callback) {
|
||||
_.omit(utils.common.toRippledAmount(amount), 'value'));
|
||||
}
|
||||
|
||||
remote.requestRipplePathFind(params,
|
||||
remote.createPathFind(params,
|
||||
composeAsync(_.partial(addParams, params), callback));
|
||||
}
|
||||
|
||||
@@ -103,7 +103,7 @@ function formatResponse(pathfind, paths) {
|
||||
}
|
||||
}
|
||||
|
||||
function getPaths(pathfind, callback) {
|
||||
function getPathsAsync(pathfind, callback) {
|
||||
validate.pathfind(pathfind);
|
||||
|
||||
const address = pathfind.source.address;
|
||||
@@ -113,4 +113,8 @@ function getPaths(pathfind, callback) {
|
||||
], composeAsync(_.partial(formatResponse, pathfind), callback));
|
||||
}
|
||||
|
||||
module.exports = utils.wrapCatch(getPaths);
|
||||
function getPaths(pathfind: Object) {
|
||||
return utils.promisify(getPathsAsync).call(this, pathfind);
|
||||
}
|
||||
|
||||
module.exports = getPaths;
|
||||
|
||||
@@ -24,17 +24,21 @@ function formatSettings(response) {
|
||||
return _.assign({}, parsedFlags, parsedFields);
|
||||
}
|
||||
|
||||
function getSettings(account, options, callback) {
|
||||
function getSettingsAsync(account, options, callback) {
|
||||
validate.address(account);
|
||||
validate.getSettingsOptions(options);
|
||||
|
||||
const request = {
|
||||
account: account,
|
||||
ledger: options.ledgerVersion
|
||||
ledger: options.ledgerVersion || 'validated'
|
||||
};
|
||||
|
||||
this.remote.requestAccountInfo(request,
|
||||
composeAsync(formatSettings, callback));
|
||||
}
|
||||
|
||||
module.exports = utils.wrapCatch(getSettings);
|
||||
function getSettings(account: string, options={}) {
|
||||
return utils.promisify(getSettingsAsync).call(this, account, options);
|
||||
}
|
||||
|
||||
module.exports = getSettings;
|
||||
|
||||
147
src/api/ledger/transaction-types.js
Normal file
147
src/api/ledger/transaction-types.js
Normal file
@@ -0,0 +1,147 @@
|
||||
/* @flow */
|
||||
'use strict';
|
||||
|
||||
type Outcome = {
|
||||
result: string,
|
||||
timestamp?: string,
|
||||
fee: string,
|
||||
balanceChanges: Object,
|
||||
orderbookChanges: Object,
|
||||
ledgerVersion: number,
|
||||
indexInLedger: number
|
||||
}
|
||||
|
||||
type Adjustment = {
|
||||
address: string,
|
||||
amount: {
|
||||
currency: string,
|
||||
counterparty?: string,
|
||||
value: string
|
||||
},
|
||||
tag?: number
|
||||
}
|
||||
|
||||
type Trustline = {
|
||||
currency: string,
|
||||
counterparty: string,
|
||||
limit: string,
|
||||
qualityIn?: number,
|
||||
qualityOut?: number,
|
||||
ripplingDisabled?: boolean,
|
||||
authorized?: boolean,
|
||||
frozen?: boolean
|
||||
}
|
||||
|
||||
type Settings = {
|
||||
passwordSpent?: boolean,
|
||||
requireDestinationTag?: boolean,
|
||||
requireAuthorization?: boolean,
|
||||
disallowIncomingXRP?: boolean,
|
||||
disableMasterKey?: boolean,
|
||||
enableTransactionIDTracking?: boolean,
|
||||
noFreeze?: boolean,
|
||||
globalFreeze?: boolean,
|
||||
defaultRipple?: boolean,
|
||||
emailHash?: string,
|
||||
walletLocator?: string,
|
||||
walletSize?: number,
|
||||
messageKey?: string,
|
||||
domain?: string,
|
||||
transferRate?: number,
|
||||
signers?: string,
|
||||
regularKey?: string
|
||||
}
|
||||
|
||||
type OrderCancellation = {
|
||||
orderSequence: number
|
||||
}
|
||||
|
||||
type Memo = {
|
||||
type?: string,
|
||||
format?: string,
|
||||
data?: string
|
||||
}
|
||||
|
||||
type Amount = {
|
||||
value: string,
|
||||
currency: string,
|
||||
counterparty?: string
|
||||
}
|
||||
|
||||
type Payment = {
|
||||
source: Adjustment,
|
||||
destination: Adjustment,
|
||||
paths?: string,
|
||||
memos?: Array<Memo>,
|
||||
invoiceID?: string,
|
||||
allowPartialPayment?: boolean,
|
||||
noDirectRipple?: boolean,
|
||||
limitQuality?: boolean
|
||||
}
|
||||
|
||||
type PaymentTransaction = {
|
||||
type: string,
|
||||
specification: Payment,
|
||||
outcome: Outcome,
|
||||
id: string,
|
||||
address: string,
|
||||
sequence: number
|
||||
}
|
||||
|
||||
type Order = {
|
||||
direction: string,
|
||||
quantity: Amount,
|
||||
totalPrice: Amount,
|
||||
immediateOrCancel?: boolean,
|
||||
fillOrKill?: boolean,
|
||||
passive?: boolean
|
||||
}
|
||||
|
||||
type OrderTransaction = {
|
||||
type: string,
|
||||
specification: Order,
|
||||
outcome: Outcome,
|
||||
id: string,
|
||||
address: string,
|
||||
sequence: number
|
||||
}
|
||||
|
||||
type OrderCancellationTransaction = {
|
||||
type: string,
|
||||
specification: OrderCancellation,
|
||||
outcome: Outcome,
|
||||
id: string,
|
||||
address: string,
|
||||
sequence: number
|
||||
}
|
||||
|
||||
type TrustlineTransaction = {
|
||||
type: string,
|
||||
specification: Trustline,
|
||||
outcome: Outcome,
|
||||
id: string,
|
||||
address: string,
|
||||
sequence: number
|
||||
}
|
||||
|
||||
type SettingsTransaction = {
|
||||
type: string,
|
||||
specification: Settings,
|
||||
outcome: Outcome,
|
||||
id: string,
|
||||
address: string,
|
||||
sequence: number
|
||||
}
|
||||
|
||||
export type TransactionOptions = {
|
||||
minLedgerVersion?: number,
|
||||
maxLedgerVersion?: number
|
||||
}
|
||||
|
||||
export type GetTransactionResponse = PaymentTransaction | OrderTransaction |
|
||||
OrderCancellationTransaction | TrustlineTransaction | SettingsTransaction
|
||||
|
||||
export type GetTransactionResponseCallback =
|
||||
(err?: ?Error, data?: GetTransactionResponse) => void
|
||||
|
||||
export type CallbackType = (err?: ?Error, data?: Object) => void
|
||||
@@ -6,8 +6,17 @@ const utils = require('./utils');
|
||||
const parseTransaction = require('./parse/transaction');
|
||||
const validate = utils.common.validate;
|
||||
const errors = utils.common.errors;
|
||||
const RippleError = require('../../core/rippleerror').RippleError;
|
||||
|
||||
function attachTransactionDate(remote, tx, callback) {
|
||||
import type {Remote} from '../../core/remote';
|
||||
|
||||
import type {CallbackType, GetTransactionResponse,
|
||||
GetTransactionResponseCallback, TransactionOptions}
|
||||
from './transaction-types';
|
||||
|
||||
function attachTransactionDate(remote: Remote, tx: Object,
|
||||
callback: CallbackType
|
||||
) {
|
||||
if (tx.date) {
|
||||
callback(null, tx);
|
||||
return;
|
||||
@@ -28,40 +37,57 @@ function attachTransactionDate(remote, tx, callback) {
|
||||
});
|
||||
}
|
||||
|
||||
function isTransactionInRange(tx, options) {
|
||||
function isTransactionInRange(tx: Object, options: TransactionOptions) {
|
||||
return (!options.minLedgerVersion
|
||||
|| tx.ledger_index >= options.minLedgerVersion)
|
||||
&& (!options.maxLedgerVersion
|
||||
|| tx.ledger_index <= options.maxLedgerVersion);
|
||||
}
|
||||
|
||||
function getTransaction(identifier, options, callback) {
|
||||
function getTransactionAsync(identifier: string, options: TransactionOptions,
|
||||
callback: GetTransactionResponseCallback
|
||||
) {
|
||||
validate.identifier(identifier);
|
||||
validate.getTransactionOptions(options);
|
||||
|
||||
const remote = this.remote;
|
||||
const maxLedgerVersion = Math.min(options.maxLedgerVersion,
|
||||
const maxLedgerVersion = Math.min(options.maxLedgerVersion || Infinity,
|
||||
remote.getLedgerSequence());
|
||||
|
||||
function callbackWrapper(error, tx) {
|
||||
function callbackWrapper(error_?: Error, tx?: Object) {
|
||||
let error = error_;
|
||||
if (error instanceof RippleError && error.remote &&
|
||||
error.remote.error === 'txnNotFound') {
|
||||
error = new errors.NotFoundError('Transaction not found');
|
||||
}
|
||||
|
||||
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)) {
|
||||
} else if (!error && tx && !isTransactionInRange(tx, options)) {
|
||||
callback(new errors.NotFoundError('Transaction not found'));
|
||||
} else if (error) {
|
||||
callback(error);
|
||||
} else if (!tx) {
|
||||
callback(new Error('Internal error'));
|
||||
} else {
|
||||
callback(error, parseTransaction(tx));
|
||||
}
|
||||
}
|
||||
|
||||
async.waterfall([
|
||||
_.partial(remote.requestTx.bind(remote), {hash: identifier, binary: false}),
|
||||
_.partial(remote.requestTx.bind(remote),
|
||||
{hash: identifier, binary: false}),
|
||||
_.partial(attachTransactionDate, remote)
|
||||
], callbackWrapper);
|
||||
}
|
||||
|
||||
module.exports = utils.wrapCatch(getTransaction);
|
||||
function getTransaction(identifier: string,
|
||||
options: TransactionOptions={}
|
||||
): Promise<GetTransactionResponse> {
|
||||
return utils.promisify(getTransactionAsync).call(this, identifier, options);
|
||||
}
|
||||
|
||||
module.exports = getTransaction;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
/* @flow */
|
||||
/* eslint-disable max-params */
|
||||
'use strict';
|
||||
const _ = require('lodash');
|
||||
const utils = require('./utils');
|
||||
@@ -14,6 +15,21 @@ function parseAccountTxTransaction(tx) {
|
||||
return parseTransaction(tx.tx);
|
||||
}
|
||||
|
||||
function counterpartyFilter(filters, tx) {
|
||||
if (!filters.counterparty) {
|
||||
return true;
|
||||
}
|
||||
if (tx.address === filters.counterparty || (
|
||||
tx.specification && (
|
||||
(tx.specification.destination &&
|
||||
tx.specification.destination.address === filters.counterparty) ||
|
||||
(tx.specification.counterparty === filters.counterparty)
|
||||
))) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function transactionFilter(address, filters, tx) {
|
||||
if (filters.excludeFailures && tx.outcome.result !== 'tesSUCCESS') {
|
||||
return false;
|
||||
@@ -27,8 +43,7 @@ function transactionFilter(address, filters, tx) {
|
||||
if (filters.initiated === false && tx.address === address) {
|
||||
return false;
|
||||
}
|
||||
if (filters.counterparty && tx.address !== filters.counterparty
|
||||
&& tx.Destination !== filters.counterparty) {
|
||||
if (filters.counterparty && !counterpartyFilter(filters, tx)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
@@ -97,27 +112,27 @@ function getTransactionsInternal(remote, address, options, callback) {
|
||||
utils.getRecursive(getter, options.limit, composeAsync(format, callback));
|
||||
}
|
||||
|
||||
function getTransactions(address, options, callback) {
|
||||
validate.address(address);
|
||||
function getTransactionsAsync(account, options, callback) {
|
||||
validate.address(account);
|
||||
validate.getTransactionsOptions(options);
|
||||
|
||||
const defaults = {maxLedgerVersion: this.remote.getLedgerSequence()};
|
||||
if (options.start) {
|
||||
getTransaction.bind(this)(options.start, {}, (error, tx) => {
|
||||
if (error) {
|
||||
callback(error);
|
||||
return;
|
||||
}
|
||||
getTransaction.call(this, options.start).then(tx => {
|
||||
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);
|
||||
});
|
||||
getTransactionsInternal(this.remote, account, newOptions, callback);
|
||||
}).catch(callback);
|
||||
} else {
|
||||
const newOptions = _.assign(defaults, options);
|
||||
getTransactionsInternal(this.remote, address, newOptions, callback);
|
||||
getTransactionsInternal(this.remote, account, newOptions, callback);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = utils.wrapCatch(getTransactions);
|
||||
function getTransactions(account: string, options={}) {
|
||||
return utils.promisify(getTransactionsAsync).call(this, account, options);
|
||||
}
|
||||
|
||||
module.exports = getTransactions;
|
||||
|
||||
@@ -10,7 +10,8 @@ function currencyFilter(currency, trustline) {
|
||||
}
|
||||
|
||||
function getAccountLines(remote, address, ledgerVersion, options, marker, limit,
|
||||
callback) {
|
||||
callback
|
||||
) {
|
||||
const requestOptions = {
|
||||
account: address,
|
||||
ledger: ledgerVersion,
|
||||
@@ -29,9 +30,10 @@ function getAccountLines(remote, address, ledgerVersion, options, marker, limit,
|
||||
});
|
||||
}
|
||||
|
||||
function getTrustlines(account: string, options: {currency: string,
|
||||
function getTrustlinesAsync(account: string, options: {currency: string,
|
||||
counterparty: string, limit: number, ledgerVersion: number},
|
||||
callback: () => void): void {
|
||||
callback: () => void
|
||||
): void {
|
||||
validate.address(account);
|
||||
validate.getTrustlinesOptions(options);
|
||||
|
||||
@@ -42,4 +44,8 @@ function getTrustlines(account: string, options: {currency: string,
|
||||
utils.getRecursive(getter, options.limit, callback);
|
||||
}
|
||||
|
||||
module.exports = utils.wrapCatch(getTrustlines);
|
||||
function getTrustlines(account: string, options={}) {
|
||||
return utils.promisify(getTrustlinesAsync).call(this, account, options);
|
||||
}
|
||||
|
||||
module.exports = getTrustlines;
|
||||
|
||||
@@ -1,23 +1,33 @@
|
||||
/* @flow */
|
||||
'use strict';
|
||||
const _ = require('lodash');
|
||||
const assert = require('assert');
|
||||
const common = require('../common');
|
||||
const dropsToXrp = common.dropsToXrp;
|
||||
const composeAsync = common.composeAsync;
|
||||
import type {Remote} from '../../core/remote';
|
||||
|
||||
function clamp(value, min, max) {
|
||||
type Callback = (err: any, data: any) => void
|
||||
|
||||
function clamp(value: number, min: number, max: number): number {
|
||||
assert(min <= max, 'Illegal clamp bounds');
|
||||
return Math.min(Math.max(value, min), max);
|
||||
}
|
||||
|
||||
function getXRPBalance(remote, address, ledgerVersion, callback) {
|
||||
function getXRPBalance(remote: Remote, address: string, ledgerVersion?: number,
|
||||
callback: Callback
|
||||
): void {
|
||||
remote.requestAccountInfo({account: address, ledger: ledgerVersion},
|
||||
composeAsync((data) => dropsToXrp(data.account_data.Balance), callback));
|
||||
}
|
||||
|
||||
type Getter = (marker: ?string, limit: number, callback: Callback) => void
|
||||
|
||||
// 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) {
|
||||
function getRecursiveRecur(getter: Getter, marker?: string, limit: number,
|
||||
callback: Callback
|
||||
): void {
|
||||
getter(marker, limit, (error, data) => {
|
||||
if (error) {
|
||||
return callback(error);
|
||||
@@ -34,11 +44,13 @@ function getRecursiveRecur(getter, marker, limit, callback) {
|
||||
});
|
||||
}
|
||||
|
||||
function getRecursive(getter, limit, callback) {
|
||||
function getRecursive(getter: Getter, limit?: number, callback: Callback) {
|
||||
getRecursiveRecur(getter, undefined, limit || Infinity, callback);
|
||||
}
|
||||
|
||||
function renameCounterpartyToIssuer(amount) {
|
||||
type Amount = {counterparty?: string, issuer?: string, value: string}
|
||||
|
||||
function renameCounterpartyToIssuer(amount?: Amount): ?{issuer?: string} {
|
||||
if (amount === undefined) {
|
||||
return undefined;
|
||||
}
|
||||
@@ -48,7 +60,9 @@ function renameCounterpartyToIssuer(amount) {
|
||||
return _.omit(withIssuer, 'counterparty');
|
||||
}
|
||||
|
||||
function renameCounterpartyToIssuerInOrder(order) {
|
||||
type Order = {taker_gets: Amount, taker_pays: Amount}
|
||||
|
||||
function renameCounterpartyToIssuerInOrder(order: 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};
|
||||
@@ -70,14 +84,21 @@ function signum(num) {
|
||||
* @returns {Number} [-1, 0, 1]
|
||||
*/
|
||||
|
||||
function compareTransactions(first, second) {
|
||||
if (first.ledgerVersion === second.ledgerVersion) {
|
||||
return signum(Number(first.indexInLedger) - Number(second.indexInLedger));
|
||||
type Outcome = {outcome: {ledgerVersion: number, indexInLedger: number}};
|
||||
|
||||
function compareTransactions(first: Outcome, second: Outcome): number {
|
||||
if (!first.outcome || !second.outcome) {
|
||||
return 0;
|
||||
}
|
||||
return Number(first.ledgerVersion) < Number(second.ledgerVersion) ? -1 : 1;
|
||||
if (first.outcome.ledgerVersion === second.outcome.ledgerVersion) {
|
||||
return signum(first.outcome.indexInLedger - second.outcome.indexInLedger);
|
||||
}
|
||||
return first.outcome.ledgerVersion < second.outcome.ledgerVersion ? -1 : 1;
|
||||
}
|
||||
|
||||
function hasCompleteLedgerRange(remote, minLedgerVersion, maxLedgerVersion) {
|
||||
function hasCompleteLedgerRange(remote: Remote, minLedgerVersion?: number,
|
||||
maxLedgerVersion?: number
|
||||
): boolean {
|
||||
const firstLedgerVersion = 32570; // earlier versions have been lost
|
||||
return remote.getServer().hasLedgerRange(
|
||||
minLedgerVersion || firstLedgerVersion,
|
||||
@@ -91,7 +112,7 @@ module.exports = {
|
||||
renameCounterpartyToIssuerInOrder,
|
||||
getRecursive,
|
||||
hasCompleteLedgerRange,
|
||||
wrapCatch: common.wrapCatch,
|
||||
promisify: common.promisify,
|
||||
clamp: clamp,
|
||||
common: common
|
||||
};
|
||||
|
||||
74
src/api/offline/ledgerhash.js
Normal file
74
src/api/offline/ledgerhash.js
Normal file
@@ -0,0 +1,74 @@
|
||||
/* @flow */
|
||||
'use strict';
|
||||
const _ = require('lodash');
|
||||
const common = require('../common');
|
||||
|
||||
function convertLedgerHeader(header) {
|
||||
return {
|
||||
accepted: header.accepted,
|
||||
closed: header.closed,
|
||||
account_hash: header.stateHash,
|
||||
close_time: header.closeTime,
|
||||
close_time_resolution: header.closeTimeResolution,
|
||||
close_flags: header.closeFlags,
|
||||
hash: header.ledgerHash,
|
||||
ledger_hash: header.ledgerHash,
|
||||
ledger_index: header.ledgerVersion.toString(),
|
||||
seqNum: header.ledgerVersion.toString(),
|
||||
parent_hash: header.parentLedgerHash,
|
||||
parent_close_time: header.parentCloseTime,
|
||||
total_coins: header.totalDrops,
|
||||
totalCoins: header.totalDrops,
|
||||
transaction_hash: header.transactionHash
|
||||
};
|
||||
}
|
||||
|
||||
function hashLedgerHeader(ledgerHeader) {
|
||||
const header = convertLedgerHeader(ledgerHeader);
|
||||
return common.core.Ledger.calculateLedgerHash(header);
|
||||
}
|
||||
|
||||
function computeTransactionHash(ledger) {
|
||||
if (ledger.rawTransactions === undefined) {
|
||||
return ledger.transactionHash;
|
||||
}
|
||||
const transactions = JSON.parse(ledger.rawTransactions);
|
||||
const txs = _.map(transactions, tx => {
|
||||
const mergeTx = _.assign({}, _.omit(tx, 'tx'), tx.tx || {});
|
||||
const renameMeta = _.assign({}, _.omit(mergeTx, 'meta'),
|
||||
tx.meta ? {metaData: tx.meta} : {});
|
||||
return renameMeta;
|
||||
});
|
||||
const ledgerObject = common.core.Ledger.from_json({transactions: txs});
|
||||
const transactionHash = ledgerObject.calc_tx_hash().to_hex();
|
||||
if (ledger.transactionHash !== undefined
|
||||
&& ledger.transactionHash !== transactionHash) {
|
||||
throw new common.errors.ValidationError('transactionHash in header'
|
||||
+ ' does not match computed hash of transactions');
|
||||
}
|
||||
return transactionHash;
|
||||
}
|
||||
|
||||
function computeStateHash(ledger) {
|
||||
if (ledger.rawState === undefined) {
|
||||
return ledger.stateHash;
|
||||
}
|
||||
const state = JSON.parse(ledger.rawState);
|
||||
const ledgerObject = common.core.Ledger.from_json({accountState: state});
|
||||
const stateHash = ledgerObject.calc_account_hash().to_hex();
|
||||
if (ledger.stateHash !== undefined && ledger.stateHash !== stateHash) {
|
||||
throw new common.errors.ValidationError('stateHash in header'
|
||||
+ ' does not match computed hash of state');
|
||||
}
|
||||
return stateHash;
|
||||
}
|
||||
|
||||
function computeLedgerHash(ledger: Object): string {
|
||||
const hashes = {
|
||||
transactionHash: computeTransactionHash(ledger),
|
||||
stateHash: computeStateHash(ledger)
|
||||
};
|
||||
return hashLedgerHeader(_.assign({}, ledger, hashes));
|
||||
}
|
||||
|
||||
module.exports = computeLedgerHash;
|
||||
@@ -2,20 +2,43 @@
|
||||
|
||||
'use strict';
|
||||
|
||||
const _ = require('lodash');
|
||||
const common = require('../common');
|
||||
import type {Remote} from '../../core/remote';
|
||||
|
||||
// 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);
|
||||
type GetServerInfoResponse = {
|
||||
buildVersion: string,
|
||||
completeLedgers: string,
|
||||
hostid: string,
|
||||
ioLatencyMs: number,
|
||||
load?: {
|
||||
jobTypes: Array<Object>,
|
||||
threads: number
|
||||
},
|
||||
lastClose: {
|
||||
convergeTimeS: number,
|
||||
proposers: number
|
||||
},
|
||||
loadFactor: number,
|
||||
peers: number,
|
||||
pubkeyNode: string,
|
||||
pubkeyValidator?: string,
|
||||
serverState: string,
|
||||
validatedLedger: {
|
||||
age: number,
|
||||
baseFeeXrp: number,
|
||||
hash: string,
|
||||
reserveBaseXrp: number,
|
||||
reserveIncXrp: number,
|
||||
seq: number
|
||||
},
|
||||
validationQuorum: number
|
||||
}
|
||||
|
||||
function disconnect(callback: (err: any, data: any) => void): void {
|
||||
this.remote.disconnect(callback);
|
||||
}
|
||||
|
||||
function isUpToDate(remote): boolean {
|
||||
function isUpToDate(remote: Remote): boolean {
|
||||
const server = remote.getServer();
|
||||
return Boolean(server) && (remote._stand_alone
|
||||
|| (Date.now() - server._lastLedgerClose) <= CONNECTION_TIMEOUT);
|
||||
@@ -25,12 +48,17 @@ function isConnected(): boolean {
|
||||
return Boolean(this.remote._ledger_current_index) && isUpToDate(this.remote);
|
||||
}
|
||||
|
||||
function getServerInfo(callback: (err: any, data: any) => void): void {
|
||||
function getServerInfoAsync(
|
||||
callback: (err: any, data?: GetServerInfoResponse) => void
|
||||
): void {
|
||||
this.remote.requestServerInfo((error, response) => {
|
||||
if (error) {
|
||||
callback(new common.errors.RippledNetworkError(error.message));
|
||||
const message =
|
||||
_.get(error, ['remote', 'error_message'], error.message);
|
||||
callback(new common.errors.RippledNetworkError(message));
|
||||
} else {
|
||||
callback(null, response.info);
|
||||
callback(null,
|
||||
common.convertKeysFromSnakeCaseToCamelCase(response.info));
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -43,6 +71,22 @@ function getLedgerVersion(): number {
|
||||
return this.remote.getLedgerSequence();
|
||||
}
|
||||
|
||||
function connect(): Promise<void> {
|
||||
return common.promisify(callback => {
|
||||
this.remote.connect(() => callback(null));
|
||||
})();
|
||||
}
|
||||
|
||||
function disconnect(): Promise<void> {
|
||||
return common.promisify(callback => {
|
||||
this.remote.disconnect(() => callback(null));
|
||||
})();
|
||||
}
|
||||
|
||||
function getServerInfo(): Promise<GetServerInfoResponse> {
|
||||
return common.promisify(getServerInfoAsync.bind(this))();
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
connect,
|
||||
disconnect,
|
||||
|
||||
@@ -30,9 +30,14 @@ function createOrderTransaction(account, order) {
|
||||
return transaction;
|
||||
}
|
||||
|
||||
function prepareOrder(account, order, instructions, callback) {
|
||||
function prepareOrderAsync(account, order, instructions, callback) {
|
||||
const transaction = createOrderTransaction(account, order);
|
||||
utils.createTxJSON(transaction, this.remote, instructions, callback);
|
||||
}
|
||||
|
||||
module.exports = utils.wrapCatch(prepareOrder);
|
||||
function prepareOrder(account: string, order: Object, instructions={}) {
|
||||
return utils.promisify(prepareOrderAsync.bind(this))(
|
||||
account, order, instructions);
|
||||
}
|
||||
|
||||
module.exports = prepareOrder;
|
||||
|
||||
@@ -13,9 +13,16 @@ function createOrderCancellationTransaction(account, sequence) {
|
||||
return transaction;
|
||||
}
|
||||
|
||||
function prepareOrderCancellation(account, sequence, instructions, callback) {
|
||||
function prepareOrderCancellationAsync(account, sequence, instructions,
|
||||
callback) {
|
||||
const transaction = createOrderCancellationTransaction(account, sequence);
|
||||
utils.createTxJSON(transaction, this.remote, instructions, callback);
|
||||
}
|
||||
|
||||
module.exports = utils.wrapCatch(prepareOrderCancellation);
|
||||
function prepareOrderCancellation(account: string, sequence: number,
|
||||
instructions={}) {
|
||||
return utils.promisify(prepareOrderCancellationAsync.bind(this))(
|
||||
account, sequence, instructions);
|
||||
}
|
||||
|
||||
module.exports = prepareOrderCancellation;
|
||||
|
||||
@@ -1,21 +1,15 @@
|
||||
/* @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 isXRPToXRPPayment(payment) {
|
||||
const sourceCurrency = _.get(payment, 'source.maxAmount.currency');
|
||||
const destinationCurrency = _.get(payment, 'destination.amount.currency');
|
||||
return sourceCurrency === 'XRP' && destinationCurrency === 'XRP';
|
||||
}
|
||||
|
||||
function isIOUWithoutCounterparty(amount) {
|
||||
@@ -29,8 +23,8 @@ function applyAnyCounterpartyEncoding(payment) {
|
||||
// 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.source.maxAmount)) {
|
||||
payment.source.maxAmount.counterparty = payment.source.address;
|
||||
}
|
||||
if (isIOUWithoutCounterparty(payment.destination.amount)) {
|
||||
payment.destination.amount.counterparty = payment.destination.address;
|
||||
@@ -58,9 +52,6 @@ function createPaymentTransaction(account, payment) {
|
||||
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)
|
||||
@@ -75,19 +66,29 @@ function createPaymentTransaction(account, payment) {
|
||||
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));
|
||||
if (!isXRPToXRPPayment(payment)) {
|
||||
// Don't set SendMax for XRP->XRP payment
|
||||
// temREDUNDANT_SEND_MAX removed in:
|
||||
// https://github.com/ripple/rippled/commit/
|
||||
// c522ffa6db2648f1d8a987843e7feabf1a0b7de8/
|
||||
transaction.sendMax(toRippledAmount(payment.source.maxAmount));
|
||||
|
||||
if (payment.paths) {
|
||||
transaction.paths(JSON.parse(payment.paths));
|
||||
}
|
||||
}
|
||||
|
||||
return transaction;
|
||||
}
|
||||
|
||||
function preparePayment(account, payment, instructions, callback) {
|
||||
function preparePaymentAsync(account, payment, instructions, callback) {
|
||||
const transaction = createPaymentTransaction(account, payment);
|
||||
utils.createTxJSON(transaction, this.remote, instructions, callback);
|
||||
}
|
||||
|
||||
module.exports = utils.wrapCatch(preparePayment);
|
||||
function preparePayment(account: string, payment: Object, instructions={}) {
|
||||
return utils.promisify(preparePaymentAsync.bind(this))(
|
||||
account, payment, instructions);
|
||||
}
|
||||
|
||||
module.exports = preparePayment;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/* @flow */
|
||||
'use strict';
|
||||
const _ = require('lodash');
|
||||
const assert = require('assert');
|
||||
const BigNumber = require('bignumber.js');
|
||||
const utils = require('./utils');
|
||||
const validate = utils.common.validate;
|
||||
const AccountFlagIndices = utils.common.constants.AccountFlagIndices;
|
||||
@@ -9,7 +9,7 @@ const AccountFields = utils.common.constants.AccountFields;
|
||||
const Transaction = utils.common.core.Transaction;
|
||||
|
||||
// Emptry string passed to setting will clear it
|
||||
const CLEAR_SETTING = '';
|
||||
const CLEAR_SETTING = null;
|
||||
|
||||
function setTransactionFlags(transaction, values) {
|
||||
const keys = Object.keys(values);
|
||||
@@ -64,7 +64,7 @@ function setTransactionFields(transaction, input) {
|
||||
*/
|
||||
|
||||
function convertTransferRate(transferRate) {
|
||||
return _.isNumber(transferRate) ? transferRate * 1e9 : transferRate;
|
||||
return (new BigNumber(transferRate)).shift(9).toNumber();
|
||||
}
|
||||
|
||||
function createSettingsTransaction(account, settings) {
|
||||
@@ -90,9 +90,14 @@ function createSettingsTransaction(account, settings) {
|
||||
return transaction;
|
||||
}
|
||||
|
||||
function prepareSettings(account, settings, instructions, callback) {
|
||||
function prepareSettingsAsync(account, settings, instructions, callback) {
|
||||
const transaction = createSettingsTransaction(account, settings);
|
||||
utils.createTxJSON(transaction, this.remote, instructions, callback);
|
||||
}
|
||||
|
||||
module.exports = utils.wrapCatch(prepareSettings);
|
||||
function prepareSettings(account: string, settings: Object, instructions={}) {
|
||||
return utils.promisify(prepareSettingsAsync.bind(this))(
|
||||
account, settings, instructions);
|
||||
}
|
||||
|
||||
module.exports = prepareSettings;
|
||||
|
||||
@@ -15,15 +15,13 @@ const validate = utils.common.validate;
|
||||
* 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 getKeyPair(secret) {
|
||||
return core.Seed.from_json(secret).get_key();
|
||||
}
|
||||
|
||||
function getPublicKeyHex(keypair) {
|
||||
return keypair.to_hex_pub();
|
||||
return keypair.pubKeyHex();
|
||||
}
|
||||
|
||||
function serialize(txJSON) {
|
||||
@@ -34,26 +32,21 @@ 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 signingData(txJSON) {
|
||||
return core.Transaction.from_json(txJSON).signingData().buffer;
|
||||
}
|
||||
|
||||
function computeSignature(txJSON, keypair) {
|
||||
const signature = keypair.sign(signingHash(txJSON));
|
||||
return core.sjcl.codec.hex.fromBits(signature).toUpperCase();
|
||||
return keypair.signHex(signingData(txJSON));
|
||||
}
|
||||
|
||||
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});
|
||||
validate.secret(secret);
|
||||
|
||||
const keypair = getKeyPair(txJSON.Account, secret);
|
||||
const keypair = getKeyPair(secret);
|
||||
if (txJSON.SigningPubKey === undefined) {
|
||||
txJSON.SigningPubKey = getPublicKeyHex(keypair);
|
||||
}
|
||||
|
||||
@@ -4,12 +4,19 @@ 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);
|
||||
function submitAsync(txBlob: string, callback: (err: any, data: any) => void):
|
||||
void {
|
||||
validate.blob(txBlob);
|
||||
const request = new Request(this.remote, 'submit');
|
||||
request.message.tx_blob = tx_blob;
|
||||
request.request(null, callback);
|
||||
request.message.tx_blob = txBlob;
|
||||
request.request(null,
|
||||
utils.common.composeAsync(
|
||||
data => utils.common.convertKeysFromSnakeCaseToCamelCase(data),
|
||||
callback));
|
||||
}
|
||||
|
||||
function submit(txBlob: string) {
|
||||
return utils.promisify(submitAsync.bind(this))(txBlob);
|
||||
}
|
||||
|
||||
module.exports = submit;
|
||||
|
||||
@@ -3,13 +3,19 @@
|
||||
const utils = require('./utils');
|
||||
const validate = utils.common.validate;
|
||||
const Transaction = utils.common.core.Transaction;
|
||||
const BigNumber = require('bignumber.js');
|
||||
|
||||
const TrustSetFlags = {
|
||||
authorized: {set: 'SetAuth'},
|
||||
allowRippling: {set: 'ClearNoRipple', unset: 'NoRipple'},
|
||||
ripplingDisabled: {set: 'NoRipple', unset: 'ClearNoRipple'},
|
||||
frozen: {set: 'SetFreeze', unset: 'ClearFreeze'}
|
||||
};
|
||||
|
||||
function convertQuality(quality) {
|
||||
return quality === undefined ? undefined :
|
||||
(new BigNumber(quality)).shift(9).truncated().toNumber();
|
||||
}
|
||||
|
||||
function createTrustlineTransaction(account, trustline) {
|
||||
validate.address(account);
|
||||
validate.trustline(trustline);
|
||||
@@ -21,15 +27,20 @@ function createTrustlineTransaction(account, trustline) {
|
||||
};
|
||||
|
||||
const transaction = new Transaction();
|
||||
transaction.trustSet(account, limit,
|
||||
trustline.qualityIn, trustline.qualityOut);
|
||||
transaction.trustSet(account, limit, convertQuality(trustline.qualityIn),
|
||||
convertQuality(trustline.qualityOut));
|
||||
utils.setTransactionBitFlags(transaction, trustline, TrustSetFlags);
|
||||
return transaction;
|
||||
}
|
||||
|
||||
function prepareTrustline(account, trustline, instructions, callback) {
|
||||
function prepareTrustlineAsync(account, trustline, instructions, callback) {
|
||||
const transaction = createTrustlineTransaction(account, trustline);
|
||||
utils.createTxJSON(transaction, this.remote, instructions, callback);
|
||||
}
|
||||
|
||||
module.exports = utils.wrapCatch(prepareTrustline);
|
||||
function prepareTrustline(account: string, trustline: Object, instructions={}) {
|
||||
return utils.promisify(prepareTrustlineAsync.bind(this))(
|
||||
account, trustline, instructions);
|
||||
}
|
||||
|
||||
module.exports = prepareTrustline;
|
||||
|
||||
@@ -65,6 +65,6 @@ function createTxJSON(transaction: any, remote: any, instructions: any,
|
||||
module.exports = {
|
||||
setTransactionBitFlags: setTransactionBitFlags,
|
||||
createTxJSON: createTxJSON,
|
||||
wrapCatch: common.wrapCatch,
|
||||
common: common
|
||||
common: common,
|
||||
promisify: common.promisify
|
||||
};
|
||||
|
||||
@@ -12,13 +12,14 @@
|
||||
|
||||
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;
|
||||
const util = require('util');
|
||||
const {createAccountID} = require('ripple-keypairs');
|
||||
const {encodeAccountID} = require('ripple-address-codec');
|
||||
const {EventEmitter} = require('events');
|
||||
const {hexToArray} = require('./utils');
|
||||
const {TransactionManager} = require('./transactionmanager');
|
||||
const {UInt160} = require('./uint160');
|
||||
|
||||
/**
|
||||
* @constructor Account
|
||||
@@ -41,7 +42,7 @@ function Account(remote, account) {
|
||||
this._entry = { };
|
||||
|
||||
function listenerAdded(type) {
|
||||
if (~Account.subscribeEvents.indexOf(type)) {
|
||||
if (_.includes(Account.subscribeEvents, type)) {
|
||||
if (!self._subs && self._remote._connected) {
|
||||
self._remote.requestSubscribe()
|
||||
.addAccount(self._account_id)
|
||||
@@ -54,7 +55,7 @@ function Account(remote, account) {
|
||||
this.on('newListener', listenerAdded);
|
||||
|
||||
function listenerRemoved(type) {
|
||||
if (~Account.subscribeEvents.indexOf(type)) {
|
||||
if (_.includes(Account.subscribeEvents, type)) {
|
||||
self._subs -= 1;
|
||||
if (!self._subs && self._remote._connected) {
|
||||
self._remote.requestUnsubscribe()
|
||||
@@ -68,7 +69,7 @@ function Account(remote, account) {
|
||||
|
||||
function attachAccount(request) {
|
||||
if (self._account.is_valid() && self._subs) {
|
||||
request.add_account(self._account_id);
|
||||
request.addAccount(self._account_id);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -376,12 +377,7 @@ Account.prototype.publicKeyIsActive = function(public_key, callback) {
|
||||
Account._publicKeyToAddress = function(public_key) {
|
||||
// Based on functions in /src/js/ripple/keypair.js
|
||||
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();
|
||||
return encodeAccountID(createAccountID(hexToArray(publicKey)));
|
||||
}
|
||||
|
||||
if (UInt160.is_valid(public_key)) {
|
||||
|
||||
127
src/core/base.js
127
src/core/base.js
@@ -1,18 +1,11 @@
|
||||
'use strict';
|
||||
const _ = require('lodash');
|
||||
const sjcl = require('./utils').sjcl;
|
||||
const utils = require('./utils');
|
||||
|
||||
const BN = require('bn.js');
|
||||
const extend = require('extend');
|
||||
const convertBase = require('./baseconverter');
|
||||
const {encode, decode} = require('ripple-address-codec');
|
||||
|
||||
const Base = {};
|
||||
|
||||
const alphabets = Base.alphabets = {
|
||||
ripple: 'rpshnaf39wBUDNEGHJKLM4PQRST7VWXYZ2bcdeCg65jkm8oFqi1tuvAxyz',
|
||||
tipple: 'RPShNAF39wBUDnEGHJKLM4pQrsT7VWXYZ2bcdeCg65jkm8ofqi1tuvaxyz',
|
||||
bitcoin: '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'
|
||||
};
|
||||
|
||||
extend(Base, {
|
||||
VER_NONE: 1,
|
||||
VER_NODE_PUBLIC: 28,
|
||||
@@ -21,134 +14,44 @@ extend(Base, {
|
||||
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]);
|
||||
VER_FAMILY_SEED: 33,
|
||||
VER_ED25519_SEED: [0x01, 0xE1, 0x4B]
|
||||
});
|
||||
|
||||
// --> 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);
|
||||
Base.encode = function(input, alphabet) {
|
||||
return encode(input, {alphabet});
|
||||
};
|
||||
|
||||
// --> input: String
|
||||
// <-- array of bytes or undefined.
|
||||
Base.decode = function(input, alpha) {
|
||||
Base.decode = function(input, alphabet) {
|
||||
if (typeof input !== 'string') {
|
||||
return undefined;
|
||||
}
|
||||
try {
|
||||
return this.encoders[alpha || 'ripple'].decode(input);
|
||||
return decode(input, {alphabet});
|
||||
} 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);
|
||||
return encode(input, {version, alphabet});
|
||||
};
|
||||
|
||||
// --> input : String
|
||||
// <-- NaN || sjcl.bn
|
||||
// <-- NaN || BN
|
||||
Base.decode_check = function(version, input, alphabet) {
|
||||
const buffer = Base.decode(input, alphabet);
|
||||
|
||||
if (!buffer || buffer.length < 5) {
|
||||
try {
|
||||
const decoded = decode(input, {version, alphabet});
|
||||
return new BN(decoded);
|
||||
} catch (e) {
|
||||
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;
|
||||
|
||||
@@ -400,6 +400,7 @@ exports.ledger = {
|
||||
};
|
||||
|
||||
exports.metadata = [
|
||||
[ 'DeliveredAmount' , OPTIONAL ],
|
||||
[ 'TransactionIndex' , REQUIRED ],
|
||||
[ 'TransactionResult' , REQUIRED ],
|
||||
[ 'AffectedNodes' , REQUIRED ]
|
||||
|
||||
@@ -27,41 +27,4 @@ exports._test = {
|
||||
RangeSet: require('./rangeset').RangeSet
|
||||
};
|
||||
|
||||
// Important: We do not guarantee any specific version of SJCL or for any
|
||||
// specific features to be included. The version and configuration may change at
|
||||
// any time without warning.
|
||||
//
|
||||
// However, for programs that are tied to a specific version of ripple.js like
|
||||
// the official client, it makes sense to expose the SJCL instance so we don't
|
||||
// have to include it twice.
|
||||
exports.sjcl = require('./utils').sjcl;
|
||||
exports.Wallet = require('ripple-wallet-generator')({sjcl: exports.sjcl});
|
||||
exports.types = require('./serializedtypes');
|
||||
|
||||
// camelCase to under_scored API conversion
|
||||
function attachUnderscored(name) {
|
||||
const o = exports[name];
|
||||
|
||||
Object.keys(o.prototype).forEach(function(key) {
|
||||
const UPPERCASE = /([A-Z]{1})[a-z]+/g;
|
||||
|
||||
if (!UPPERCASE.test(key)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const underscored = key.replace(UPPERCASE, function(c) {
|
||||
return '_' + c.toLowerCase();
|
||||
});
|
||||
|
||||
o.prototype[underscored] = o.prototype[key];
|
||||
});
|
||||
}
|
||||
|
||||
['Remote',
|
||||
'Request',
|
||||
'Transaction',
|
||||
'Account',
|
||||
'Server'
|
||||
].forEach(attachUnderscored);
|
||||
|
||||
// vim:sw=2:sts=2:ts=8:et
|
||||
|
||||
@@ -1,102 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
/*eslint new-cap: 1*/
|
||||
|
||||
var sjcl = require('./utils').sjcl;
|
||||
|
||||
var UInt160 = require('./uint160').UInt160;
|
||||
var UInt256 = require('./uint256').UInt256;
|
||||
var Base = require('./base').Base;
|
||||
|
||||
function KeyPair() {
|
||||
this._curve = sjcl.ecc.curves.k256;
|
||||
this._secret = null;
|
||||
this._pubkey = null;
|
||||
}
|
||||
|
||||
KeyPair.from_bn_secret = function(j) {
|
||||
return (j instanceof this) ? j.clone() : (new this()).parse_bn_secret(j);
|
||||
};
|
||||
|
||||
KeyPair.prototype.parse_bn_secret = function(j) {
|
||||
this._secret = new sjcl.ecc.ecdsa.secretKey(sjcl.ecc.curves.k256, j);
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* @private
|
||||
*
|
||||
* @return {sjcl.ecc.ecdsa.publicKey} public key
|
||||
*/
|
||||
KeyPair.prototype._pub = function() {
|
||||
var curve = this._curve;
|
||||
|
||||
if (!this._pubkey && this._secret) {
|
||||
var exponent = this._secret._exponent;
|
||||
|
||||
this._pubkey = new sjcl.ecc.ecdsa.publicKey(curve, curve.G.mult(exponent));
|
||||
}
|
||||
|
||||
return this._pubkey;
|
||||
};
|
||||
|
||||
/**
|
||||
* @private
|
||||
*
|
||||
* @return {sjcl.bitArray} public key bits in compressed form
|
||||
*/
|
||||
KeyPair.prototype._pub_bits = function() {
|
||||
var pub = this._pub();
|
||||
|
||||
if (!pub) {
|
||||
return null;
|
||||
}
|
||||
|
||||
var point = pub._point, y_even = point.y.mod(2).equals(0);
|
||||
|
||||
return sjcl.bitArray.concat(
|
||||
[sjcl.bitArray.partial(8, y_even ? 0x02 : 0x03)],
|
||||
point.x.toBits(this._curve.r.bitLength())
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* @return {String} public key bytes in compressed form, hex encoded.
|
||||
*/
|
||||
KeyPair.prototype.to_hex_pub = function() {
|
||||
var bits = this._pub_bits();
|
||||
|
||||
if (!bits) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return sjcl.codec.hex.fromBits(bits).toUpperCase();
|
||||
};
|
||||
|
||||
function sha256_ripemd160(bits) {
|
||||
return sjcl.hash.ripemd160.hash(sjcl.hash.sha256.hash(bits));
|
||||
}
|
||||
|
||||
KeyPair.prototype.get_address = function() {
|
||||
var bits = this._pub_bits();
|
||||
|
||||
if (!bits) {
|
||||
return null;
|
||||
}
|
||||
|
||||
var hash = sha256_ripemd160(bits);
|
||||
|
||||
var address = UInt160.from_bits(hash);
|
||||
address.set_version(Base.VER_ACCOUNT_ID);
|
||||
return address;
|
||||
};
|
||||
|
||||
KeyPair.prototype.sign = function(hash) {
|
||||
var PARANOIA_256_BITS = 6; // sjcl constant for ensuring 256 bits of entropy
|
||||
hash = UInt256.from_json(hash);
|
||||
var sig = this._secret.sign(hash.to_bits(), PARANOIA_256_BITS);
|
||||
sig = this._secret.canonicalizeSignature(sig);
|
||||
return this._secret.encodeDER(sig);
|
||||
};
|
||||
|
||||
exports.KeyPair = KeyPair;
|
||||
@@ -1,19 +1,19 @@
|
||||
/* eslint-disable valid-jsdoc */
|
||||
'use strict';
|
||||
var Transaction = require('./transaction').Transaction;
|
||||
var SHAMap = require('./shamap').SHAMap;
|
||||
var SHAMapTreeNode = require('./shamap').SHAMapTreeNode;
|
||||
var SerializedObject = require('./serializedobject').SerializedObject;
|
||||
var stypes = require('./serializedtypes');
|
||||
var UInt160 = require('./uint160').UInt160;
|
||||
var Currency = require('./currency').Currency;
|
||||
const BigNumber = require('bignumber.js');
|
||||
const Transaction = require('./transaction').Transaction;
|
||||
const SHAMap = require('./shamap').SHAMap;
|
||||
const SHAMapTreeNode = require('./shamap').SHAMapTreeNode;
|
||||
const SerializedObject = require('./serializedobject').SerializedObject;
|
||||
const stypes = require('./serializedtypes');
|
||||
const UInt160 = require('./uint160').UInt160;
|
||||
const Currency = require('./currency').Currency;
|
||||
|
||||
function Ledger() {
|
||||
this.ledger_json = {};
|
||||
}
|
||||
|
||||
Ledger.from_json = function(v) {
|
||||
var ledger = new Ledger();
|
||||
const ledger = new Ledger();
|
||||
ledger.parse_json(v);
|
||||
return ledger;
|
||||
};
|
||||
@@ -23,14 +23,13 @@ Ledger.space = require('./ledgerspaces');
|
||||
/**
|
||||
* Generate the key for an AccountRoot entry.
|
||||
*
|
||||
* @param {String|UInt160} account Ripple Account
|
||||
* @param {String|UInt160} accountArg - Ripple Account
|
||||
* @return {UInt256}
|
||||
*/
|
||||
Ledger.calcAccountRootEntryHash =
|
||||
Ledger.prototype.calcAccountRootEntryHash = function(account) {
|
||||
account = UInt160.from_json(account);
|
||||
|
||||
var index = new SerializedObject();
|
||||
Ledger.prototype.calcAccountRootEntryHash = function(accountArg) {
|
||||
const account = UInt160.from_json(accountArg);
|
||||
const index = new SerializedObject();
|
||||
|
||||
index.append([0, Ledger.space.account.charCodeAt(0)]);
|
||||
index.append(account.to_bytes());
|
||||
@@ -41,17 +40,15 @@ Ledger.prototype.calcAccountRootEntryHash = function(account) {
|
||||
/**
|
||||
* Generate the key for an Offer entry.
|
||||
*
|
||||
* @param {String|UInt160} account Ripple Account
|
||||
* @param {Number} sequence Sequence number of the OfferCreate transaction
|
||||
* @param {String|UInt160} accountArg - Ripple Account
|
||||
* @param {Number} sequence - Sequence number of the OfferCreate transaction
|
||||
* that instantiated this offer.
|
||||
* @return {UInt256}
|
||||
*/
|
||||
Ledger.calcOfferEntryHash =
|
||||
Ledger.prototype.calcOfferEntryHash = function(account, sequence) {
|
||||
account = UInt160.from_json(account);
|
||||
sequence = parseInt(sequence, 10);
|
||||
|
||||
var index = new SerializedObject();
|
||||
Ledger.prototype.calcOfferEntryHash = function(accountArg, sequence) {
|
||||
const account = UInt160.from_json(accountArg);
|
||||
const index = new SerializedObject();
|
||||
|
||||
index.append([0, Ledger.space.offer.charCodeAt(0)]);
|
||||
index.append(account.to_bytes());
|
||||
@@ -65,17 +62,17 @@ Ledger.prototype.calcOfferEntryHash = function(account, sequence) {
|
||||
*
|
||||
* The ordering of the two account parameters does not matter.
|
||||
*
|
||||
* @param {String|UInt160} account1 First Ripple Account
|
||||
* @param {String|UInt160} account2 Second Ripple Account
|
||||
* @param {String|Currency} currency The currency code
|
||||
* @param {String|UInt160} _account1 - First Ripple Account
|
||||
* @param {String|UInt160} _account2 - Second Ripple Account
|
||||
* @param {String|Currency} _currency - The currency code
|
||||
* @return {UInt256}
|
||||
*/
|
||||
Ledger.calcRippleStateEntryHash =
|
||||
Ledger.prototype.calcRippleStateEntryHash = function(
|
||||
account1, account2, currency) {
|
||||
currency = Currency.from_json(currency);
|
||||
account1 = UInt160.from_json(account1);
|
||||
account2 = UInt160.from_json(account2);
|
||||
_account1, _account2, _currency) {
|
||||
const currency = Currency.from_json(_currency);
|
||||
const account1 = UInt160.from_json(_account1);
|
||||
const account2 = UInt160.from_json(_account2);
|
||||
|
||||
if (!account1.is_valid()) {
|
||||
throw new Error('Invalid first account');
|
||||
@@ -87,18 +84,14 @@ Ledger.prototype.calcRippleStateEntryHash = function(
|
||||
throw new Error('Invalid currency');
|
||||
}
|
||||
|
||||
// The lower ID has to come first
|
||||
if (account1.to_bn().greaterEquals(account2.to_bn())) {
|
||||
var tmp = account2;
|
||||
account2 = account1;
|
||||
account1 = tmp;
|
||||
}
|
||||
|
||||
var index = new SerializedObject();
|
||||
const swap = account1.greater_than(account2);
|
||||
const lowAccount = swap ? account2 : account1;
|
||||
const highAccount = swap ? account1 : account2;
|
||||
const index = new SerializedObject();
|
||||
|
||||
index.append([0, Ledger.space.rippleState.charCodeAt(0)]);
|
||||
index.append(account1.to_bytes());
|
||||
index.append(account2.to_bytes());
|
||||
index.append(lowAccount.to_bytes());
|
||||
index.append(highAccount.to_bytes());
|
||||
index.append(currency.to_bytes());
|
||||
|
||||
return index.hash();
|
||||
@@ -109,13 +102,13 @@ Ledger.prototype.parse_json = function(v) {
|
||||
};
|
||||
|
||||
Ledger.prototype.calc_tx_hash = function() {
|
||||
var tx_map = new SHAMap();
|
||||
const tx_map = new SHAMap();
|
||||
|
||||
this.ledger_json.transactions.forEach(function(tx_json) {
|
||||
var tx = Transaction.from_json(tx_json);
|
||||
var meta = SerializedObject.from_json(tx_json.metaData);
|
||||
const tx = Transaction.from_json(tx_json);
|
||||
const meta = SerializedObject.from_json(tx_json.metaData);
|
||||
|
||||
var data = new SerializedObject();
|
||||
const data = new SerializedObject();
|
||||
stypes.VariableLength.serialize(data, tx.serialize().to_hex());
|
||||
stypes.VariableLength.serialize(data, meta.to_hex());
|
||||
tx_map.add_item(tx.hash(), data, SHAMapTreeNode.TYPE_TRANSACTION_MD);
|
||||
@@ -125,22 +118,23 @@ Ledger.prototype.calc_tx_hash = function() {
|
||||
};
|
||||
|
||||
/**
|
||||
* @param options .sanity_test {Boolean}
|
||||
* @return hash of shamap
|
||||
* @param {Object} options - object
|
||||
*
|
||||
* If `true`, will serialize each accountState item to binary and then back to
|
||||
* json before finally serializing for hashing. This is mostly to expose any
|
||||
* issues with ripple-lib's binary <--> json codecs.
|
||||
* @param {Boolean} [options.sanity_test=false] - If `true`, will serialize each
|
||||
* accountState item to binary and then back to json before finally
|
||||
* serializing for hashing. This is mostly to expose any issues with
|
||||
* ripple-lib's binary <--> json codecs.
|
||||
*
|
||||
* @return {UInt256} - hash of shamap
|
||||
*/
|
||||
Ledger.prototype.calc_account_hash = function(options) {
|
||||
var account_map = new SHAMap();
|
||||
var erred;
|
||||
const account_map = new SHAMap();
|
||||
let erred;
|
||||
|
||||
this.ledger_json.accountState.forEach(function(le) {
|
||||
var data = SerializedObject.from_json(le);
|
||||
let data = SerializedObject.from_json(le);
|
||||
|
||||
var json;
|
||||
let json;
|
||||
if (options && options.sanity_test) {
|
||||
try {
|
||||
json = data.to_json();
|
||||
@@ -163,4 +157,24 @@ Ledger.prototype.calc_account_hash = function(options) {
|
||||
return account_map.hash();
|
||||
};
|
||||
|
||||
// see rippled Ledger::updateHash()
|
||||
Ledger.calculateLedgerHash =
|
||||
Ledger.prototype.calculateLedgerHash = function(ledgerHeader) {
|
||||
const so = new SerializedObject();
|
||||
const prefix = 0x4C575200;
|
||||
const totalCoins = (new BigNumber(ledgerHeader.total_coins)).toString(16);
|
||||
|
||||
stypes.Int32.serialize(so, Number(ledgerHeader.ledger_index));
|
||||
stypes.Int64.serialize(so, totalCoins);
|
||||
stypes.Hash256.serialize(so, ledgerHeader.parent_hash);
|
||||
stypes.Hash256.serialize(so, ledgerHeader.transaction_hash);
|
||||
stypes.Hash256.serialize(so, ledgerHeader.account_hash);
|
||||
stypes.Int32.serialize(so, ledgerHeader.parent_close_time);
|
||||
stypes.Int32.serialize(so, ledgerHeader.close_time);
|
||||
stypes.Int8.serialize(so, ledgerHeader.close_time_resolution);
|
||||
stypes.Int8.serialize(so, ledgerHeader.close_flags);
|
||||
|
||||
return so.hash(prefix).to_hex();
|
||||
};
|
||||
|
||||
exports.Ledger = Ledger;
|
||||
|
||||
@@ -62,9 +62,9 @@ function OrderBook(remote,
|
||||
this._ownerFunds = {};
|
||||
this._ownerOffersTotal = {};
|
||||
|
||||
// We consider ourselves synchronized if we have a current
|
||||
// We consider ourselves synced if we have a current
|
||||
// copy of the offers, we are online and subscribed to updates
|
||||
this._synchronized = false;
|
||||
this._synced = false;
|
||||
|
||||
// Transfer rate of the taker gets currency issuer
|
||||
this._issuerTransferRate = null;
|
||||
@@ -278,7 +278,6 @@ OrderBook.prototype.requestOffers = function(callback=function() {}) {
|
||||
}
|
||||
|
||||
self.setOffers(res.offers);
|
||||
self._synchronized = true;
|
||||
self.notifyDirectOffersChanged();
|
||||
|
||||
callback(null, self._offers);
|
||||
@@ -293,7 +292,8 @@ OrderBook.prototype.requestOffers = function(callback=function() {}) {
|
||||
callback(err);
|
||||
}
|
||||
|
||||
const request = this._remote.requestBookOffers(this.toJSON());
|
||||
const requestOptions = _.merge({}, this.toJSON(), {ledger: 'validated'});
|
||||
const request = this._remote.requestBookOffers(requestOptions);
|
||||
request.once('success', handleOffers);
|
||||
request.once('error', handleError);
|
||||
request.request();
|
||||
@@ -408,7 +408,7 @@ OrderBook.prototype.resetCache = function() {
|
||||
this._ownerFunds = {};
|
||||
this._ownerOffersTotal = {};
|
||||
this._offerCounts = {};
|
||||
this._synchronized = false;
|
||||
this._synced = false;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -754,9 +754,15 @@ OrderBook.prototype.updateFundedAmounts = function(transaction) {
|
||||
log.info('waiting for transfer rate');
|
||||
}
|
||||
|
||||
this.requestTransferRate(function() {
|
||||
// Defer until transfer rate is requested
|
||||
self.updateFundedAmounts(transaction);
|
||||
this.requestTransferRate(function(err) {
|
||||
if (err) {
|
||||
log.error(
|
||||
'Failed to request transfer rate, will not update funded amounts: '
|
||||
+ err.toString());
|
||||
} else {
|
||||
// Defer until transfer rate is requested
|
||||
self.updateFundedAmounts(transaction);
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
@@ -842,7 +848,7 @@ OrderBook.prototype.updateOwnerOffersFundedAmount = function(account) {
|
||||
OrderBook.prototype.notify = function(transaction) {
|
||||
const self = this;
|
||||
|
||||
if (!this._subscribed) {
|
||||
if (!(this._subscribed && this._synced)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1083,6 +1089,7 @@ OrderBook.prototype.setOffers = function(offers) {
|
||||
});
|
||||
|
||||
this._offers = newOffers;
|
||||
this._synced = true;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -1101,7 +1108,7 @@ OrderBook.prototype.offers =
|
||||
OrderBook.prototype.getOffers = function(callback) {
|
||||
assert.strictEqual(typeof callback, 'function', 'Callback missing');
|
||||
|
||||
if (this._synchronized) {
|
||||
if (this._synced) {
|
||||
callback(null, this._offers);
|
||||
} else {
|
||||
this.once('model', function(m) {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
var EventEmitter = require('events').EventEmitter;
|
||||
var util = require('util');
|
||||
var Amount = require('./amount').Amount;
|
||||
var extend = require('extend');
|
||||
'use strict';
|
||||
const EventEmitter = require('events').EventEmitter;
|
||||
const util = require('util');
|
||||
const Amount = require('./amount').Amount;
|
||||
|
||||
/**
|
||||
* Represents a persistent path finding request.
|
||||
@@ -10,16 +10,18 @@ var extend = require('extend');
|
||||
* find request is triggered it will supercede the existing one, making it emit
|
||||
* the 'end' and 'superceded' events.
|
||||
*/
|
||||
function PathFind(remote, src_account, dst_account, dst_amount, src_currencies) {
|
||||
|
||||
function PathFind(remote, src_account, dst_account, dst_amount, src_currencies
|
||||
) {
|
||||
EventEmitter.call(this);
|
||||
|
||||
this.remote = remote;
|
||||
|
||||
this.src_account = src_account;
|
||||
this.dst_account = dst_account;
|
||||
this.dst_amount = dst_amount;
|
||||
this.src_account = src_account;
|
||||
this.dst_account = dst_account;
|
||||
this.dst_amount = dst_amount;
|
||||
this.src_currencies = src_currencies;
|
||||
};
|
||||
}
|
||||
|
||||
util.inherits(PathFind, EventEmitter);
|
||||
|
||||
@@ -32,14 +34,16 @@ util.inherits(PathFind, EventEmitter);
|
||||
* so you should only have to call it if the path find was closed or superceded
|
||||
* and you wish to restart it.
|
||||
*/
|
||||
PathFind.prototype.create = function () {
|
||||
var self = this;
|
||||
|
||||
var req = this.remote.request_path_find_create(
|
||||
this.src_account,
|
||||
this.dst_account,
|
||||
this.dst_amount,
|
||||
this.src_currencies);
|
||||
PathFind.prototype.create = function() {
|
||||
const self = this;
|
||||
|
||||
const req = this.remote.requestPathFindCreate({
|
||||
source_account: this.src_account,
|
||||
destination_account: this.dst_account,
|
||||
destination_amount: this.dst_amount,
|
||||
source_currencies: this.src_currencies
|
||||
});
|
||||
|
||||
req.once('error', function(err) {
|
||||
self.emit('error', err);
|
||||
@@ -54,27 +58,28 @@ PathFind.prototype.create = function () {
|
||||
req.broadcast().request();
|
||||
};
|
||||
|
||||
PathFind.prototype.close = function () {
|
||||
this.remote.request_path_find_close().broadcast().request();
|
||||
PathFind.prototype.close = function() {
|
||||
this.removeAllListeners('update');
|
||||
this.remote.requestPathFindClose().broadcast().request();
|
||||
this.emit('end');
|
||||
this.emit('close');
|
||||
};
|
||||
|
||||
PathFind.prototype.notify_update = function (message) {
|
||||
var src_account = message.source_account;
|
||||
var dst_account = message.destination_account;
|
||||
var dst_amount = Amount.from_json(message.destination_amount);
|
||||
PathFind.prototype.notify_update = function(message) {
|
||||
const src_account = message.source_account;
|
||||
const dst_account = message.destination_account;
|
||||
const dst_amount = Amount.from_json(message.destination_amount);
|
||||
|
||||
// Only pass the event along if this path find response matches what we were
|
||||
// looking for.
|
||||
if (this.src_account === src_account &&
|
||||
this.dst_account === dst_account &&
|
||||
this.dst_amount.equals(dst_amount)) {
|
||||
dst_amount.equals(this.dst_amount)) {
|
||||
this.emit('update', message);
|
||||
}
|
||||
};
|
||||
|
||||
PathFind.prototype.notify_superceded = function () {
|
||||
PathFind.prototype.notify_superceded = function() {
|
||||
// XXX If we're set to re-subscribe whenever we connect to a new server, then
|
||||
// we should cancel that behavior here. See PathFind#create.
|
||||
|
||||
|
||||
@@ -45,7 +45,7 @@ class RangeSet {
|
||||
const rangeStrings = rangesString.split(',');
|
||||
_.forEach(rangeStrings, rangeString => {
|
||||
const range = rangeString.split('-').map(Number);
|
||||
this.addRange(range[0], range[1]);
|
||||
this.addRange(range[0], range.length === 1 ? range[0] : range[1]);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -65,6 +65,7 @@ function Remote(options = {}) {
|
||||
this._transaction_listeners = 0;
|
||||
this._received_tx = new LRU({max: 100});
|
||||
this._cur_path_find = null;
|
||||
this._queued_path_finds = [];
|
||||
|
||||
if (this.local_signing) {
|
||||
// Local signing implies local fees and sequences
|
||||
@@ -90,7 +91,7 @@ function Remote(options = {}) {
|
||||
this._books = { };
|
||||
|
||||
// Secrets that we know about.
|
||||
// Secrets can be set by calling set_secret(account, secret).
|
||||
// Secrets can be set by calling setSecret(account, secret).
|
||||
// account : secret
|
||||
this.secrets = { };
|
||||
|
||||
@@ -205,7 +206,7 @@ Remote.TRANSACTION_EVENTS = [
|
||||
'transaction_all'
|
||||
];
|
||||
|
||||
// Flags for ledger entries. In support of account_root().
|
||||
// Flags for ledger entries. In support of accountRoot().
|
||||
Remote.flags = {
|
||||
// AccountRoot
|
||||
account_root: {
|
||||
@@ -469,7 +470,7 @@ Remote.prototype.disconnect = function(callback_) {
|
||||
server.disconnect();
|
||||
});
|
||||
|
||||
this._set_state('offline');
|
||||
this._setState('offline');
|
||||
|
||||
return this;
|
||||
};
|
||||
@@ -744,20 +745,18 @@ Remote.prototype.getServer = function() {
|
||||
* @param {Request} request
|
||||
*/
|
||||
|
||||
Remote.prototype.request = function(request_) {
|
||||
let request = request_;
|
||||
|
||||
Remote.prototype.request = function(request) {
|
||||
if (typeof request === 'string') {
|
||||
if (!/^request_/.test(request)) {
|
||||
request = 'request_' + request;
|
||||
}
|
||||
const prefix = /^request_/.test(request) ? '' : 'request_';
|
||||
const requestName = prefix + request;
|
||||
const methodName = requestName.replace(/(\_\w)/g, m => m[1].toUpperCase());
|
||||
|
||||
if (typeof this[request] === 'function') {
|
||||
if (typeof this[methodName] === 'function') {
|
||||
const args = _.slice(arguments, 1);
|
||||
return this[request].apply(this, args);
|
||||
return this[methodName].apply(this, args);
|
||||
}
|
||||
|
||||
throw new Error('Command does not exist: ' + request);
|
||||
throw new Error('Command does not exist: ' + requestName);
|
||||
}
|
||||
|
||||
if (!(request instanceof Request)) {
|
||||
@@ -1748,8 +1747,8 @@ Remote.prototype._serverPrepareSubscribe = function(server, callback_) {
|
||||
const request = this.requestSubscribe(feeds);
|
||||
|
||||
function serverSubscribed(message) {
|
||||
self._stand_alone = !!message.stand_alone;
|
||||
self._testnet = !!message.testnet;
|
||||
self._stand_alone = Boolean(message.stand_alone);
|
||||
self._testnet = Boolean(message.testnet);
|
||||
self._handleLedgerClosed(message, server);
|
||||
self.emit('subscribed');
|
||||
}
|
||||
@@ -1936,9 +1935,14 @@ Remote.prototype.findAccount = function(accountID) {
|
||||
* @return {PathFind}
|
||||
*/
|
||||
|
||||
function createPathFind(options_) {
|
||||
function createPathFind(options_, callback) {
|
||||
const options = {};
|
||||
|
||||
if (this._cur_path_find !== null) {
|
||||
this._queued_path_finds.push({options: options_, callback: callback});
|
||||
return null;
|
||||
}
|
||||
|
||||
if (_.isPlainObject(options_)) {
|
||||
_.merge(options, options_);
|
||||
} else {
|
||||
@@ -1957,10 +1961,21 @@ function createPathFind(options_) {
|
||||
this._cur_path_find.notify_superceded();
|
||||
}
|
||||
|
||||
pathFind.create();
|
||||
if (callback) {
|
||||
pathFind.on('update', (data) => {
|
||||
if (data.full_reply) {
|
||||
pathFind.close();
|
||||
callback(null, data);
|
||||
}
|
||||
});
|
||||
pathFind.on('error', (error) => {
|
||||
pathFind.close();
|
||||
callback(error);
|
||||
});
|
||||
}
|
||||
|
||||
this._cur_path_find = pathFind;
|
||||
|
||||
pathFind.create();
|
||||
return pathFind;
|
||||
}
|
||||
|
||||
@@ -2279,7 +2294,7 @@ Remote.prototype.requestRipplePathFind = function(options_, callback_) {
|
||||
destination_account: options_.dst_account,
|
||||
destination_amount: options_.dst_amount,
|
||||
source_currencies: options_.src_currencies
|
||||
}, options_);
|
||||
}, options_);
|
||||
} else {
|
||||
_.merge(options, makeOptions(
|
||||
'ripple_path_find',
|
||||
@@ -2327,7 +2342,7 @@ Remote.prototype.requestPathFindCreate = function(options_, callback_) {
|
||||
destination_account: options_.dst_account,
|
||||
destination_amount: options_.dst_amount,
|
||||
source_currencies: options_.src_currencies
|
||||
}, options_);
|
||||
}, options_);
|
||||
} else {
|
||||
_.merge(options, makeOptions(
|
||||
'path_find',
|
||||
@@ -2371,6 +2386,11 @@ Remote.prototype.requestPathFindClose = function(callback) {
|
||||
|
||||
request.message.subcommand = 'close';
|
||||
request.callback(callback);
|
||||
this._cur_path_find = null;
|
||||
if (this._queued_path_finds.length > 0) {
|
||||
const pathfind = this._queued_path_finds.shift();
|
||||
this.createPathFind(pathfind.options, pathfind.callback);
|
||||
}
|
||||
|
||||
return request;
|
||||
};
|
||||
@@ -2486,7 +2506,7 @@ Remote.prototype.createTransaction = function(type, options = {}) {
|
||||
TrustSet: transaction.trustSet,
|
||||
OfferCreate: transaction.offerCreate,
|
||||
OfferCancel: transaction.offerCancel,
|
||||
SetRegularkey: transaction.setRegularKey
|
||||
SetRegularKey: transaction.setRegularKey
|
||||
};
|
||||
|
||||
const transactionConstructor = constructorMap[type];
|
||||
|
||||
149
src/core/seed.js
149
src/core/seed.js
@@ -4,18 +4,18 @@
|
||||
// Seed support
|
||||
//
|
||||
|
||||
const {KeyPair, KeyType} = require('ripple-keypairs');
|
||||
const {decodeSeed, encodeSeed} = require('ripple-address-codec');
|
||||
const extend = require('extend');
|
||||
const utils = require('./utils');
|
||||
const sjcl = utils.sjcl;
|
||||
const sjclcodec = require('sjcl-codec');
|
||||
const BN = require('bn.js');
|
||||
const hashjs = require('hash.js');
|
||||
|
||||
const Base = require('./base').Base;
|
||||
const UInt = require('./uint').UInt;
|
||||
const UInt160 = require('./uint160').UInt160;
|
||||
const KeyPair = require('./keypair').KeyPair;
|
||||
|
||||
const Seed = extend(function() {
|
||||
this._curve = sjcl.ecc.curves.k256;
|
||||
this._value = NaN;
|
||||
this._type = KeyType.secp256k1;
|
||||
}, UInt);
|
||||
|
||||
Seed.width = 16;
|
||||
@@ -28,14 +28,15 @@ Seed.prototype.parse_json = function(j) {
|
||||
if (typeof j === 'string') {
|
||||
if (!j.length) {
|
||||
this._value = NaN;
|
||||
// XXX Should actually always try and continue if it failed.
|
||||
} else if (j[0] === 's') {
|
||||
this._value = Base.decode_check(Base.VER_FAMILY_SEED, j);
|
||||
} else if (/^[0-9a-fA-f]{32}$/.test(j)) {
|
||||
this.parse_hex(j);
|
||||
// XXX Should also try 1751
|
||||
} else {
|
||||
this.parse_passphrase(j);
|
||||
this.parse_base58(j);
|
||||
if (!this.is_valid()) {
|
||||
this.parse_hex(j);
|
||||
// XXX Should also try 1751
|
||||
}
|
||||
if (!this.is_valid() && j[0] !== 's') {
|
||||
this.parse_passphrase(j);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
this._value = NaN;
|
||||
@@ -44,15 +45,37 @@ Seed.prototype.parse_json = function(j) {
|
||||
return this;
|
||||
};
|
||||
|
||||
Seed.prototype.parse_base58 = function(j) {
|
||||
if (typeof j !== 'string') {
|
||||
throw new Error('Value must be a string');
|
||||
}
|
||||
if (!j.length || j[0] !== 's') {
|
||||
this._value = NaN;
|
||||
} else {
|
||||
try {
|
||||
const {bytes, type} = decodeSeed(j);
|
||||
this._value = new BN(bytes);
|
||||
this._type = type;
|
||||
} catch (e) {
|
||||
this._value = NaN;
|
||||
}
|
||||
}
|
||||
return this;
|
||||
};
|
||||
|
||||
Seed.prototype.set_ed25519 = function() {
|
||||
this._type = KeyType.ed25519;
|
||||
return this;
|
||||
};
|
||||
|
||||
Seed.prototype.parse_passphrase = function(j) {
|
||||
if (typeof j !== 'string') {
|
||||
throw new Error('Passphrase must be a string');
|
||||
}
|
||||
|
||||
const hash = sjcl.hash.sha512.hash(sjcl.codec.utf8String.toBits(j));
|
||||
const bits = sjcl.bitArray.bitSlice(hash, 0, 128);
|
||||
|
||||
this.parse_bits(bits);
|
||||
const phraseBytes = sjclcodec.bytes.fromBits(sjclcodec.utf8String.toBits(j));
|
||||
const hash = hashjs.sha512().update(phraseBytes).digest();
|
||||
this.parse_bytes(hash.slice(0, 16));
|
||||
|
||||
return this;
|
||||
};
|
||||
@@ -61,100 +84,14 @@ Seed.prototype.to_json = function() {
|
||||
if (!(this.is_valid())) {
|
||||
return NaN;
|
||||
}
|
||||
|
||||
const output = Base.encode_check(Base.VER_FAMILY_SEED, this.to_bytes());
|
||||
|
||||
return output;
|
||||
return encodeSeed(this.to_bytes(), this._type);
|
||||
};
|
||||
|
||||
function append_int(a, i) {
|
||||
return [].concat(a, i >> 24, (i >> 16) & 0xff, (i >> 8) & 0xff, i & 0xff);
|
||||
}
|
||||
|
||||
function firstHalfOfSHA512(bytes) {
|
||||
return sjcl.bitArray.bitSlice(
|
||||
sjcl.hash.sha512.hash(sjcl.codec.bytes.toBits(bytes)),
|
||||
0, 256
|
||||
);
|
||||
}
|
||||
|
||||
// Removed a `*` so this JSDoc-ish syntax is ignored.
|
||||
// This will soon all change anyway.
|
||||
/*
|
||||
* @param account
|
||||
* {undefined} take first, default, KeyPair
|
||||
*
|
||||
* {Number} specifies the account number of the KeyPair
|
||||
* desired.
|
||||
*
|
||||
* {Uint160} (from_json able), specifies the address matching the KeyPair
|
||||
* that is desired.
|
||||
*
|
||||
* @param maxLoops (optional)
|
||||
* {Number} specifies the amount of attempts taken
|
||||
* to generate a matching KeyPair
|
||||
*
|
||||
*/
|
||||
Seed.prototype.get_key = function(account, maxLoops) {
|
||||
let account_number = 0, address;
|
||||
let max_loops = maxLoops || 1;
|
||||
|
||||
Seed.prototype.get_key = function() {
|
||||
if (!this.is_valid()) {
|
||||
throw new Error('Cannot generate keys from invalid seed!');
|
||||
}
|
||||
if (account) {
|
||||
if (typeof account === 'number') {
|
||||
account_number = account;
|
||||
max_loops = account_number + 1;
|
||||
} else {
|
||||
address = UInt160.from_json(account);
|
||||
}
|
||||
}
|
||||
|
||||
let private_gen, public_gen;
|
||||
const curve = this._curve;
|
||||
let i = 0;
|
||||
|
||||
do {
|
||||
private_gen = sjcl.bn.fromBits(
|
||||
firstHalfOfSHA512(append_int(this.to_bytes(), i)));
|
||||
i++;
|
||||
} while (!curve.r.greaterEquals(private_gen));
|
||||
|
||||
public_gen = curve.G.mult(private_gen);
|
||||
|
||||
let sec;
|
||||
let key_pair;
|
||||
|
||||
do {
|
||||
|
||||
i = 0;
|
||||
|
||||
do {
|
||||
sec = sjcl.bn.fromBits(
|
||||
firstHalfOfSHA512(
|
||||
append_int(
|
||||
append_int(public_gen.toBytesCompressed(), account_number)
|
||||
,
|
||||
i
|
||||
)));
|
||||
i++;
|
||||
} while (!curve.r.greaterEquals(sec));
|
||||
|
||||
account_number++;
|
||||
sec = sec.add(private_gen).mod(curve.r);
|
||||
key_pair = KeyPair.from_bn_secret(sec);
|
||||
|
||||
if (max_loops-- <= 0) {
|
||||
// We are almost certainly looking for an account that would take same
|
||||
// value of $too_long {forever, ...}
|
||||
throw new Error('Too many loops looking for KeyPair yielding ' +
|
||||
address.to_json() + ' from ' + this.to_json());
|
||||
}
|
||||
|
||||
} while (address && !key_pair.get_address().equals(address));
|
||||
|
||||
return key_pair;
|
||||
return KeyPair.fromSeed(this.to_bytes(), this._type);
|
||||
};
|
||||
|
||||
exports.Seed = Seed;
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
'use strict';
|
||||
|
||||
const _ = require('lodash');
|
||||
const assert = require('assert');
|
||||
const extend = require('extend');
|
||||
const BN = require('bn.js');
|
||||
const hashjs = require('hash.js');
|
||||
const sjclcodec = require('sjcl-codec');
|
||||
const binformat = require('./binformat');
|
||||
const stypes = require('./serializedtypes');
|
||||
const utils = require('./utils');
|
||||
const UInt256 = require('./uint256').UInt256;
|
||||
|
||||
const sjcl = utils.sjcl;
|
||||
|
||||
const TRANSACTION_TYPES = { };
|
||||
|
||||
Object.keys(binformat.tx).forEach(function(key) {
|
||||
@@ -28,20 +28,16 @@ Object.keys(binformat.ter).forEach(function(key) {
|
||||
TRANSACTION_RESULTS[binformat.ter[key]] = key;
|
||||
});
|
||||
|
||||
function normalize_sjcl_bn_hex(string) {
|
||||
const hex = string.slice(2); // remove '0x' prefix
|
||||
// now strip leading zeros
|
||||
const i = _.findIndex(hex, function(c) {
|
||||
return c !== '0';
|
||||
});
|
||||
return i >= 0 ? hex.slice(i) : '0';
|
||||
function fieldType(fieldName) {
|
||||
const fieldDef = binformat.fieldsInverseMap[fieldName];
|
||||
return binformat.types[fieldDef[0]];
|
||||
}
|
||||
|
||||
function SerializedObject(buf) {
|
||||
if (Array.isArray(buf) || (Buffer && Buffer.isBuffer(buf))) {
|
||||
this.buffer = buf;
|
||||
} else if (typeof buf === 'string') {
|
||||
this.buffer = sjcl.codec.bytes.fromBits(sjcl.codec.hex.toBits(buf));
|
||||
this.buffer = sjclcodec.bytes.fromBits(sjclcodec.hex.toBits(buf));
|
||||
} else if (!buf) {
|
||||
this.buffer = [];
|
||||
} else {
|
||||
@@ -205,11 +201,11 @@ SerializedObject.prototype.read = readOrPeek(true);
|
||||
SerializedObject.prototype.peek = readOrPeek(false);
|
||||
|
||||
SerializedObject.prototype.to_bits = function() {
|
||||
return sjcl.codec.bytes.toBits(this.buffer);
|
||||
return sjclcodec.bytes.toBits(this.buffer);
|
||||
};
|
||||
|
||||
SerializedObject.prototype.to_hex = function() {
|
||||
return sjcl.codec.hex.fromBits(this.to_bits()).toUpperCase();
|
||||
return sjclcodec.hex.fromBits(this.to_bits()).toUpperCase();
|
||||
};
|
||||
|
||||
SerializedObject.prototype.to_json = function() {
|
||||
@@ -231,12 +227,12 @@ SerializedObject.prototype.to_json = function() {
|
||||
return output;
|
||||
};
|
||||
|
||||
SerializedObject.jsonify_structure = function(structure, field_name) {
|
||||
SerializedObject.jsonify_structure = function(structure, fieldName) {
|
||||
let output;
|
||||
|
||||
switch (typeof structure) {
|
||||
case 'number':
|
||||
switch (field_name) {
|
||||
switch (fieldName) {
|
||||
case 'LedgerEntryType':
|
||||
output = LEDGER_ENTRY_TYPES[structure];
|
||||
break;
|
||||
@@ -257,11 +253,10 @@ SerializedObject.jsonify_structure = function(structure, field_name) {
|
||||
|
||||
if (typeof structure.to_json === 'function') {
|
||||
output = structure.to_json();
|
||||
} else if (structure instanceof sjcl.bn) {
|
||||
output = ('0000000000000000' +
|
||||
normalize_sjcl_bn_hex(structure.toString())
|
||||
.toUpperCase()
|
||||
).slice(-16);
|
||||
} else if (structure instanceof BN) {
|
||||
// We assume that any BN is a UInt64 field
|
||||
assert.equal(fieldType(fieldName), 'Int64');
|
||||
output = utils.arrayToHex(structure.toArray('bn', 8));
|
||||
} else {
|
||||
// new Array or Object
|
||||
output = new structure.constructor();
|
||||
@@ -307,11 +302,9 @@ SerializedObject.prototype.hash = function(prefix) {
|
||||
|
||||
// Copy buffer to temporary buffer
|
||||
sign_buffer.append(this.buffer);
|
||||
const bytes = hashjs.sha512().update(sign_buffer.buffer).digest();
|
||||
|
||||
const bits = sjcl.codec.bytes.toBits(sign_buffer.buffer);
|
||||
const sha512hex = sjcl.codec.hex.fromBits(sjcl.hash.sha512.hash(bits));
|
||||
|
||||
return UInt256.from_hex(sha512hex.substr(0, 64).toUpperCase());
|
||||
return UInt256.from_bytes(bytes.slice(0, 32));
|
||||
};
|
||||
|
||||
// DEPRECATED
|
||||
|
||||
@@ -10,13 +10,13 @@
|
||||
|
||||
const assert = require('assert');
|
||||
const extend = require('extend');
|
||||
const BN = require('bn.js');
|
||||
const GlobalBigNumber = require('bignumber.js');
|
||||
const sjclcodec = require('sjcl-codec');
|
||||
const Amount = require('./amount').Amount;
|
||||
const Currency = require('./currency').Currency;
|
||||
const binformat = require('./binformat');
|
||||
const utils = require('./utils');
|
||||
const sjcl = utils.sjcl;
|
||||
const SJCL_BN = sjcl.bn;
|
||||
|
||||
const UInt128 = require('./uint128').UInt128;
|
||||
const UInt160 = require('./uint160').UInt160;
|
||||
@@ -44,8 +44,7 @@ function isHexInt64String(val) {
|
||||
return isString(val) && /^[0-9A-F]{0,16}$/i.test(val);
|
||||
}
|
||||
|
||||
function serializeBits(so, bits, noLength) {
|
||||
const byteData = sjcl.codec.bytes.fromBits(bits);
|
||||
function serializeBytes(so, byteData, noLength) {
|
||||
if (!noLength) {
|
||||
SerializedType.serialize_varint(so, byteData.length);
|
||||
}
|
||||
@@ -53,23 +52,12 @@ function serializeBits(so, bits, noLength) {
|
||||
}
|
||||
|
||||
function serializeHex(so, hexData, noLength) {
|
||||
serializeBits(so, sjcl.codec.hex.toBits(hexData), noLength);
|
||||
}
|
||||
|
||||
/**
|
||||
* parses bytes as hex
|
||||
*
|
||||
* @param {Array} byte_array bytes
|
||||
* @return {String} hex string
|
||||
*/
|
||||
function convertByteArrayToHex(byte_array) {
|
||||
return sjcl.codec.hex.fromBits(sjcl.codec.bytes.toBits(byte_array))
|
||||
.toUpperCase();
|
||||
serializeBytes(so, utils.hexToArray(hexData), noLength);
|
||||
}
|
||||
|
||||
function convertHexToString(hexString) {
|
||||
const bits = sjcl.codec.hex.toBits(hexString);
|
||||
return sjcl.codec.utf8String.fromBits(bits);
|
||||
const bits = sjclcodec.hex.toBits(hexString);
|
||||
return sjclcodec.utf8String.fromBits(bits);
|
||||
}
|
||||
|
||||
function sort_fields(keys) {
|
||||
@@ -308,25 +296,27 @@ const STInt64 = exports.Int64 = new SerializedType({
|
||||
if (value < 0) {
|
||||
throw new Error('Negative value for unsigned Int64 is invalid.');
|
||||
}
|
||||
bigNumObject = new SJCL_BN(value, 10);
|
||||
bigNumObject = new BN(value, 10);
|
||||
} else if (isString(value)) {
|
||||
if (!isHexInt64String(value)) {
|
||||
throw new Error('Not a valid hex Int64.');
|
||||
}
|
||||
bigNumObject = new SJCL_BN(value, 16);
|
||||
} else if (value instanceof SJCL_BN) {
|
||||
if (!value.greaterEquals(0)) {
|
||||
bigNumObject = new BN(value, 16);
|
||||
} else if (value instanceof BN) {
|
||||
if (value.cmpn(0) < 0) {
|
||||
throw new Error('Negative value for unsigned Int64 is invalid.');
|
||||
}
|
||||
bigNumObject = value;
|
||||
} else {
|
||||
throw new Error('Invalid type for Int64');
|
||||
throw new Error('Invalid type for Int64: ' + (typeof value) + ' value');
|
||||
}
|
||||
serializeBits(so, bigNumObject.toBits(64), true); // noLength = true
|
||||
// `'be'` means big endian, and the following arg is the byte length, which
|
||||
// it will pad with 0s to if not enough bytes, or throw if over
|
||||
serializeBytes(so, bigNumObject.toArray('be', 8), /* noLength= */true);
|
||||
},
|
||||
parse: function(so) {
|
||||
const bytes = so.read(8);
|
||||
return SJCL_BN.fromBits(sjcl.codec.bytes.toBits(bytes));
|
||||
return new BN(bytes);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -338,7 +328,7 @@ const STHash128 = exports.Hash128 = new SerializedType({
|
||||
if (!hash.is_valid()) {
|
||||
throw new Error('Invalid Hash128');
|
||||
}
|
||||
serializeBits(so, hash.to_bits(), true); // noLength = true
|
||||
serializeBytes(so, hash.to_bytes(), true); // noLength = true
|
||||
},
|
||||
parse: function(so) {
|
||||
return UInt128.from_bytes(so.read(16));
|
||||
@@ -353,7 +343,7 @@ const STHash256 = exports.Hash256 = new SerializedType({
|
||||
if (!hash.is_valid()) {
|
||||
throw new Error('Invalid Hash256');
|
||||
}
|
||||
serializeBits(so, hash.to_bits(), true); // noLength = true
|
||||
serializeBytes(so, hash.to_bytes(), true); // noLength = true
|
||||
},
|
||||
parse: function(so) {
|
||||
return UInt256.from_bytes(so.read(32));
|
||||
@@ -368,7 +358,7 @@ const STHash160 = exports.Hash160 = new SerializedType({
|
||||
if (!hash.is_valid()) {
|
||||
throw new Error('Invalid Hash160');
|
||||
}
|
||||
serializeBits(so, hash.to_bits(), true); // noLength = true
|
||||
serializeBytes(so, hash.to_bytes(), true); // noLength = true
|
||||
},
|
||||
parse: function(so) {
|
||||
return UInt160.from_bytes(so.read(20));
|
||||
@@ -442,7 +432,7 @@ exports.Quality = new SerializedType({
|
||||
lo = parseInt(mantissaHex.slice(-8), 16);
|
||||
}
|
||||
|
||||
const valueBytes = sjcl.codec.bytes.fromBits([hi, lo]);
|
||||
const valueBytes = sjclcodec.bytes.fromBits([hi, lo]);
|
||||
|
||||
so.append(valueBytes);
|
||||
}
|
||||
@@ -482,7 +472,7 @@ const STAmount = exports.Amount = new SerializedType({
|
||||
valueHex = '0' + valueHex;
|
||||
}
|
||||
|
||||
valueBytes = sjcl.codec.bytes.fromBits(sjcl.codec.hex.toBits(valueHex));
|
||||
valueBytes = sjclcodec.bytes.fromBits(sjclcodec.hex.toBits(valueHex));
|
||||
// Clear most significant two bits - these bits should already be 0 if
|
||||
// Amount enforces the range correctly, but we'll clear them anyway just
|
||||
// so this code can make certain guarantees about the encoded value.
|
||||
@@ -516,7 +506,7 @@ const STAmount = exports.Amount = new SerializedType({
|
||||
lo = parseInt(mantissaHex.slice(-8), 16);
|
||||
}
|
||||
|
||||
valueBytes = sjcl.codec.bytes.fromBits([hi, lo]);
|
||||
valueBytes = sjclcodec.bytes.fromBits([hi, lo]);
|
||||
}
|
||||
|
||||
so.append(valueBytes);
|
||||
@@ -582,7 +572,7 @@ const STVL = exports.VariableLength = exports.VL = new SerializedType({
|
||||
},
|
||||
parse: function(so) {
|
||||
const len = this.parse_varint(so);
|
||||
return convertByteArrayToHex(so.read(len));
|
||||
return utils.arrayToHex(so.read(len));
|
||||
}
|
||||
});
|
||||
|
||||
@@ -594,7 +584,7 @@ const STAccount = exports.Account = new SerializedType({
|
||||
if (!account.is_valid()) {
|
||||
throw new Error('Invalid account!');
|
||||
}
|
||||
serializeBits(so, account.to_bits());
|
||||
serializeBytes(so, account.to_bytes());
|
||||
},
|
||||
parse: function(so) {
|
||||
const len = this.parse_varint(so);
|
||||
@@ -807,27 +797,27 @@ exports.STMemo = new SerializedType({
|
||||
if (parsedType !== 'unformatted_memo') {
|
||||
output.parsed_memo_type = parsedType;
|
||||
}
|
||||
/*eslint-disable no-empty*/
|
||||
/* eslint-disable no-empty */
|
||||
} catch (e) {
|
||||
// empty
|
||||
// we don't know what's in the binary, apparently it's not a UTF-8
|
||||
// string
|
||||
// this is fine, we won't add the parsed_memo_type field
|
||||
}
|
||||
/*eslint-enable no-empty*/
|
||||
/* eslint-enable no-empty */
|
||||
}
|
||||
|
||||
if (output.MemoFormat !== undefined) {
|
||||
try {
|
||||
output.parsed_memo_format = convertHexToString(output.MemoFormat);
|
||||
/*eslint-disable no-empty*/
|
||||
/* eslint-disable no-empty */
|
||||
} catch (e) {
|
||||
// empty
|
||||
// we don't know what's in the binary, apparently it's not a UTF-8
|
||||
// string
|
||||
// this is fine, we won't add the parsed_memo_format field
|
||||
}
|
||||
/*eslint-enable no-empty*/
|
||||
/* eslint-enable no-empty */
|
||||
}
|
||||
|
||||
if (output.MemoData !== undefined) {
|
||||
@@ -842,7 +832,7 @@ exports.STMemo = new SerializedType({
|
||||
// otherwise see if we can parse text
|
||||
output.parsed_memo_data = convertHexToString(output.MemoData);
|
||||
}
|
||||
/*eslint-disable no-empty*/
|
||||
/* eslint-disable no-empty */
|
||||
} catch(e) {
|
||||
// empty
|
||||
// we'll fail in case the content does not match what the MemoFormat
|
||||
@@ -850,7 +840,7 @@ exports.STMemo = new SerializedType({
|
||||
// this is fine, we won't add the parsed_memo_data, the user has to
|
||||
// parse themselves
|
||||
}
|
||||
/*eslint-enable no-empty*/
|
||||
/* eslint-enable no-empty */
|
||||
}
|
||||
|
||||
so.read(1);
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
'use strict';
|
||||
|
||||
|
||||
const _ = require('lodash');
|
||||
const assert = require('assert');
|
||||
const util = require('util');
|
||||
const url = require('url');
|
||||
const HttpsProxyAgent = require('https-proxy-agent');
|
||||
const LRU = require('lru-cache');
|
||||
const EventEmitter = require('events').EventEmitter;
|
||||
const RippleError = require('./').RippleError;
|
||||
const RippleError = require('./rippleerror').RippleError;
|
||||
const Amount = require('./amount').Amount;
|
||||
const RangeSet = require('./rangeset').RangeSet;
|
||||
const log = require('./log').internal.sub('server');
|
||||
@@ -621,7 +623,7 @@ Server.prototype._handleLedgerClosed = function(message) {
|
||||
Server.prototype._handleServerStatus = function(message) {
|
||||
// This message is only received when online.
|
||||
// As we are connected, it is the definitive final state.
|
||||
const isOnline = ~Server.onlineStates.indexOf(message.server_status);
|
||||
const isOnline = _.includes(Server.onlineStates, message.server_status);
|
||||
|
||||
this._setState(isOnline ? 'online' : 'offline');
|
||||
|
||||
@@ -732,7 +734,7 @@ Server.prototype._handleResponseSubscribe = function(message) {
|
||||
this._ledgerRanges.parseAndAddRanges(message.validated_ledgers);
|
||||
}
|
||||
|
||||
if (~Server.onlineStates.indexOf(message.server_status)) {
|
||||
if (_.includes(Server.onlineStates, message.server_status)) {
|
||||
this._setState('online');
|
||||
}
|
||||
};
|
||||
@@ -905,6 +907,9 @@ Server.prototype._feeTxUnit = function() {
|
||||
*/
|
||||
|
||||
Server.prototype._reserve = function(ownerCount) {
|
||||
// We should be in a valid state before calling this method
|
||||
assert(this._reserve_base && this._reserve_inc);
|
||||
|
||||
const reserve_base = Amount.from_json(String(this._reserve_base));
|
||||
const reserve_inc = Amount.from_json(String(this._reserve_inc));
|
||||
const owner_count = ownerCount || 0;
|
||||
@@ -913,7 +918,7 @@ Server.prototype._reserve = function(ownerCount) {
|
||||
throw new Error('Owner count must not be negative.');
|
||||
}
|
||||
|
||||
return reserve_base.add(reserve_inc.product_human(owner_count));
|
||||
return reserve_base.add(reserve_inc.multiply(owner_count));
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -4,7 +4,7 @@ const util = require('util');
|
||||
const lodash = require('lodash');
|
||||
const EventEmitter = require('events').EventEmitter;
|
||||
const utils = require('./utils');
|
||||
const sjcl = require('./utils').sjcl;
|
||||
const sjclcodec = require('sjcl-codec');
|
||||
const Amount = require('./amount').Amount;
|
||||
const Currency = require('./amount').Currency;
|
||||
const UInt160 = require('./amount').UInt160;
|
||||
@@ -399,8 +399,8 @@ Transaction.prototype.complete = function() {
|
||||
if (typeof this.tx_json.SigningPubKey === 'undefined') {
|
||||
try {
|
||||
const seed = Seed.from_json(this._secret);
|
||||
const key = seed.get_key(this.tx_json.Account);
|
||||
this.tx_json.SigningPubKey = key.to_hex_pub();
|
||||
const key = seed.get_key();
|
||||
this.tx_json.SigningPubKey = key.pubKeyHex();
|
||||
} catch(e) {
|
||||
this.emit('error', new RippleError(
|
||||
'tejSecretInvalid', 'Invalid secret'));
|
||||
@@ -469,13 +469,13 @@ Transaction.prototype.hash = function(prefix_, asUINT256, serialized) {
|
||||
return asUINT256 ? hash : hash.to_hex();
|
||||
};
|
||||
|
||||
Transaction.prototype.sign = function(testnet) {
|
||||
Transaction.prototype.sign = function() {
|
||||
const seed = Seed.from_json(this._secret);
|
||||
const prev_sig = this.tx_json.TxnSignature;
|
||||
|
||||
delete this.tx_json.TxnSignature;
|
||||
|
||||
const hash = this.signingHash(testnet);
|
||||
const hash = this.signingHash();
|
||||
|
||||
// If the hash is the same, we can re-use the previous signature
|
||||
if (prev_sig && hash === this.previousSigningHash) {
|
||||
@@ -483,10 +483,8 @@ Transaction.prototype.sign = function(testnet) {
|
||||
return this;
|
||||
}
|
||||
|
||||
const key = seed.get_key(this.tx_json.Account);
|
||||
const sig = key.sign(hash);
|
||||
const hex = sjcl.codec.hex.fromBits(sig).toUpperCase();
|
||||
|
||||
const key = seed.get_key();
|
||||
const hex = key.signHex(this.signingData().buffer);
|
||||
this.tx_json.TxnSignature = hex;
|
||||
this.previousSigningHash = hash;
|
||||
|
||||
@@ -781,8 +779,8 @@ Transaction.prototype.addMemo = function(options_) {
|
||||
}
|
||||
|
||||
function convertStringToHex(string) {
|
||||
const utf8String = sjcl.codec.utf8String.toBits(string);
|
||||
return sjcl.codec.hex.fromBits(utf8String).toUpperCase();
|
||||
const utf8String = sjclcodec.utf8String.toBits(string);
|
||||
return sjclcodec.hex.fromBits(utf8String).toUpperCase();
|
||||
}
|
||||
|
||||
const memo = {};
|
||||
@@ -892,10 +890,6 @@ Transaction.prototype.accountSet = function(options_) {
|
||||
};
|
||||
|
||||
Transaction.prototype.setAccountSetFlag = function(name, value) {
|
||||
// if (this.getType() !== 'AccountSet') {
|
||||
// throw new Error('TransactionType must be AccountSet to use ' + name);
|
||||
// }
|
||||
|
||||
const accountSetFlags = Transaction.set_clear_flags.AccountSet;
|
||||
let flagValue = value;
|
||||
|
||||
@@ -930,12 +924,6 @@ Transaction.prototype.setClearFlag = function(flag) {
|
||||
|
||||
Transaction.prototype.setTransferRate =
|
||||
Transaction.prototype.transferRate = function(rate) {
|
||||
/* eslint-disable max-len */
|
||||
// if (this.getType() !== 'AccountSet') {
|
||||
// throw new Error('TransactionType must be AccountSet to use TransferRate');
|
||||
// }
|
||||
/* eslint-enable max-len */
|
||||
|
||||
const transferRate = rate;
|
||||
|
||||
if (transferRate === 0) {
|
||||
@@ -1037,26 +1025,14 @@ Transaction.prototype.rippleLineSet = function(options_) {
|
||||
};
|
||||
|
||||
Transaction.prototype.setLimit = function(amount) {
|
||||
// if (this.getType() !== 'TrustSet') {
|
||||
// throw new Error('TransactionType must be TrustSet to use LimitAmount');
|
||||
// }
|
||||
|
||||
return this._setAmount('LimitAmount', amount, {no_native: true});
|
||||
};
|
||||
|
||||
Transaction.prototype.setQualityIn = function(quality) {
|
||||
// if (this.getType() !== 'TrustSet') {
|
||||
// throw new Error('TransactionType must be TrustSet to use QualityIn');
|
||||
// }
|
||||
|
||||
return this._setUInt32('QualityIn', quality);
|
||||
};
|
||||
|
||||
Transaction.prototype.setQualityOut = function(quality) {
|
||||
// if (this.getType() !== 'TrustSet') {
|
||||
// throw new Error('TransactionType must be TrustSet to use QualityOut');
|
||||
// }
|
||||
|
||||
return this._setUInt32('QualityOut', quality);
|
||||
};
|
||||
|
||||
@@ -1106,18 +1082,10 @@ Transaction.prototype.payment = function(options_) {
|
||||
};
|
||||
|
||||
Transaction.prototype.setAmount = function(amount) {
|
||||
// if (this.getType() !== 'Payment') {
|
||||
// throw new Error('TransactionType must be Payment to use SendMax');
|
||||
// }
|
||||
|
||||
return this._setAmount('Amount', amount);
|
||||
};
|
||||
|
||||
Transaction.prototype.setDestination = function(destination) {
|
||||
// if (this.getType() !== 'Payment') {
|
||||
// throw new Error('TransactionType must be Payment to use Destination');
|
||||
// }
|
||||
|
||||
return this._setAccount('Destination', destination);
|
||||
};
|
||||
|
||||
@@ -1129,13 +1097,19 @@ Transaction.prototype.setDestination = function(destination) {
|
||||
|
||||
Transaction.prototype.setSendMax =
|
||||
Transaction.prototype.sendMax = function(send_max) {
|
||||
// if (this.getType() !== 'Payment') {
|
||||
// throw new Error('TransactionType must be Payment to use SendMax');
|
||||
// }
|
||||
|
||||
return this._setAmount('SendMax', send_max);
|
||||
};
|
||||
|
||||
/**
|
||||
* Set DeliverMin for Payment
|
||||
*
|
||||
* @param {String|Object} deliver_min minimum amount to deliver
|
||||
*/
|
||||
|
||||
Transaction.prototype.setDeliverMin = function(deliver_min) {
|
||||
return this._setAmount('DeliverMin', deliver_min);
|
||||
};
|
||||
|
||||
/**
|
||||
* Filter invalid properties from path objects in a path array
|
||||
*
|
||||
@@ -1186,9 +1160,6 @@ Transaction.prototype.pathAdd = function(path) {
|
||||
if (!Array.isArray(path)) {
|
||||
throw new Error('Path must be an array');
|
||||
}
|
||||
// if (this.getType() !== 'Payment') {
|
||||
// throw new Error('TransactionType must be Payment to use Paths');
|
||||
// }
|
||||
|
||||
this.tx_json.Paths = this.tx_json.Paths || [];
|
||||
this.tx_json.Paths.push(Transaction._rewritePath(path));
|
||||
@@ -1207,9 +1178,10 @@ Transaction.prototype.paths = function(paths) {
|
||||
if (!Array.isArray(paths)) {
|
||||
throw new Error('Paths must be an array');
|
||||
}
|
||||
// if (this.getType() !== 'Payment') {
|
||||
// throw new Error('TransactionType must be Payment to use Paths');
|
||||
// }
|
||||
|
||||
if (paths.length === 0) {
|
||||
return this;
|
||||
}
|
||||
|
||||
this.tx_json.Paths = [];
|
||||
paths.forEach(this.addPath, this);
|
||||
@@ -1228,10 +1200,6 @@ Transaction.prototype.paths = function(paths) {
|
||||
|
||||
Transaction.prototype.setBuildPath =
|
||||
Transaction.prototype.buildPath = function(build) {
|
||||
// if (this.getType() !== 'Payment') {
|
||||
// throw new Error('TransactionType must be Payment to use build_path');
|
||||
// }
|
||||
|
||||
this._build_path = build === undefined || build;
|
||||
|
||||
return this;
|
||||
@@ -1245,10 +1213,6 @@ Transaction.prototype.buildPath = function(build) {
|
||||
|
||||
Transaction.prototype.setDestinationTag =
|
||||
Transaction.prototype.destinationTag = function(tag) {
|
||||
// if (this.getType() !== 'Payment') {
|
||||
// throw new Error('TransactionType must be Payment to use DestinationTag');
|
||||
// }
|
||||
|
||||
return this._setUInt32('DestinationTag', tag);
|
||||
};
|
||||
|
||||
@@ -1260,10 +1224,6 @@ Transaction.prototype.destinationTag = function(tag) {
|
||||
|
||||
Transaction.prototype.setInvoiceID =
|
||||
Transaction.prototype.invoiceID = function(id) {
|
||||
// if (this.getType() !== 'Payment') {
|
||||
// throw new Error('TransactionType must be Payment to use InvoiceID');
|
||||
// }
|
||||
|
||||
return this._setHash256('InvoiceID', id, {pad: true});
|
||||
};
|
||||
|
||||
@@ -1321,26 +1281,14 @@ Transaction.prototype.offerCreate = function(options_) {
|
||||
};
|
||||
|
||||
Transaction.prototype.setTakerGets = function(amount) {
|
||||
// if (this.getType() !== 'OfferCreate') {
|
||||
// throw new Error('TransactionType must be OfferCreate to use TakerGets');
|
||||
// }
|
||||
|
||||
return this._setAmount('TakerGets', amount);
|
||||
};
|
||||
|
||||
Transaction.prototype.setTakerPays = function(amount) {
|
||||
// if (this.getType() !== 'OfferCreate') {
|
||||
// throw new Error('TransactionType must be OfferCreate to use TakerPays');
|
||||
// }
|
||||
|
||||
return this._setAmount('TakerPays', amount);
|
||||
};
|
||||
|
||||
Transaction.prototype.setExpiration = function(expiration) {
|
||||
// if (this.getType() !== 'OfferCreate') {
|
||||
// throw new Error('TransactionType must be OfferCreate to use Expiration');
|
||||
// }
|
||||
|
||||
const timeOffset = expiration instanceof Date
|
||||
? expiration.getTime()
|
||||
: expiration;
|
||||
@@ -1349,14 +1297,6 @@ Transaction.prototype.setExpiration = function(expiration) {
|
||||
};
|
||||
|
||||
Transaction.prototype.setOfferSequence = function(offerSequence) {
|
||||
/* eslint-disable max-len */
|
||||
// if (!/^Offer(Cancel|Create)$/.test(this.getType())) {
|
||||
// throw new Error(
|
||||
// 'TransactionType must be OfferCreate or OfferCancel to use OfferSequence'
|
||||
// );
|
||||
// }
|
||||
/* eslint-enable max-len */
|
||||
|
||||
return this._setUInt32('OfferSequence', offerSequence);
|
||||
};
|
||||
|
||||
@@ -1423,13 +1363,6 @@ Transaction.prototype.submit = function(callback) {
|
||||
return this;
|
||||
}
|
||||
|
||||
/* eslint-disable max-len */
|
||||
// if (this.state !== 'unsubmitted') {
|
||||
// this.emit('error', new Error('Attempt to submit transaction more than once'));
|
||||
// return;
|
||||
// }
|
||||
/* eslint-enable max-len */
|
||||
|
||||
this.getManager().submit(this);
|
||||
|
||||
return this;
|
||||
|
||||
@@ -524,7 +524,7 @@ TransactionManager.prototype._prepareRequest = function(tx) {
|
||||
tx.sign();
|
||||
|
||||
const serialized = tx.serialize();
|
||||
submitRequest.tx_blob(serialized.to_hex());
|
||||
submitRequest.txBlob(serialized.to_hex());
|
||||
|
||||
const hash = tx.hash(null, null, serialized);
|
||||
tx.addId(hash);
|
||||
@@ -534,9 +534,9 @@ TransactionManager.prototype._prepareRequest = function(tx) {
|
||||
// sealed and delivered, and the txn unmodified.
|
||||
// TODO: perhaps an exception should be raised if build_path is attempted
|
||||
// while local signing
|
||||
submitRequest.build_path(tx._build_path);
|
||||
submitRequest.buildPath(tx._build_path);
|
||||
submitRequest.secret(tx._secret);
|
||||
submitRequest.tx_json(tx.tx_json);
|
||||
submitRequest.txJson(tx.tx_json);
|
||||
}
|
||||
|
||||
return submitRequest;
|
||||
@@ -728,8 +728,7 @@ TransactionManager.prototype.submit = function(tx) {
|
||||
|
||||
if (typeof tx.tx_json.Sequence !== 'number') {
|
||||
// Honor manually-set sequences
|
||||
this._nextSequence += 1;
|
||||
tx.tx_json.Sequence = this._nextSequence;
|
||||
tx.tx_json.Sequence = this._nextSequence++;
|
||||
}
|
||||
|
||||
tx.once('cleanup', function() {
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
'use strict';
|
||||
|
||||
/*eslint new-cap: 1*/
|
||||
/* eslint new-cap: 1 */
|
||||
|
||||
const assert = require('assert');
|
||||
const lodash = require('lodash');
|
||||
const sjclcodec = require('sjcl-codec');
|
||||
const utils = require('./utils');
|
||||
const sjcl = utils.sjcl;
|
||||
const BN = require('bn.js');
|
||||
|
||||
//
|
||||
// Abstract UInt class
|
||||
@@ -14,7 +15,7 @@ const sjcl = utils.sjcl;
|
||||
//
|
||||
|
||||
function UInt() {
|
||||
// Internal form: NaN or sjcl.bn
|
||||
// Internal form: NaN or BN
|
||||
this._value = NaN;
|
||||
}
|
||||
|
||||
@@ -67,15 +68,6 @@ UInt.from_bytes = function(j) {
|
||||
return (new this()).parse_bytes(j);
|
||||
};
|
||||
|
||||
// Return a new UInt from j.
|
||||
UInt.from_bn = function(j) {
|
||||
if (j instanceof this) {
|
||||
return j.clone();
|
||||
}
|
||||
|
||||
return (new this()).parse_bn(j);
|
||||
};
|
||||
|
||||
// Return a new UInt from j.
|
||||
UInt.from_number = function(j) {
|
||||
if (j instanceof this) {
|
||||
@@ -108,16 +100,33 @@ UInt.prototype.copyTo = function(d) {
|
||||
return d;
|
||||
};
|
||||
|
||||
UInt.prototype.equals = function(d) {
|
||||
return this.is_valid() && d.is_valid() && this._value.equals(d._value);
|
||||
UInt.prototype.equals = function(o) {
|
||||
return this.is_valid() &&
|
||||
o.is_valid() &&
|
||||
// This throws but the expression will short circuit
|
||||
this.cmp(o) === 0;
|
||||
};
|
||||
|
||||
UInt.prototype.cmp = function(o) {
|
||||
assert(this.is_valid() && o.is_valid());
|
||||
return this._value.cmp(o._value);
|
||||
};
|
||||
|
||||
UInt.prototype.greater_than = function(o) {
|
||||
return this.cmp(o) > 0;
|
||||
};
|
||||
|
||||
UInt.prototype.less_than = function(o) {
|
||||
return this.cmp(o) < 0;
|
||||
};
|
||||
|
||||
UInt.prototype.is_valid = function() {
|
||||
return this._value instanceof sjcl.bn;
|
||||
return this._value instanceof BN;
|
||||
};
|
||||
|
||||
UInt.prototype.is_zero = function() {
|
||||
return this.is_valid() && this._value.equals(new sjcl.bn(0));
|
||||
// cmpn means cmp with N)umber
|
||||
return this.is_valid() && this._value.cmpn(0) === 0;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -150,14 +159,14 @@ UInt.prototype.parse_generic = function(j) {
|
||||
case subclass.STR_ZERO:
|
||||
case subclass.ACCOUNT_ZERO:
|
||||
case subclass.HEX_ZERO:
|
||||
this._value = new sjcl.bn(0);
|
||||
this._value = new BN(0);
|
||||
break;
|
||||
|
||||
case '1':
|
||||
case subclass.STR_ONE:
|
||||
case subclass.ACCOUNT_ONE:
|
||||
case subclass.HEX_ONE:
|
||||
this._value = new sjcl.bn(1);
|
||||
this._value = new BN(1);
|
||||
break;
|
||||
|
||||
default:
|
||||
@@ -165,7 +174,7 @@ UInt.prototype.parse_generic = function(j) {
|
||||
switch (j.length) {
|
||||
case subclass.width:
|
||||
const hex = utils.arrayToHex(utils.stringToArray(j));
|
||||
this._value = new sjcl.bn(hex, 16);
|
||||
this._value = new BN(hex, 16);
|
||||
break;
|
||||
case subclass.width * 2:
|
||||
// Assume hex, check char set
|
||||
@@ -187,7 +196,7 @@ UInt.prototype.parse_generic = function(j) {
|
||||
|
||||
UInt.prototype.parse_hex = function(j) {
|
||||
if (new RegExp(`^[0-9A-Fa-f]{${this.constructor.width * 2}}$`).test(j)) {
|
||||
this._value = new sjcl.bn(j, 16);
|
||||
this._value = new BN(j, 16);
|
||||
} else {
|
||||
this._value = NaN;
|
||||
}
|
||||
@@ -198,24 +207,12 @@ UInt.prototype.parse_hex = function(j) {
|
||||
};
|
||||
|
||||
UInt.prototype.parse_bits = function(j) {
|
||||
if (sjcl.bitArray.bitLength(j) === this.constructor.width * 8) {
|
||||
this._value = sjcl.bn.fromBits(j);
|
||||
// let bytes = sjcl.codec.bytes.fromBits(j);
|
||||
// this.parse_bytes(bytes);
|
||||
} else {
|
||||
this._value = NaN;
|
||||
}
|
||||
|
||||
this._update();
|
||||
|
||||
return this;
|
||||
return this.parse_bytes(sjclcodec.bytes.fromBits(j));
|
||||
};
|
||||
|
||||
|
||||
UInt.prototype.parse_bytes = function(j) {
|
||||
if (Array.isArray(j) && j.length === this.constructor.width) {
|
||||
const bits = sjcl.codec.bytes.toBits(j);
|
||||
this._value = sjcl.bn.fromBits(bits);
|
||||
this._value = new BN(j);
|
||||
} else {
|
||||
this._value = NaN;
|
||||
}
|
||||
@@ -225,26 +222,13 @@ UInt.prototype.parse_bytes = function(j) {
|
||||
return this;
|
||||
};
|
||||
|
||||
|
||||
UInt.prototype.parse_json = UInt.prototype.parse_hex;
|
||||
|
||||
UInt.prototype.parse_bn = function(j) {
|
||||
if ((j instanceof sjcl.bn) && j.bitLength() <= this.constructor.width * 8) {
|
||||
this._value = new sjcl.bn(j);
|
||||
} else {
|
||||
this._value = NaN;
|
||||
}
|
||||
|
||||
this._update();
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
UInt.prototype.parse_number = function(j) {
|
||||
this._value = NaN;
|
||||
|
||||
if (typeof j === 'number' && isFinite(j) && j >= 0) {
|
||||
this._value = new sjcl.bn(j);
|
||||
this._value = new BN(j);
|
||||
}
|
||||
|
||||
this._update();
|
||||
@@ -258,7 +242,7 @@ UInt.prototype.to_bytes = function() {
|
||||
return null;
|
||||
}
|
||||
|
||||
return sjcl.codec.bytes.fromBits(this.to_bits());
|
||||
return this._value.toArray('be', this.constructor.width);
|
||||
};
|
||||
|
||||
UInt.prototype.to_hex = function() {
|
||||
@@ -266,27 +250,18 @@ UInt.prototype.to_hex = function() {
|
||||
return null;
|
||||
}
|
||||
|
||||
return sjcl.codec.hex.fromBits(this.to_bits()).toUpperCase();
|
||||
return utils.arrayToHex(this.to_bytes());
|
||||
};
|
||||
|
||||
UInt.prototype.to_json = UInt.prototype.to_hex;
|
||||
|
||||
// Convert from internal form.
|
||||
UInt.prototype.to_bits = function() {
|
||||
if (!this.is_valid()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return this._value.toBits(this.constructor.width * 8);
|
||||
};
|
||||
|
||||
UInt.prototype.to_bn = function() {
|
||||
if (!this.is_valid()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const bits = this.to_bits();
|
||||
|
||||
return sjcl.bn.fromBits(bits);
|
||||
return sjclcodec.bytes.toBits(this.to_bytes());
|
||||
};
|
||||
|
||||
exports.UInt = UInt;
|
||||
|
||||
@@ -77,7 +77,7 @@ function hexToArray(h) {
|
||||
|
||||
function arrayToHex(a) {
|
||||
return a.map(function(byteValue) {
|
||||
const hex = byteValue.toString(16);
|
||||
const hex = byteValue.toString(16).toUpperCase();
|
||||
return hex.length > 1 ? hex : '0' + hex;
|
||||
}).join('');
|
||||
}
|
||||
@@ -166,6 +166,4 @@ exports.fromTimestamp = fromTimestamp;
|
||||
exports.getMantissaDecimalString = getMantissaDecimalString;
|
||||
exports.getMantissa16FromString = getMantissa16FromString;
|
||||
|
||||
exports.sjcl = require('sjcl-extended');
|
||||
|
||||
// vim:sw=2:sts=2:ts=8:et
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
'use strict';
|
||||
const _ = require('lodash');
|
||||
const core = require('./core');
|
||||
const RippleAPI = require('./api');
|
||||
|
||||
module.exports = _.assign({}, core, {RippleAPI: RippleAPI});
|
||||
module.exports = {
|
||||
RippleAPI,
|
||||
_DEPRECATED: core // WARNING: this will be removed soon
|
||||
};
|
||||
|
||||
1
src/js
1
src/js
Submodule src/js deleted from 16dde36fa2
816
test/api-test.js
816
test/api-test.js
@@ -2,15 +2,20 @@
|
||||
'use strict';
|
||||
const _ = require('lodash');
|
||||
const assert = require('assert-diff');
|
||||
const path = require('path');
|
||||
const setupAPI = require('./setup-api');
|
||||
const RippleAPI = require('ripple-api').RippleAPI;
|
||||
const common = RippleAPI._PRIVATE.common;
|
||||
const fixtures = require('./fixtures/api');
|
||||
const requests = fixtures.requests;
|
||||
const responses = fixtures.responses;
|
||||
const addresses = require('./fixtures/addresses');
|
||||
const hashes = require('./fixtures/hashes');
|
||||
const MockPRNG = require('./mock-prng');
|
||||
const sjcl = require('../src').sjcl;
|
||||
const address = addresses.ACCOUNT;
|
||||
const validate = common.validate;
|
||||
const utils = RippleAPI._PRIVATE.ledgerUtils;
|
||||
const ledgerClosed = require('./fixtures/api/rippled/ledger-close-newer');
|
||||
const schemaValidator = RippleAPI._PRIVATE.schemaValidator;
|
||||
|
||||
const orderbook = {
|
||||
base: {
|
||||
@@ -23,21 +28,13 @@ const orderbook = {
|
||||
}
|
||||
};
|
||||
|
||||
function checkResult(expected, done, error, response) {
|
||||
if (error) {
|
||||
done(error);
|
||||
return;
|
||||
}
|
||||
function checkResult(expected, schemaName, response) {
|
||||
// console.log(JSON.stringify(response, null, 2));
|
||||
assert.deepEqual(response, expected);
|
||||
done();
|
||||
}
|
||||
|
||||
function withDeterministicPRNG(f) {
|
||||
const prng = sjcl.random;
|
||||
sjcl.random = new MockPRNG();
|
||||
f();
|
||||
sjcl.random = prng;
|
||||
if (schemaName) {
|
||||
schemaValidator.schemaValidate(schemaName, response);
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
describe('RippleAPI', function() {
|
||||
@@ -45,156 +42,406 @@ describe('RippleAPI', function() {
|
||||
beforeEach(setupAPI.setup);
|
||||
afterEach(setupAPI.teardown);
|
||||
|
||||
it('preparePayment', function(done) {
|
||||
this.api.preparePayment(address, requests.preparePayment, instructions,
|
||||
_.partial(checkResult, responses.preparePayment, done));
|
||||
it('preparePayment', function() {
|
||||
const localInstructions = _.defaults({
|
||||
maxFee: '0.000012'
|
||||
}, instructions);
|
||||
return this.api.preparePayment(
|
||||
address, requests.preparePayment, localInstructions).then(
|
||||
_.partial(checkResult, responses.preparePayment, 'tx'));
|
||||
});
|
||||
|
||||
it('preparePayment with all options specified', function(done) {
|
||||
this.api.preparePayment(address, requests.preparePaymentAllOptions,
|
||||
instructions,
|
||||
_.partial(checkResult, responses.preparePaymentAllOptions, done));
|
||||
it('preparePayment with all options specified', function() {
|
||||
const localInstructions = {
|
||||
maxLedgerVersion: this.api.getLedgerVersion() + 100,
|
||||
fee: '0.000012'
|
||||
};
|
||||
return this.api.preparePayment(
|
||||
address, requests.preparePaymentAllOptions, localInstructions).then(
|
||||
_.partial(checkResult, responses.preparePaymentAllOptions, 'tx'));
|
||||
});
|
||||
|
||||
it('preparePayment without counterparty set', function(done) {
|
||||
this.api.preparePayment(address, requests.preparePaymentNoCounterparty,
|
||||
instructions,
|
||||
_.partial(checkResult, responses.preparePaymentNoCounterparty, done));
|
||||
it('preparePayment without counterparty set', function() {
|
||||
const localInstructions = _.defaults({sequence: 23}, instructions);
|
||||
return this.api.preparePayment(
|
||||
address, requests.preparePaymentNoCounterparty, localInstructions).then(
|
||||
_.partial(checkResult, responses.preparePaymentNoCounterparty, 'tx'));
|
||||
});
|
||||
|
||||
it('prepareOrder - buy order', function(done) {
|
||||
this.api.prepareOrder(address, requests.prepareOrder, instructions,
|
||||
_.partial(checkResult, responses.prepareOrder, done));
|
||||
it('prepareOrder - buy order', function() {
|
||||
return this.api.prepareOrder(address, requests.prepareOrder, instructions)
|
||||
.then(_.partial(checkResult, responses.prepareOrder, 'tx'));
|
||||
});
|
||||
|
||||
it('prepareOrder - sell order', function(done) {
|
||||
this.api.prepareOrder(address, requests.prepareOrderSell, instructions,
|
||||
_.partial(checkResult, responses.prepareOrderSell, done));
|
||||
it('prepareOrder - sell order', function() {
|
||||
return this.api.prepareOrder(
|
||||
address, requests.prepareOrderSell, instructions).then(
|
||||
_.partial(checkResult, responses.prepareOrderSell, 'tx'));
|
||||
});
|
||||
|
||||
it('prepareOrderCancellation', function(done) {
|
||||
this.api.prepareOrderCancellation(address, 23, instructions,
|
||||
_.partial(checkResult, responses.prepareOrderCancellation, done));
|
||||
it('prepareOrderCancellation', function() {
|
||||
return this.api.prepareOrderCancellation(address, 23, instructions).then(
|
||||
_.partial(checkResult, responses.prepareOrderCancellation, 'tx'));
|
||||
});
|
||||
|
||||
it('prepareTrustline', function(done) {
|
||||
this.api.prepareTrustline(address, requests.prepareTrustline,
|
||||
instructions, _.partial(checkResult, responses.prepareTrustline, done));
|
||||
it('prepareTrustline - simple', function() {
|
||||
return this.api.prepareTrustline(
|
||||
address, requests.prepareTrustline.simple, instructions).then(
|
||||
_.partial(checkResult, responses.prepareTrustline.simple, 'tx'));
|
||||
});
|
||||
|
||||
it('prepareSettings', function(done) {
|
||||
this.api.prepareSettings(address, requests.prepareSettings, instructions,
|
||||
_.partial(checkResult, responses.prepareSettings.flags, done));
|
||||
it('prepareTrustline - complex', function() {
|
||||
return this.api.prepareTrustline(
|
||||
address, requests.prepareTrustline.complex, instructions).then(
|
||||
_.partial(checkResult, responses.prepareTrustline.complex, 'tx'));
|
||||
});
|
||||
|
||||
it('prepareSettings - regularKey', function(done) {
|
||||
it('prepareSettings', function() {
|
||||
return this.api.prepareSettings(
|
||||
address, requests.prepareSettings, instructions).then(
|
||||
_.partial(checkResult, responses.prepareSettings.flags, 'tx'));
|
||||
});
|
||||
|
||||
it('prepareSettings - regularKey', function() {
|
||||
const regularKey = {regularKey: 'rAR8rR8sUkBoCZFawhkWzY4Y5YoyuznwD'};
|
||||
this.api.prepareSettings(address, regularKey, instructions,
|
||||
_.partial(checkResult, responses.prepareSettings.regularKey, done));
|
||||
return this.api.prepareSettings(address, regularKey, instructions).then(
|
||||
_.partial(checkResult, responses.prepareSettings.regularKey, 'tx'));
|
||||
});
|
||||
|
||||
it('prepareSettings - flag set', function() {
|
||||
const settings = {requireDestinationTag: true};
|
||||
return this.api.prepareSettings(address, settings, instructions).then(
|
||||
_.partial(checkResult, responses.prepareSettings.flagSet, 'tx'));
|
||||
});
|
||||
|
||||
it('prepareSettings - flag clear', function() {
|
||||
const settings = {requireDestinationTag: false};
|
||||
return this.api.prepareSettings(address, settings, instructions).then(
|
||||
_.partial(checkResult, responses.prepareSettings.flagClear, 'tx'));
|
||||
});
|
||||
|
||||
it('prepareSettings - string field clear', function() {
|
||||
const settings = {walletLocator: null};
|
||||
return this.api.prepareSettings(address, settings, instructions).then(
|
||||
_.partial(checkResult, responses.prepareSettings.fieldClear, 'tx'));
|
||||
});
|
||||
|
||||
it('prepareSettings - integer field clear', function() {
|
||||
const settings = {walletSize: null};
|
||||
return this.api.prepareSettings(address, settings, instructions)
|
||||
.then(data => {
|
||||
assert(data);
|
||||
assert.strictEqual(data.WalletSize, 0);
|
||||
});
|
||||
});
|
||||
|
||||
it('prepareSettings - set transferRate', function() {
|
||||
const settings = {transferRate: 1};
|
||||
return this.api.prepareSettings(address, settings, instructions).then(
|
||||
_.partial(checkResult, responses.prepareSettings.setTransferRate, 'tx'));
|
||||
});
|
||||
|
||||
it('sign', function() {
|
||||
const secret = 'shsWGZcmZz6YsWWmcnpfr6fLTdtFV';
|
||||
withDeterministicPRNG(() => {
|
||||
const result = this.api.sign(requests.sign, secret);
|
||||
assert.deepEqual(result, responses.sign);
|
||||
const result = this.api.sign(requests.sign, secret);
|
||||
assert.deepEqual(result, responses.sign);
|
||||
schemaValidator.schemaValidate('sign', result);
|
||||
});
|
||||
|
||||
it('submit', function() {
|
||||
return this.api.submit(responses.sign.signedTransaction).then(
|
||||
_.partial(checkResult, responses.submit, 'submit'));
|
||||
});
|
||||
|
||||
it('getBalances', function() {
|
||||
return this.api.getBalances(address).then(
|
||||
_.partial(checkResult, responses.getBalances, 'getBalances'));
|
||||
});
|
||||
|
||||
it('getTransaction - payment', function() {
|
||||
return this.api.getTransaction(hashes.VALID_TRANSACTION_HASH).then(
|
||||
_.partial(checkResult, responses.getTransaction.payment,
|
||||
'getTransaction'));
|
||||
});
|
||||
|
||||
it('getTransaction - settings', function() {
|
||||
const hash =
|
||||
'4FB3ADF22F3C605E23FAEFAA185F3BD763C4692CAC490D9819D117CD33BFAA1B';
|
||||
return this.api.getTransaction(hash).then(
|
||||
_.partial(checkResult, responses.getTransaction.settings,
|
||||
'getTransaction'));
|
||||
});
|
||||
|
||||
it('getTransaction - order', function() {
|
||||
const hash =
|
||||
'10A6FB4A66EE80BED46AAE4815D7DC43B97E944984CCD5B93BCF3F8538CABC51';
|
||||
return this.api.getTransaction(hash).then(
|
||||
_.partial(checkResult, responses.getTransaction.order,
|
||||
'getTransaction'));
|
||||
});
|
||||
|
||||
it('getTransaction - order cancellation', function() {
|
||||
const hash =
|
||||
'809335DD3B0B333865096217AA2F55A4DF168E0198080B3A090D12D88880FF0E';
|
||||
return this.api.getTransaction(hash).then(
|
||||
_.partial(checkResult, responses.getTransaction.orderCancellation,
|
||||
'getTransaction'));
|
||||
});
|
||||
|
||||
it('getTransaction - trustline set', function() {
|
||||
const hash =
|
||||
'635A0769BD94710A1F6A76CDE65A3BC661B20B798807D1BBBDADCEA26420538D';
|
||||
return this.api.getTransaction(hash).then(
|
||||
_.partial(checkResult, responses.getTransaction.trustline,
|
||||
'getTransaction'));
|
||||
});
|
||||
|
||||
it('getTransaction - trustline frozen off', function() {
|
||||
const hash =
|
||||
'FE72FAD0FA7CA904FB6C633A1666EDF0B9C73B2F5A4555D37EEF2739A78A531B';
|
||||
return this.api.getTransaction(hash).then(
|
||||
_.partial(checkResult, responses.getTransaction.trustlineFrozenOff,
|
||||
'getTransaction'));
|
||||
});
|
||||
|
||||
it('getTransaction - trustline no quality', function() {
|
||||
const hash =
|
||||
'BAF1C678323C37CCB7735550C379287667D8288C30F83148AD3C1CB019FC9002';
|
||||
return this.api.getTransaction(hash).then(
|
||||
_.partial(checkResult, responses.getTransaction.trustlineNoQuality,
|
||||
'getTransaction'));
|
||||
});
|
||||
|
||||
it('getTransaction - not validated', function() {
|
||||
const hash =
|
||||
'4FB3ADF22F3C605E23FAEFAA185F3BD763C4692CAC490D9819D117CD33BFAA10';
|
||||
return this.api.getTransaction(hash).then(
|
||||
_.partial(checkResult, responses.getTransaction.notValidated,
|
||||
'getTransaction'));
|
||||
});
|
||||
|
||||
it('getTransaction - tracking on', function() {
|
||||
const hash =
|
||||
'8925FC8844A1E930E2CC76AD0A15E7665AFCC5425376D548BB1413F484C31B8C';
|
||||
return this.api.getTransaction(hash).then(
|
||||
_.partial(checkResult, responses.getTransaction.trackingOn,
|
||||
'getTransaction'));
|
||||
});
|
||||
|
||||
it('getTransaction - tracking off', function() {
|
||||
const hash =
|
||||
'C8C5E20DFB1BF533D0D81A2ED23F0A3CBD1EF2EE8A902A1D760500473CC9C582';
|
||||
return this.api.getTransaction(hash).then(
|
||||
_.partial(checkResult, responses.getTransaction.trackingOff,
|
||||
'getTransaction'));
|
||||
});
|
||||
|
||||
it('getTransaction - set regular key', function() {
|
||||
const hash =
|
||||
'278E6687C1C60C6873996210A6523564B63F2844FB1019576C157353B1813E60';
|
||||
return this.api.getTransaction(hash).then(
|
||||
_.partial(checkResult, responses.getTransaction.setRegularKey,
|
||||
'getTransaction'));
|
||||
});
|
||||
|
||||
it('getTransaction - not found in range', function() {
|
||||
const hash =
|
||||
'809335DD3B0B333865096217AA2F55A4DF168E0198080B3A090D12D88880FF0E';
|
||||
const options = {
|
||||
minLedgerVersion: 32570,
|
||||
maxLedgerVersion: 32571
|
||||
};
|
||||
return this.api.getTransaction(hash, options).then(() => {
|
||||
assert(false, 'Should throw NotFoundError');
|
||||
}).catch(error => {
|
||||
assert(error instanceof this.api.errors.NotFoundError);
|
||||
});
|
||||
});
|
||||
|
||||
it('submit', function(done) {
|
||||
this.api.submit(responses.sign.signedTransaction,
|
||||
_.partial(checkResult, responses.submit, done));
|
||||
it('getTransaction - not found by hash', function() {
|
||||
const hash = hashes.NOTFOUND_TRANSACTION_HASH;
|
||||
return this.api.getTransaction(hash).then(() => {
|
||||
assert(false, 'Should throw NotFoundError');
|
||||
}).catch(error => {
|
||||
assert(error instanceof this.api.errors.NotFoundError);
|
||||
});
|
||||
});
|
||||
|
||||
it('getBalances', function(done) {
|
||||
this.api.getBalances(address, {},
|
||||
_.partial(checkResult, responses.getBalances, done));
|
||||
it('getTransaction - missing ledger history', function() {
|
||||
const hash = hashes.NOTFOUND_TRANSACTION_HASH;
|
||||
// make gaps in history
|
||||
this.api.remote.getServer().emit('message', ledgerClosed);
|
||||
return this.api.getTransaction(hash).then(() => {
|
||||
assert(false, 'Should throw MissingLedgerHistoryError');
|
||||
}).catch(error => {
|
||||
assert(error instanceof this.api.errors.MissingLedgerHistoryError);
|
||||
});
|
||||
});
|
||||
|
||||
it('getTransaction - payment', function(done) {
|
||||
this.api.getTransaction(hashes.VALID_TRANSACTION_HASH, {},
|
||||
_.partial(checkResult, responses.getTransaction.payment, done));
|
||||
});
|
||||
|
||||
it('getTransaction - settings', function(done) {
|
||||
it('getTransaction - ledger_index not found', function() {
|
||||
const hash =
|
||||
'4FB3ADF22F3C605E23FAEFAA185F3BD763C4692CAC490D9819D117CD33BFAA1B';
|
||||
this.api.getTransaction(hash, {},
|
||||
_.partial(checkResult, responses.getTransaction.settings, done));
|
||||
'4FB3ADF22F3C605E23FAEFAA185F3BD763C4692CAC490D9819D117CD33BFAA11';
|
||||
return this.api.getTransaction(hash).then(() => {
|
||||
assert(false, 'Should throw NotFoundError');
|
||||
}).catch(error => {
|
||||
assert(error instanceof this.api.errors.NotFoundError);
|
||||
assert(error.message.indexOf('ledger_index') !== -1);
|
||||
});
|
||||
});
|
||||
|
||||
it('getTransaction - order', function(done) {
|
||||
it('getTransaction - transaction ledger not found', function() {
|
||||
const hash =
|
||||
'10A6FB4A66EE80BED46AAE4815D7DC43B97E944984CCD5B93BCF3F8538CABC51';
|
||||
this.api.getTransaction(hash, {},
|
||||
_.partial(checkResult, responses.getTransaction.order, done));
|
||||
'4FB3ADF22F3C605E23FAEFAA185F3BD763C4692CAC490D9819D117CD33BFAA12';
|
||||
return this.api.getTransaction(hash).then(() => {
|
||||
assert(false, 'Should throw NotFoundError');
|
||||
}).catch(error => {
|
||||
assert(error instanceof this.api.errors.NotFoundError);
|
||||
assert(error.message.indexOf('ledger not found') !== -1);
|
||||
});
|
||||
});
|
||||
|
||||
it('getTransaction - order cancellation', function(done) {
|
||||
it('getTransaction - ledger missing close time', function() {
|
||||
const hash =
|
||||
'809335DD3B0B333865096217AA2F55A4DF168E0198080B3A090D12D88880FF0E';
|
||||
this.api.getTransaction(hash, {},
|
||||
_.partial(checkResult, responses.getTransaction.orderCancellation, done));
|
||||
'0F7ED9F40742D8A513AE86029462B7A6768325583DF8EE21B7EC663019DD6A04';
|
||||
return this.api.getTransaction(hash).then(() => {
|
||||
assert(false, 'Should throw ApiError');
|
||||
}).catch(error => {
|
||||
assert(error instanceof this.api.errors.ApiError);
|
||||
});
|
||||
});
|
||||
|
||||
it('getTransaction - trustline set', function(done) {
|
||||
const hash =
|
||||
'635A0769BD94710A1F6A76CDE65A3BC661B20B798807D1BBBDADCEA26420538D';
|
||||
this.api.getTransaction(hash, {},
|
||||
_.partial(checkResult, responses.getTransaction.trustline, done));
|
||||
});
|
||||
|
||||
it('getTransactions', function(done) {
|
||||
it('getTransactions', function() {
|
||||
const options = {types: ['payment', 'order'], initiated: true, limit: 2};
|
||||
this.api.getTransactions(address, options,
|
||||
_.partial(checkResult, responses.getTransactions, done));
|
||||
return this.api.getTransactions(address, options).then(
|
||||
_.partial(checkResult, responses.getTransactions,
|
||||
'getTransactions'));
|
||||
});
|
||||
|
||||
it('getTransactions - earliest first', function() {
|
||||
const options = {types: ['payment', 'order'], initiated: true, limit: 2,
|
||||
earliestFirst: true
|
||||
};
|
||||
const expected = _.cloneDeep(responses.getTransactions)
|
||||
.sort(utils.compareTransactions);
|
||||
return this.api.getTransactions(address, options).then(
|
||||
_.partial(checkResult, expected, 'getTransactions'));
|
||||
});
|
||||
|
||||
it('getTransactions - earliest first with start option', function() {
|
||||
const options = {types: ['payment', 'order'], initiated: true, limit: 2,
|
||||
start: hashes.VALID_TRANSACTION_HASH,
|
||||
earliestFirst: true
|
||||
};
|
||||
return this.api.getTransactions(address, options).then(data => {
|
||||
assert.strictEqual(data.length, 0);
|
||||
});
|
||||
});
|
||||
|
||||
it('getTransactions - gap', function() {
|
||||
const options = {types: ['payment', 'order'], initiated: true, limit: 2,
|
||||
maxLedgerVersion: 348858000
|
||||
};
|
||||
return this.api.getTransactions(address, options).then(() => {
|
||||
assert(false, 'Should throw MissingLedgerHistoryError');
|
||||
}).catch(error => {
|
||||
assert(error instanceof this.api.errors.MissingLedgerHistoryError);
|
||||
});
|
||||
});
|
||||
|
||||
it('getTransactions - tx not found', function() {
|
||||
const options = {types: ['payment', 'order'], initiated: true, limit: 2,
|
||||
start: hashes.NOTFOUND_TRANSACTION_HASH,
|
||||
counterparty: address
|
||||
};
|
||||
return this.api.getTransactions(address, options).then(() => {
|
||||
assert(false, 'Should throw NotFoundError');
|
||||
}).catch(error => {
|
||||
assert(error instanceof this.api.errors.NotFoundError);
|
||||
});
|
||||
});
|
||||
|
||||
it('getTransactions - filters', function() {
|
||||
const options = {types: ['payment', 'order'], initiated: true, limit: 10,
|
||||
excludeFailures: true,
|
||||
counterparty: addresses.ISSUER
|
||||
};
|
||||
return this.api.getTransactions(address, options).then(data => {
|
||||
assert.strictEqual(data.length, 10);
|
||||
assert(_.every(data, t => t.type === 'payment' || t.type === 'order'));
|
||||
assert(_.every(data, t => t.outcome.result === 'tesSUCCESS'));
|
||||
});
|
||||
});
|
||||
|
||||
it('getTransactions - filters for incoming', function() {
|
||||
const options = {types: ['payment', 'order'], initiated: false, limit: 10,
|
||||
excludeFailures: true,
|
||||
counterparty: addresses.ISSUER
|
||||
};
|
||||
return this.api.getTransactions(address, options).then(data => {
|
||||
assert.strictEqual(data.length, 10);
|
||||
assert(_.every(data, t => t.type === 'payment' || t.type === 'order'));
|
||||
assert(_.every(data, t => t.outcome.result === 'tesSUCCESS'));
|
||||
});
|
||||
});
|
||||
|
||||
// this is the case where core.RippleError just falls
|
||||
// through the api to the user
|
||||
it('getTransactions - error', function() {
|
||||
const options = {types: ['payment', 'order'], initiated: true, limit: 13};
|
||||
return this.api.getTransactions(address, options).then(() => {
|
||||
assert(false, 'Should throw RippleError');
|
||||
}).catch(error => {
|
||||
assert(error instanceof common.core.RippleError);
|
||||
});
|
||||
});
|
||||
|
||||
// TODO: this doesn't test much, just that it doesn't crash
|
||||
it('getTransactions with start option', function(done) {
|
||||
it('getTransactions with start option', function() {
|
||||
const options = {
|
||||
start: hashes.VALID_TRANSACTION_HASH,
|
||||
earliestFirst: false,
|
||||
limit: 2
|
||||
};
|
||||
this.api.getTransactions(address, options,
|
||||
_.partial(checkResult, responses.getTransactions, done));
|
||||
return this.api.getTransactions(address, options).then(
|
||||
_.partial(checkResult, responses.getTransactions, 'getTransactions'));
|
||||
});
|
||||
|
||||
it('getTrustlines', function(done) {
|
||||
it('getTrustlines', function() {
|
||||
const options = {currency: 'USD'};
|
||||
this.api.getTrustlines(address, options,
|
||||
_.partial(checkResult, responses.getTrustlines, done));
|
||||
return this.api.getTrustlines(address, options).then(
|
||||
_.partial(checkResult, responses.getTrustlines, 'getTrustlines'));
|
||||
});
|
||||
|
||||
it('generateWallet', function() {
|
||||
withDeterministicPRNG(() => {
|
||||
assert.deepEqual(this.api.generateWallet(), responses.generateWallet);
|
||||
});
|
||||
it('generateAddress', function() {
|
||||
function random() {
|
||||
return _.fill(Array(16), 0);
|
||||
}
|
||||
assert.deepEqual(this.api.generateAddress({random}),
|
||||
responses.generateAddress);
|
||||
});
|
||||
|
||||
it('getSettings', function(done) {
|
||||
this.api.getSettings(address, {},
|
||||
_.partial(checkResult, responses.getSettings, done));
|
||||
it('getSettings', function() {
|
||||
return this.api.getSettings(address).then(
|
||||
_.partial(checkResult, responses.getSettings, 'getSettings'));
|
||||
});
|
||||
|
||||
it('getAccountInfo', function(done) {
|
||||
this.api.getAccountInfo(address, {},
|
||||
_.partial(checkResult, responses.getAccountInfo, done));
|
||||
it('getAccountInfo', function() {
|
||||
return this.api.getAccountInfo(address).then(
|
||||
_.partial(checkResult, responses.getAccountInfo, 'getAccountInfo'));
|
||||
});
|
||||
|
||||
it('getOrders', function(done) {
|
||||
this.api.getOrders(address, {},
|
||||
_.partial(checkResult, responses.getOrders, done));
|
||||
it('getOrders', function() {
|
||||
return this.api.getOrders(address).then(
|
||||
_.partial(checkResult, responses.getOrders, 'getOrders'));
|
||||
});
|
||||
|
||||
it('getOrderbook', function(done) {
|
||||
this.api.getOrderbook(address, orderbook, {},
|
||||
_.partial(checkResult, responses.getOrderbook, done));
|
||||
it('getOrderbook', function() {
|
||||
return this.api.getOrderbook(address, orderbook).then(
|
||||
_.partial(checkResult, responses.getOrderbook, 'getOrderbook'));
|
||||
});
|
||||
|
||||
it('getOrderbook - sorted so that best deals come first', function(done) {
|
||||
this.api.getOrderbook(address, orderbook, {}, (error, data) => {
|
||||
it('getOrderbook - sorted so that best deals come first', function() {
|
||||
return this.api.getOrderbook(address, orderbook).then(data => {
|
||||
const bidRates = data.bids.map(bid => bid.properties.makerExchangeRate);
|
||||
const askRates = data.asks.map(ask => ask.properties.makerExchangeRate);
|
||||
// makerExchangeRate = quality = takerPays.value/takerGets.value
|
||||
@@ -202,12 +449,11 @@ describe('RippleAPI', function() {
|
||||
// bids and asks should be sorted so that the best deals come first
|
||||
assert.deepEqual(_.sortBy(bidRates, x => Number(x)), bidRates);
|
||||
assert.deepEqual(_.sortBy(askRates, x => Number(x)), askRates);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('getOrderbook - currency & counterparty are correct', function(done) {
|
||||
this.api.getOrderbook(address, orderbook, {}, (error, data) => {
|
||||
it('getOrderbook - currency & counterparty are correct', function() {
|
||||
return this.api.getOrderbook(address, orderbook).then(data => {
|
||||
const orders = _.flatten([data.bids, data.asks]);
|
||||
_.forEach(orders, order => {
|
||||
const quantity = order.specification.quantity;
|
||||
@@ -218,54 +464,358 @@ describe('RippleAPI', function() {
|
||||
assert.strictEqual(totalPrice.currency, counter.currency);
|
||||
assert.strictEqual(totalPrice.counterparty, counter.counterparty);
|
||||
});
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('getOrderbook - direction is correct for bids and asks', function(done) {
|
||||
this.api.getOrderbook(address, orderbook, {}, (error, data) => {
|
||||
it('getOrderbook - direction is correct for bids and asks', function() {
|
||||
return this.api.getOrderbook(address, orderbook).then(data => {
|
||||
assert(_.every(data.bids, bid => bid.specification.direction === 'buy'));
|
||||
assert(_.every(data.asks, ask => ask.specification.direction === 'sell'));
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('getServerInfo', function(done) {
|
||||
this.api.getServerInfo(
|
||||
_.partial(checkResult, responses.getServerInfo, done));
|
||||
it('getServerInfo', function() {
|
||||
return this.api.getServerInfo().then(
|
||||
_.partial(checkResult, responses.getServerInfo, 'getServerInfo'));
|
||||
});
|
||||
|
||||
it('getServerInfo - error', function() {
|
||||
this.mockRippled.returnErrorOnServerInfo = true;
|
||||
return this.api.getServerInfo().then(() => {
|
||||
assert(false, 'Should throw NetworkError');
|
||||
}).catch(error => {
|
||||
assert(error instanceof this.api.errors.NetworkError);
|
||||
assert(error.message.indexOf('too much load') !== -1);
|
||||
});
|
||||
});
|
||||
|
||||
it('getFee', function() {
|
||||
assert.strictEqual(this.api.getFee(), '0.000012');
|
||||
});
|
||||
|
||||
it('disconnect & isConnected', function(done) {
|
||||
it('disconnect & isConnected', function() {
|
||||
assert.strictEqual(this.api.isConnected(), true);
|
||||
this.api.disconnect(() => {
|
||||
return this.api.disconnect().then(() => {
|
||||
assert.strictEqual(this.api.isConnected(), false);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('getPaths', function(done) {
|
||||
const pathfind = {
|
||||
source: {
|
||||
address: address
|
||||
},
|
||||
destination: {
|
||||
address: addresses.OTHER_ACCOUNT,
|
||||
amount: {
|
||||
currency: 'USD',
|
||||
counterparty: addresses.ISSUER,
|
||||
value: '100'
|
||||
}
|
||||
}
|
||||
};
|
||||
this.api.getPaths(pathfind,
|
||||
_.partial(checkResult, responses.getPaths, done));
|
||||
it('getPaths', function() {
|
||||
return this.api.getPaths(requests.getPaths.normal).then(
|
||||
_.partial(checkResult, responses.getPaths.XrpToUsd, 'getPaths'));
|
||||
});
|
||||
|
||||
// @TODO
|
||||
// need decide what to do with currencies/XRP:
|
||||
// if add 'XRP' in currencies, then there will be exception in
|
||||
// xrpToDrops function (called from toRippledAmount)
|
||||
it('getPaths USD 2 USD', function() {
|
||||
return this.api.getPaths(requests.getPaths.UsdToUsd).then(
|
||||
_.partial(checkResult, responses.getPaths.UsdToUsd, 'getPaths'));
|
||||
});
|
||||
|
||||
it('getPaths XRP 2 XRP', function() {
|
||||
return this.api.getPaths(requests.getPaths.XrpToXrp).then(
|
||||
_.partial(checkResult, responses.getPaths.XrpToXrp, 'getPaths'));
|
||||
});
|
||||
|
||||
it('getPaths - XRP 2 XRP - not enough', function() {
|
||||
return this.api.getPaths(requests.getPaths.XrpToXrpNotEnough).then(() => {
|
||||
assert(false, 'Should throw NotFoundError');
|
||||
}).catch(error => {
|
||||
assert(error instanceof this.api.errors.NotFoundError);
|
||||
});
|
||||
});
|
||||
|
||||
it('getPaths - does not accept currency', function() {
|
||||
return this.api.getPaths(requests.getPaths.NotAcceptCurrency).then(() => {
|
||||
assert(false, 'Should throw NotFoundError');
|
||||
}).catch(error => {
|
||||
assert(error instanceof this.api.errors.NotFoundError);
|
||||
});
|
||||
});
|
||||
|
||||
it('getPaths - no paths', function() {
|
||||
return this.api.getPaths(requests.getPaths.NoPaths).then(() => {
|
||||
assert(false, 'Should throw NotFoundError');
|
||||
}).catch(error => {
|
||||
assert(error instanceof this.api.errors.NotFoundError);
|
||||
});
|
||||
});
|
||||
|
||||
it('getPaths - no paths with source currencies', function() {
|
||||
const pathfind = requests.getPaths.NoPathsWithCurrencies;
|
||||
return this.api.getPaths(pathfind).then(() => {
|
||||
assert(false, 'Should throw NotFoundError');
|
||||
}).catch(error => {
|
||||
assert(error instanceof this.api.errors.NotFoundError);
|
||||
});
|
||||
});
|
||||
|
||||
it('getLedgerVersion', function() {
|
||||
assert.strictEqual(this.api.getLedgerVersion(), 8819951);
|
||||
});
|
||||
|
||||
it('getLedger', function() {
|
||||
return this.api.getLedger().then(
|
||||
_.partial(checkResult, responses.getLedger.header, 'getLedger'));
|
||||
});
|
||||
|
||||
it('getLedger - full, then computeLedgerHash', function() {
|
||||
const request = {
|
||||
includeTransactions: true,
|
||||
includeState: true,
|
||||
includeAllData: true,
|
||||
ledgerVersion: 38129
|
||||
};
|
||||
return this.api.getLedger(request).then(
|
||||
_.partial(checkResult, responses.getLedger.full, 'getLedger'))
|
||||
.then(response => {
|
||||
const ledger = _.assign({}, response,
|
||||
{parentCloseTime: response.closeTime});
|
||||
const hash = this.api.computeLedgerHash(ledger);
|
||||
assert.strictEqual(hash,
|
||||
'E6DB7365949BF9814D76BCC730B01818EB9136A89DB224F3F9F5AAE4569D758E');
|
||||
});
|
||||
});
|
||||
|
||||
it('ledger utils - compareTransactions', function() {
|
||||
let first = {outcome: {ledgerVersion: 1, indexInLedger: 100}};
|
||||
let second = {outcome: {ledgerVersion: 1, indexInLedger: 200}};
|
||||
|
||||
assert.strictEqual(utils.compareTransactions(first, second), -1);
|
||||
|
||||
first = {outcome: {ledgerVersion: 1, indexInLedger: 100}};
|
||||
second = {outcome: {ledgerVersion: 1, indexInLedger: 100}};
|
||||
|
||||
assert.strictEqual(utils.compareTransactions(first, second), 0);
|
||||
|
||||
first = {outcome: {ledgerVersion: 1, indexInLedger: 200}};
|
||||
second = {outcome: {ledgerVersion: 1, indexInLedger: 100}};
|
||||
|
||||
assert.strictEqual(utils.compareTransactions(first, second), 1);
|
||||
});
|
||||
|
||||
it('ledger utils - renameCounterpartyToIssuer', function() {
|
||||
assert.strictEqual(utils.renameCounterpartyToIssuer(undefined), undefined);
|
||||
const amountArg = {issuer: '1'};
|
||||
assert.deepEqual(utils.renameCounterpartyToIssuer(amountArg), amountArg);
|
||||
});
|
||||
|
||||
it('ledger utils - getRecursive', function(done) {
|
||||
function getter(marker, limit, callback) {
|
||||
if (marker === undefined) {
|
||||
callback(null, {marker: 'A', limit: limit, results: [1]});
|
||||
} else {
|
||||
callback(new Error(), null);
|
||||
}
|
||||
}
|
||||
utils.getRecursive(getter, 10, (error) => {
|
||||
assert(error instanceof Error);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
describe('schema-validator', function() {
|
||||
beforeEach(function() {
|
||||
const schema = schemaValidator.loadSchema(path.join(__dirname,
|
||||
'./fixtures/schemas/ledgerhash.json'));
|
||||
schemaValidator.SCHEMAS.ledgerhash = schema;
|
||||
});
|
||||
|
||||
it('valid', function() {
|
||||
assert.doesNotThrow(function() {
|
||||
schemaValidator.schemaValidate('ledgerhash',
|
||||
'0F7ED9F40742D8A513AE86029462B7A6768325583DF8EE21B7EC663019DD6A0F');
|
||||
});
|
||||
});
|
||||
|
||||
it('invalid', function() {
|
||||
assert.throws(function() {
|
||||
schemaValidator.schemaValidate('ledgerhash', 'invalid');
|
||||
}, this.api.errors.ValidationError);
|
||||
});
|
||||
|
||||
it('invalid - empty value', function() {
|
||||
assert.throws(function() {
|
||||
schemaValidator.schemaValidate('ledgerhash', '');
|
||||
}, this.api.errors.ValidationError);
|
||||
});
|
||||
|
||||
it('load schema error', function() {
|
||||
assert.throws(function() {
|
||||
schemaValidator.loadSchema('/bad/file/name');
|
||||
}, Error);
|
||||
});
|
||||
|
||||
it('schema not found error', function() {
|
||||
assert.throws(function() {
|
||||
schemaValidator.schemaValidate('unexisting', 'anything');
|
||||
}, /schema not found/);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('validator', function() {
|
||||
|
||||
it('validateLedgerRange', function() {
|
||||
const options = {
|
||||
minLedgerVersion: 20000,
|
||||
maxLedgerVersion: 10000
|
||||
};
|
||||
assert.throws(_.partial(validate.getTransactionsOptions, options),
|
||||
this.api.errors.ValidationError);
|
||||
assert.throws(_.partial(validate.getTransactionsOptions, options),
|
||||
/minLedgerVersion must not be greater than maxLedgerVersion/);
|
||||
});
|
||||
|
||||
it('addressAndSecret', function() {
|
||||
const noSecret = {address: address};
|
||||
assert.throws(_.partial(validate.addressAndSecret, noSecret),
|
||||
this.api.errors.ValidationError);
|
||||
assert.throws(_.partial(validate.addressAndSecret, noSecret),
|
||||
/Parameter missing/);
|
||||
const badSecret = {address: address, secret: 'sbad'};
|
||||
assert.throws(_.partial(validate.addressAndSecret, badSecret),
|
||||
this.api.errors.ValidationError);
|
||||
const goodWallet = {address: 'rpZMK8hwyrBvLorFNWHRCGt88nCJWbixur',
|
||||
secret: 'shzjfakiK79YQdMjy4h8cGGfQSV6u'
|
||||
};
|
||||
assert.doesNotThrow(_.partial(validate.addressAndSecret, goodWallet));
|
||||
});
|
||||
|
||||
it('secret', function() {
|
||||
assert.doesNotThrow(_.partial(validate.secret,
|
||||
'shzjfakiK79YQdMjy4h8cGGfQSV6u'));
|
||||
assert.throws(_.partial(validate.secret, 1),
|
||||
/Invalid parameter/);
|
||||
assert.throws(_.partial(validate.secret, ''),
|
||||
this.api.errors.ValidationError);
|
||||
assert.throws(_.partial(validate.secret, 's!!!'),
|
||||
this.api.errors.ValidationError);
|
||||
assert.throws(_.partial(validate.secret, 'passphrase'),
|
||||
this.api.errors.ValidationError);
|
||||
// 32 0s is a valid hex repr of seed bytes
|
||||
const hex = new Array(33).join('0');
|
||||
assert.throws(_.partial(validate.secret, hex),
|
||||
this.api.errors.ValidationError);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('common utils', function() {
|
||||
|
||||
it('wrapCatch', function(done) {
|
||||
common.wrapCatch(function() {
|
||||
throw new Error('error');
|
||||
})(function(error) {
|
||||
assert(error instanceof Error);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('convertExceptions', function() {
|
||||
assert.throws(common.convertExceptions(function() {
|
||||
throw new Error('fall through');
|
||||
}), this.api.errors.ApiError);
|
||||
assert.throws(common.convertExceptions(function() {
|
||||
throw new Error('fall through');
|
||||
}), /fall through/);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('common errors', function() {
|
||||
|
||||
it('TransactionError', function() {
|
||||
// TransactionError is not used anywhere, so just test its creation
|
||||
assert.throws(function() {
|
||||
throw new common.errors.TransactionError('fall through');
|
||||
}, this.api.errors.TransactionError);
|
||||
assert.throws(function() {
|
||||
throw new common.errors.TransactionError('fall through');
|
||||
}, /fall through/);
|
||||
});
|
||||
|
||||
it('TimeOutError', function() {
|
||||
// TimeOutError is not used anywhere, so just test its creation
|
||||
assert.throws(function() {
|
||||
throw new common.errors.TimeOutError('fall through');
|
||||
}, this.api.errors.TimeOutError);
|
||||
assert.throws(function() {
|
||||
throw new common.errors.TimeOutError('fall through');
|
||||
}, /fall through/);
|
||||
});
|
||||
|
||||
it('RippledNetworkError', function() {
|
||||
assert.throws(function() {
|
||||
throw new common.errors.RippledNetworkError();
|
||||
}, /Cannot connect to rippled/);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('RippleAPI - offline', function() {
|
||||
it('prepareSettings and sign', function() {
|
||||
const api = new RippleAPI();
|
||||
const secret = 'shsWGZcmZz6YsWWmcnpfr6fLTdtFV';
|
||||
const settings = requests.prepareSettings;
|
||||
const instructions = {
|
||||
sequence: 23,
|
||||
maxLedgerVersion: 8820051,
|
||||
fee: '0.000012'
|
||||
};
|
||||
return api.prepareSettings(address, settings, instructions).then(txJSON => {
|
||||
assert.deepEqual(txJSON, responses.prepareSettings.flags);
|
||||
assert.deepEqual(api.sign(txJSON, secret), responses.sign);
|
||||
});
|
||||
});
|
||||
|
||||
it('computeLedgerHash', function() {
|
||||
const api = new RippleAPI();
|
||||
const header = requests.computeLedgerHash.header;
|
||||
const ledgerHash = api.computeLedgerHash(header);
|
||||
assert.strictEqual(ledgerHash,
|
||||
'F4D865D83EB88C1A1911B9E90641919A1314F36E1B099F8E95FE3B7C77BE3349');
|
||||
});
|
||||
|
||||
it('computeLedgerHash - with transactions', function() {
|
||||
const api = new RippleAPI();
|
||||
const header = _.omit(requests.computeLedgerHash.header,
|
||||
'transactionHash');
|
||||
header.rawTransactions = JSON.stringify(
|
||||
requests.computeLedgerHash.transactions);
|
||||
const ledgerHash = api.computeLedgerHash(header);
|
||||
assert.strictEqual(ledgerHash,
|
||||
'F4D865D83EB88C1A1911B9E90641919A1314F36E1B099F8E95FE3B7C77BE3349');
|
||||
});
|
||||
|
||||
it('computeLedgerHash - incorrent transaction_hash', function() {
|
||||
const api = new RippleAPI();
|
||||
const header = _.assign({}, requests.computeLedgerHash.header,
|
||||
{transactionHash:
|
||||
'325EACC5271322539EEEC2D6A5292471EF1B3E72AE7180533EFC3B8F0AD435C9'});
|
||||
header.rawTransactions = JSON.stringify(
|
||||
requests.computeLedgerHash.transactions);
|
||||
assert.throws(() => api.computeLedgerHash(header));
|
||||
});
|
||||
|
||||
it('isValidAddress - valid', function() {
|
||||
const api = new RippleAPI();
|
||||
assert(api.isValidAddress(address));
|
||||
});
|
||||
|
||||
it('isValidAddress - invalid', function() {
|
||||
const api = new RippleAPI();
|
||||
assert(!api.isValidAddress(address.slice(0, -1) + 'a'));
|
||||
});
|
||||
|
||||
it('isValidAddress - invalid - hex representation', function() {
|
||||
const api = new RippleAPI();
|
||||
const hex = '6e3efa86a5eb0a3c5dc9beb3a204783bb00e1913';
|
||||
assert(!api.isValidAddress(hex));
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
@@ -31,11 +31,11 @@ describe('Base', function() {
|
||||
describe('decode_check', function() {
|
||||
it('rrrrrrrrrrrrrrrrrrrrrhoLvTp', function() {
|
||||
const decoded = Base.decode_check(0, 'rrrrrrrrrrrrrrrrrrrrrhoLvTp');
|
||||
assert(decoded.equals(0));
|
||||
assert(decoded.cmpn(0) === 0);
|
||||
});
|
||||
it('rrrrrrrrrrrrrrrrrrrrBZbvji', function() {
|
||||
const decoded = Base.decode_check(0, 'rrrrrrrrrrrrrrrrrrrrBZbvji');
|
||||
assert(decoded.equals(1));
|
||||
assert(decoded.cmpn(1) === 0);
|
||||
});
|
||||
});
|
||||
describe('decode-encode identity', function() {
|
||||
|
||||
474
test/fixtures/api/requests/compute-ledger-hash-transactions.json
vendored
Normal file
474
test/fixtures/api/requests/compute-ledger-hash-transactions.json
vendored
Normal file
@@ -0,0 +1,474 @@
|
||||
[
|
||||
{
|
||||
"hash": "f8f337dee5d5b238a10af4a4d56926ba26c83ee7af5a5a6474340c56f9252df3",
|
||||
"date": "2015-08-12T01:01:10+00:00",
|
||||
"ledger_index": 15202439,
|
||||
"tx": {
|
||||
"TransactionType": "Payment",
|
||||
"Flags": 2147483648,
|
||||
"Sequence": 1608,
|
||||
"LastLedgerSequence": 15202446,
|
||||
"Amount": "120000000",
|
||||
"Fee": "15000",
|
||||
"SigningPubKey": "03BC0973F997BC6384BE455B163519A3E96BC2D725C37F7172D5FED5DD38E2A357",
|
||||
"TxnSignature": "3045022100D80A1802B00AEEF9FDFDE594B0D568217A312D54E6337B8519C0D699841EFB96022067F6913B13D0EC2354C5A67CE0A41AE4181A09CD08A1BB0638D128D357961006",
|
||||
"Account": "rDPL68aNpdfp9h59R4QT5R6B1Z2W9oRc51",
|
||||
"Destination": "rE4S4Xw8euysJ3mt7gmK8EhhYEwmALpb3R"
|
||||
},
|
||||
"meta": {
|
||||
"TransactionIndex": 6,
|
||||
"AffectedNodes": [
|
||||
{
|
||||
"ModifiedNode": {
|
||||
"LedgerEntryType": "AccountRoot",
|
||||
"PreviousTxnLgrSeq": 15202381,
|
||||
"PreviousTxnID": "8FFB65C6907C9679C5F8AADA97072CD1B8FE4955FC6A614AC87408AE7C9088AD",
|
||||
"LedgerIndex": "B07B367ABF05243A536986DEC74684E983BBBDDF443ADE9CDC43A22D6E6A1420",
|
||||
"PreviousFields": {
|
||||
"Sequence": 1608,
|
||||
"Balance": "61455842701"
|
||||
},
|
||||
"FinalFields": {
|
||||
"Flags": 0,
|
||||
"Sequence": 1609,
|
||||
"OwnerCount": 0,
|
||||
"Balance": "61335827701",
|
||||
"Account": "rDPL68aNpdfp9h59R4QT5R6B1Z2W9oRc51"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"ModifiedNode": {
|
||||
"LedgerEntryType": "AccountRoot",
|
||||
"PreviousTxnLgrSeq": 15202438,
|
||||
"PreviousTxnID": "B01591A2353CD39EFAC989D542EE37591F60CF9BB2B66526C8C958774813407E",
|
||||
"LedgerIndex": "F77EB82FA9593E695F22155C00C569A570CF32316BEFDFF0B16BADAFF2ACFF19",
|
||||
"PreviousFields": {
|
||||
"Balance": "26762033252"
|
||||
},
|
||||
"FinalFields": {
|
||||
"Flags": 0,
|
||||
"Sequence": 6448,
|
||||
"OwnerCount": 3,
|
||||
"Balance": "26882033252",
|
||||
"Account": "rE4S4Xw8euysJ3mt7gmK8EhhYEwmALpb3R"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"TransactionResult": "tesSUCCESS"
|
||||
}
|
||||
},
|
||||
{
|
||||
"hash": "f8d5de632b1d8b64e577c46912cce483d6df4fd4e2cf4a3d586a099de3b27021",
|
||||
"date": "2015-08-12T01:01:10+00:00",
|
||||
"ledger_index": 15202439,
|
||||
"tx": {
|
||||
"TransactionType": "Payment",
|
||||
"Flags": 2147483648,
|
||||
"Sequence": 18874,
|
||||
"LastLedgerSequence": 15202446,
|
||||
"Amount": "120000000",
|
||||
"Fee": "15000",
|
||||
"SigningPubKey": "035D097E75D4B35345CEB30F9B1D18CB81165FE6ADD02481AA5B02B5F9C8107EE1",
|
||||
"TxnSignature": "304402203D80E8BC71908AB345948AB71FB7B8DE239DD79636D96D3C5BDA2B2F192A5EEA0220686413D69BF0D813FC61DABD437AEFAAE69925D3E10FCD5B2C4D90B5AF7B883D",
|
||||
"Account": "rnHScgV6wSP9sR25uYWiMo3QYNA5ybQ7cH",
|
||||
"Destination": "rwnnfHDaEAwXaVji52cWWizbHVMs2Cz5K9"
|
||||
},
|
||||
"meta": {
|
||||
"TransactionIndex": 5,
|
||||
"AffectedNodes": [
|
||||
{
|
||||
"ModifiedNode": {
|
||||
"LedgerEntryType": "AccountRoot",
|
||||
"PreviousTxnLgrSeq": 15202429,
|
||||
"PreviousTxnID": "B1F39887411C1771998F38502EDF33170F9F5659503DB9DE642EBA896B5F198B",
|
||||
"LedgerIndex": "2AAA3361C593C4DE7ABD9A607B3CA7070A3F74E3C3F2FDE4DDB9484E47ED056E",
|
||||
"PreviousFields": {
|
||||
"Sequence": 18874,
|
||||
"Balance": "13795295558367"
|
||||
},
|
||||
"FinalFields": {
|
||||
"Flags": 0,
|
||||
"Sequence": 18875,
|
||||
"OwnerCount": 0,
|
||||
"Balance": "13795175543367",
|
||||
"Account": "rnHScgV6wSP9sR25uYWiMo3QYNA5ybQ7cH"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"ModifiedNode": {
|
||||
"LedgerEntryType": "AccountRoot",
|
||||
"PreviousTxnLgrSeq": 15202416,
|
||||
"PreviousTxnID": "00CF9C7BE3EBAF76893C6A3F6D10B4D89F8D856C97B9D44938CF1682132ACEB8",
|
||||
"LedgerIndex": "928582D6F6942B18F3462FA04BA99F476B64FEB9921BFAD583182DC28CB74187",
|
||||
"PreviousFields": {
|
||||
"Balance": "17674359316"
|
||||
},
|
||||
"FinalFields": {
|
||||
"Flags": 0,
|
||||
"Sequence": 1710,
|
||||
"OwnerCount": 0,
|
||||
"Balance": "17794359316",
|
||||
"Account": "rwnnfHDaEAwXaVji52cWWizbHVMs2Cz5K9"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"TransactionResult": "tesSUCCESS"
|
||||
}
|
||||
},
|
||||
{
|
||||
"hash": "e9004490a92413e92dacd621ac73fd434a8950c350f7572ffeaf4d6aaf8fc288",
|
||||
"date": "2015-08-12T01:01:10+00:00",
|
||||
"ledger_index": 15202439,
|
||||
"tx": {
|
||||
"TransactionType": "Payment",
|
||||
"Flags": 2147483648,
|
||||
"Sequence": 1615,
|
||||
"LastLedgerSequence": 15202446,
|
||||
"Amount": "400000000",
|
||||
"Fee": "15000",
|
||||
"SigningPubKey": "03ACFAA11628C558AB5E7FA64705F442BDAABA6E9D318B30E010BC87CDEA8D1D7D",
|
||||
"TxnSignature": "3045022100A3530C2E983FB05DFF27172C649494291F7BEBA2E6A59EEAF945CB9728D1DB5E022015BCA0E9D69760224DD7C2B68F3BC1F239D89C3397161AA3901C2E04EE31C18F",
|
||||
"Account": "razcSDpwds1aTeqDphqzBr7ay1ZELYAWTm",
|
||||
"Destination": "rhuqJAE2UfhGCvkR7Ve35bvm39JmRvFML4"
|
||||
},
|
||||
"meta": {
|
||||
"TransactionIndex": 4,
|
||||
"AffectedNodes": [
|
||||
{
|
||||
"ModifiedNode": {
|
||||
"LedgerEntryType": "AccountRoot",
|
||||
"PreviousTxnLgrSeq": 15202394,
|
||||
"PreviousTxnID": "99E8F8988390F5A8DF69BBA4F04705E5085EE91B27583D28210D37B7513F10BB",
|
||||
"LedgerIndex": "17CF549DFC0813DDC44559C89E99B4C1D033D59FF379AD948CBEC141F179293D",
|
||||
"PreviousFields": {
|
||||
"Sequence": 1615,
|
||||
"Balance": "45875786250"
|
||||
},
|
||||
"FinalFields": {
|
||||
"Flags": 0,
|
||||
"Sequence": 1616,
|
||||
"OwnerCount": 0,
|
||||
"Balance": "45475771250",
|
||||
"Account": "razcSDpwds1aTeqDphqzBr7ay1ZELYAWTm"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"ModifiedNode": {
|
||||
"LedgerEntryType": "AccountRoot",
|
||||
"PreviousTxnLgrSeq": 15202438,
|
||||
"PreviousTxnID": "9EC0784393DA95BB3B38FABC59FEFEE34BA8487DD892B9EAC1D70E483D1B0FA6",
|
||||
"LedgerIndex": "EB13399E9A69F121BEDA810F1AE9CB4023B4B09C5055CB057B572029B2FC8DD4",
|
||||
"PreviousFields": {
|
||||
"Balance": "76953067090"
|
||||
},
|
||||
"FinalFields": {
|
||||
"Flags": 0,
|
||||
"Sequence": 601,
|
||||
"OwnerCount": 4,
|
||||
"Balance": "77353067090",
|
||||
"Account": "rhuqJAE2UfhGCvkR7Ve35bvm39JmRvFML4"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"TransactionResult": "tesSUCCESS"
|
||||
}
|
||||
},
|
||||
{
|
||||
"hash": "d44bff924d23211b82b8f604af6d92f260f8dd13103a96f03e48825c4a978fd6",
|
||||
"date": "2015-08-12T01:01:10+00:00",
|
||||
"ledger_index": 15202439,
|
||||
"tx": {
|
||||
"TransactionType": "Payment",
|
||||
"Flags": 2147483648,
|
||||
"Sequence": 1674,
|
||||
"LastLedgerSequence": 15202446,
|
||||
"Amount": "800000000",
|
||||
"Fee": "15000",
|
||||
"SigningPubKey": "028F28D78FDA74222F4008F012247DF3BBD42B90CE4CFD87E29598196108E91B52",
|
||||
"TxnSignature": "3044022065A003194D91E774D180BE47D4E086BB2624BC8F6DB7C655E135D5C6C03BBC7C02205DC961C2B7A06D701B29C2116ACF6F84CC84205FF44411576C15507852ECC31C",
|
||||
"Account": "rQGLp9nChtWkdgcHjj6McvJithN2S2HJsP",
|
||||
"Destination": "rEUubanepAAugnNJY1gxEZLDnk9W5NCoFU"
|
||||
},
|
||||
"meta": {
|
||||
"TransactionIndex": 3,
|
||||
"AffectedNodes": [
|
||||
{
|
||||
"ModifiedNode": {
|
||||
"LedgerEntryType": "AccountRoot",
|
||||
"PreviousTxnLgrSeq": 15202409,
|
||||
"PreviousTxnID": "6A9B73C13B8A74BCDB64B5ADFE3D8FFEAC7928B82CFD6C9A35254D7798AD0688",
|
||||
"LedgerIndex": "D1A7795E8E997E7DE65D64283FD7CEEB5E43C2E5C4A794C2CFCEC6724E03F464",
|
||||
"PreviousFields": {
|
||||
"Sequence": 1674,
|
||||
"Balance": "8774844732"
|
||||
},
|
||||
"FinalFields": {
|
||||
"Flags": 0,
|
||||
"Sequence": 1675,
|
||||
"OwnerCount": 0,
|
||||
"Balance": "7974829732",
|
||||
"Account": "rQGLp9nChtWkdgcHjj6McvJithN2S2HJsP"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"ModifiedNode": {
|
||||
"LedgerEntryType": "AccountRoot",
|
||||
"PreviousTxnLgrSeq": 15202388,
|
||||
"PreviousTxnID": "ECE994DA817228D9170D22C01CE1BF5B17FFE1AE6404FF215719C1049E9939E0",
|
||||
"LedgerIndex": "E5EA9215A6D41C4E20C831ACE436E5B75F9BA2A9BD4325BA65BD9D44F5E13A08",
|
||||
"PreviousFields": {
|
||||
"Balance": "9077529029"
|
||||
},
|
||||
"FinalFields": {
|
||||
"Flags": 0,
|
||||
"Sequence": 1496,
|
||||
"OwnerCount": 0,
|
||||
"Balance": "9877529029",
|
||||
"Account": "rEUubanepAAugnNJY1gxEZLDnk9W5NCoFU"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"TransactionResult": "tesSUCCESS"
|
||||
}
|
||||
},
|
||||
{
|
||||
"hash": "c978d915bfb17687335cbfc4b207d9e7213bcee35b468c2eee016cdce4edb6e4",
|
||||
"date": "2015-08-12T01:01:10+00:00",
|
||||
"ledger_index": 15202439,
|
||||
"tx": {
|
||||
"TransactionType": "OfferCreate",
|
||||
"Sequence": 289444,
|
||||
"OfferSequence": 289443,
|
||||
"LastLedgerSequence": 15202441,
|
||||
"TakerPays": {
|
||||
"value": "19.99999999991",
|
||||
"currency": "EUR",
|
||||
"issuer": "rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q"
|
||||
},
|
||||
"TakerGets": {
|
||||
"value": "20.88367500010602",
|
||||
"currency": "USD",
|
||||
"issuer": "rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q"
|
||||
},
|
||||
"Fee": "10000",
|
||||
"SigningPubKey": "024D129D4F5A12D4C5A9E9D1E4AC447BBE3496F182FAE82F7709C7EB9F12DBC697",
|
||||
"TxnSignature": "3044022041EBE6B06BA493867F4FFBD72E5D6253F97306E1E82DABDF9649E15B1151B59F0220539C589F40174471C067FDC761A2B791F36F1A3C322734B43DB16880E489BD81",
|
||||
"Account": "rD8LigXE7165r3VWhSQ4FwzJy7PNrTMwUq",
|
||||
"Memos": [
|
||||
{
|
||||
"Memo": {
|
||||
"MemoType": "6F666665725F636F6D6D656E74",
|
||||
"MemoData": "72655F6575722368656467655F726970706C65",
|
||||
"parsed_memo_type": "offer_comment"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"meta": {
|
||||
"TransactionIndex": 2,
|
||||
"AffectedNodes": [
|
||||
{
|
||||
"CreatedNode": {
|
||||
"LedgerEntryType": "Offer",
|
||||
"LedgerIndex": "2069A6F3B349C246630536B3A0D18FECF0B088D6846ED74D56762096B972ADBE",
|
||||
"NewFields": {
|
||||
"Sequence": 289444,
|
||||
"BookDirectory": "D3C7DF102A0CEDB307D6F471B0CE679C5C206D8227D9BB2E5422061A1FB5AF31",
|
||||
"TakerPays": {
|
||||
"value": "19.99999999991",
|
||||
"currency": "EUR",
|
||||
"issuer": "rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q"
|
||||
},
|
||||
"TakerGets": {
|
||||
"value": "20.88367500010602",
|
||||
"currency": "USD",
|
||||
"issuer": "rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q"
|
||||
},
|
||||
"Account": "rD8LigXE7165r3VWhSQ4FwzJy7PNrTMwUq"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"ModifiedNode": {
|
||||
"LedgerEntryType": "DirectoryNode",
|
||||
"LedgerIndex": "68E8826D6545315B54943AF0D6A45264598F2DE8A71CB9EFA97C9F4456078BE8",
|
||||
"FinalFields": {
|
||||
"Flags": 0,
|
||||
"RootIndex": "68E8826D6545315B54943AF0D6A45264598F2DE8A71CB9EFA97C9F4456078BE8",
|
||||
"Owner": "rD8LigXE7165r3VWhSQ4FwzJy7PNrTMwUq"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"DeletedNode": {
|
||||
"LedgerEntryType": "Offer",
|
||||
"LedgerIndex": "9AC6C83397287FDFF4DB7ED6D96DA060CF32ED6593B18C332EEDFE833AE48E1C",
|
||||
"FinalFields": {
|
||||
"Flags": 0,
|
||||
"Sequence": 289443,
|
||||
"PreviousTxnLgrSeq": 15202438,
|
||||
"BookNode": "0000000000000000",
|
||||
"OwnerNode": "0000000000000000",
|
||||
"PreviousTxnID": "6C1B0818CA470DBD5EFC28FC863862B0DF9D9F659475612446806401C56E3B28",
|
||||
"BookDirectory": "D3C7DF102A0CEDB307D6F471B0CE679C5C206D8227D9BB2E5422061A1FB5AF31",
|
||||
"TakerPays": {
|
||||
"value": "19.99999999991",
|
||||
"currency": "EUR",
|
||||
"issuer": "rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q"
|
||||
},
|
||||
"TakerGets": {
|
||||
"value": "20.88367500010602",
|
||||
"currency": "USD",
|
||||
"issuer": "rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q"
|
||||
},
|
||||
"Account": "rD8LigXE7165r3VWhSQ4FwzJy7PNrTMwUq"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"ModifiedNode": {
|
||||
"LedgerEntryType": "DirectoryNode",
|
||||
"LedgerIndex": "D3C7DF102A0CEDB307D6F471B0CE679C5C206D8227D9BB2E5422061A1FB5AF31",
|
||||
"FinalFields": {
|
||||
"Flags": 0,
|
||||
"ExchangeRate": "5422061A1FB5AF31",
|
||||
"RootIndex": "D3C7DF102A0CEDB307D6F471B0CE679C5C206D8227D9BB2E5422061A1FB5AF31",
|
||||
"TakerPaysCurrency": "0000000000000000000000004555520000000000",
|
||||
"TakerPaysIssuer": "DD39C650A96EDA48334E70CC4A85B8B2E8502CD3",
|
||||
"TakerGetsCurrency": "0000000000000000000000005553440000000000",
|
||||
"TakerGetsIssuer": "DD39C650A96EDA48334E70CC4A85B8B2E8502CD3"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"ModifiedNode": {
|
||||
"LedgerEntryType": "AccountRoot",
|
||||
"PreviousTxnLgrSeq": 15202438,
|
||||
"PreviousTxnID": "6C1B0818CA470DBD5EFC28FC863862B0DF9D9F659475612446806401C56E3B28",
|
||||
"LedgerIndex": "D8614A045CBA0F0081B23FD80CA87E7D08651FA02450C7BEE1B480836F0DC95D",
|
||||
"PreviousFields": {
|
||||
"Sequence": 289444,
|
||||
"Balance": "3712981021"
|
||||
},
|
||||
"FinalFields": {
|
||||
"Flags": 0,
|
||||
"Sequence": 289445,
|
||||
"OwnerCount": 13,
|
||||
"Balance": "3712971021",
|
||||
"Account": "rD8LigXE7165r3VWhSQ4FwzJy7PNrTMwUq"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"TransactionResult": "tesSUCCESS"
|
||||
}
|
||||
},
|
||||
{
|
||||
"hash": "31b34fd7c90cdc6cf680a814debc6f616c69275c0e99711f904de088a8ed4b28",
|
||||
"date": "2015-08-12T01:01:10+00:00",
|
||||
"ledger_index": 15202439,
|
||||
"tx": {
|
||||
"TransactionType": "AccountSet",
|
||||
"Flags": 2147483648,
|
||||
"Sequence": 387262,
|
||||
"LastLedgerSequence": 15202440,
|
||||
"Fee": "10500",
|
||||
"SigningPubKey": "027DFE042DC2BD07D2E88DD526A5FBF816C831C25CA0BB62A3BF320A3B2BA6DB5C",
|
||||
"TxnSignature": "30440220572D89688D9F9DB9874CDDDD3EBDCB5808A836982864C81F185FBC54FAD1A7B902202E09AAA6D65EECC9ACDEA7F70D8D2EE024152C7B288FA9E42C427260CF922F58",
|
||||
"Account": "rn6uAt46Xi6uxA2dRCtqaJyM3aaP6V9WWM"
|
||||
},
|
||||
"meta": {
|
||||
"TransactionIndex": 1,
|
||||
"AffectedNodes": [
|
||||
{
|
||||
"ModifiedNode": {
|
||||
"LedgerEntryType": "AccountRoot",
|
||||
"PreviousTxnLgrSeq": 15202429,
|
||||
"PreviousTxnID": "212D4BFAD4DFB0887B57AB840A8385F31FC2839FFD4169A824280565CC2885C0",
|
||||
"LedgerIndex": "317481AD6274D399F50E13EF447825DA628197E6262B80642DAE0D8300D77E55",
|
||||
"PreviousFields": {
|
||||
"Sequence": 387262,
|
||||
"Balance": "207020609"
|
||||
},
|
||||
"FinalFields": {
|
||||
"Flags": 0,
|
||||
"Sequence": 387263,
|
||||
"OwnerCount": 22,
|
||||
"Balance": "207010109",
|
||||
"Account": "rn6uAt46Xi6uxA2dRCtqaJyM3aaP6V9WWM"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"TransactionResult": "tesSUCCESS"
|
||||
}
|
||||
},
|
||||
{
|
||||
"hash": "260bc2964ffe6d81cb25c152f8054ffb2ce6ed04ff89d8d0d0559bc14bef0e46",
|
||||
"date": "2015-08-12T01:01:10+00:00",
|
||||
"ledger_index": 15202439,
|
||||
"tx": {
|
||||
"TransactionType": "Payment",
|
||||
"Flags": 2147483648,
|
||||
"Sequence": 1673,
|
||||
"LastLedgerSequence": 15202446,
|
||||
"Amount": "1700000000",
|
||||
"Fee": "15000",
|
||||
"SigningPubKey": "02C26CF5D395A1CB352BE10D5AAB73FE27FC0AFAE0BD6121E55D097EBDCF394E11",
|
||||
"TxnSignature": "304402204190B6DC7D14B1CC8DDAA87F1C01FEDA6D67D598D65E1AA19D4ADE937ED14B720220662EE404438F415AD3335B9FBA1A4C2A5F72AA387740D8A011A8C53346481B1D",
|
||||
"Account": "rEE77T1E5vEFcEB9zM92jBD3rPs3kPdS1j",
|
||||
"Destination": "r3AsrDRMNYaKNCofo9a5Us7R66RAzTigiU"
|
||||
},
|
||||
"meta": {
|
||||
"TransactionIndex": 0,
|
||||
"AffectedNodes": [
|
||||
{
|
||||
"ModifiedNode": {
|
||||
"LedgerEntryType": "AccountRoot",
|
||||
"PreviousTxnLgrSeq": 15202352,
|
||||
"PreviousTxnID": "6B3D159578F8E1CEBB268DBC5209ADB35DD075F463855886421D307026D27C67",
|
||||
"LedgerIndex": "AB5EBD00C6F12DEC32B1687A51948ADF07DC2ABDD7485E9665DCE5268039B461",
|
||||
"PreviousFields": {
|
||||
"Balance": "23493344926"
|
||||
},
|
||||
"FinalFields": {
|
||||
"Flags": 0,
|
||||
"Sequence": 1775,
|
||||
"OwnerCount": 0,
|
||||
"Balance": "25193344926",
|
||||
"Account": "r3AsrDRMNYaKNCofo9a5Us7R66RAzTigiU"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"ModifiedNode": {
|
||||
"LedgerEntryType": "AccountRoot",
|
||||
"PreviousTxnLgrSeq": 15202236,
|
||||
"PreviousTxnID": "A2C23A20377BA7A90F77F01F8E337B64E22C929C5490E2E9698A7A9BFFEC592A",
|
||||
"LedgerIndex": "C67232D5308CBE1A8C3D75284D98CC1623D906DB30774C06B3F4934BC1DE5CEE",
|
||||
"PreviousFields": {
|
||||
"Sequence": 1673,
|
||||
"Balance": "17034504878"
|
||||
},
|
||||
"FinalFields": {
|
||||
"Flags": 0,
|
||||
"Sequence": 1674,
|
||||
"OwnerCount": 0,
|
||||
"Balance": "15334489878",
|
||||
"Account": "rEE77T1E5vEFcEB9zM92jBD3rPs3kPdS1j"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"TransactionResult": "tesSUCCESS"
|
||||
}
|
||||
}
|
||||
]
|
||||
14
test/fixtures/api/requests/compute-ledger-hash.json
vendored
Normal file
14
test/fixtures/api/requests/compute-ledger-hash.json
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"accepted": true,
|
||||
"stateHash": "D9ABF622DA26EEEE48203085D4BC23B0F77DC6F8724AC33D975DA3CA492D2E44",
|
||||
"closeTime": 492656470,
|
||||
"parentCloseTime": 492656460,
|
||||
"closeFlags": 0,
|
||||
"closeTimeHuman": "2015-Aug-12 01:01:10",
|
||||
"closeTimeResolution": 10,
|
||||
"closed": true,
|
||||
"ledgerVersion": 15202439,
|
||||
"parentLedgerHash": "12724A65B030C15A1573AA28B1BBB5DF3DA4589AA3623675A31CAE69B23B1C4E",
|
||||
"totalDrops": "99998831688050493",
|
||||
"transactionHash": "325EACC5271322539EEEC2D6A5292471EF1B3E72AE7180533EFC3B8F0AD435C8"
|
||||
}
|
||||
17
test/fixtures/api/requests/getpaths/no-paths-with-currencies.json
vendored
Normal file
17
test/fixtures/api/requests/getpaths/no-paths-with-currencies.json
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"source": {
|
||||
"address": "rwBYyfufTzk77zUSKEu4MvixfarC35av1J",
|
||||
"currencies": [
|
||||
{
|
||||
"currency": "USD"
|
||||
}
|
||||
]
|
||||
},
|
||||
"destination": {
|
||||
"address": "r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59",
|
||||
"amount": {
|
||||
"value": "1000002",
|
||||
"currency": "USD"
|
||||
}
|
||||
}
|
||||
}
|
||||
12
test/fixtures/api/requests/getpaths/no-paths.json
vendored
Normal file
12
test/fixtures/api/requests/getpaths/no-paths.json
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"source": {
|
||||
"address": "rwBYyfufTzk77zUSKEu4MvixfarC35av1J"
|
||||
},
|
||||
"destination": {
|
||||
"address": "r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59",
|
||||
"amount": {
|
||||
"value": "1000002",
|
||||
"currency": "USD"
|
||||
}
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user