mirror of
https://github.com/Xahau/xahau.js.git
synced 2025-11-06 13:55:49 +00:00
Compare commits
83 Commits
v0.8.0-bet
...
v0.8.1-rc1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
519ddee092 | ||
|
|
3e0fcc5b8b | ||
|
|
b1972985c4 | ||
|
|
51c42e9257 | ||
|
|
86dcbcc671 | ||
|
|
3b7cd9d84f | ||
|
|
1073ec6214 | ||
|
|
14a5e42a63 | ||
|
|
b4564a86b4 | ||
|
|
03386a61e9 | ||
|
|
8bb2623360 | ||
|
|
ab0e4188b3 | ||
|
|
42c853dbf4 | ||
|
|
ce48a1793b | ||
|
|
6177543d98 | ||
|
|
9697bfa817 | ||
|
|
70425ab5c8 | ||
|
|
7cccb451d2 | ||
|
|
a39fb9d551 | ||
|
|
8f7cdc6e4f | ||
|
|
8f7e365b03 | ||
|
|
64735e523f | ||
|
|
f126610219 | ||
|
|
2caef539ce | ||
|
|
468fb87749 | ||
|
|
4f4808ff15 | ||
|
|
e6bbca7df1 | ||
|
|
e7d1095be2 | ||
|
|
a08d5ce6e5 | ||
|
|
fec2f5578d | ||
|
|
4869e30914 | ||
|
|
e1f31765e7 | ||
|
|
a3668defa8 | ||
|
|
765ff9fa32 | ||
|
|
dd04177f83 | ||
|
|
2e2ab6bffc | ||
|
|
934cacfc1b | ||
|
|
9800fd8f11 | ||
|
|
3e84996788 | ||
|
|
5a3f55d774 | ||
|
|
dbddc314a6 | ||
|
|
c98f875811 | ||
|
|
29a1ffb3b8 | ||
|
|
17770ad4c9 | ||
|
|
cc9ed435eb | ||
|
|
27a723b453 | ||
|
|
af6c9b6bd2 | ||
|
|
2d3bbecb05 | ||
|
|
51e4cb15b4 | ||
|
|
5ce91a027c | ||
|
|
3cb337e7ec | ||
|
|
c29f92f05b | ||
|
|
01903cc6d2 | ||
|
|
fff7a6bc9e | ||
|
|
678c67622d | ||
|
|
2a6aec94fb | ||
|
|
bc52f33e9c | ||
|
|
006beeb5f9 | ||
|
|
ff85b3c4c9 | ||
|
|
6c7b2b17dc | ||
|
|
131de6661c | ||
|
|
d416f31801 | ||
|
|
8885a9e3e5 | ||
|
|
27e100f4ee | ||
|
|
40dc49bd63 | ||
|
|
989509dc07 | ||
|
|
9c3f5fbcd2 | ||
|
|
0917f66cb2 | ||
|
|
66c56df7dc | ||
|
|
b5fdfa2604 | ||
|
|
a0d4a3c84d | ||
|
|
d8374b2f49 | ||
|
|
a2a2162f48 | ||
|
|
d845d094db | ||
|
|
81e805fcb9 | ||
|
|
81283eeb84 | ||
|
|
60069d0a28 | ||
|
|
7c0561d17f | ||
|
|
45ac10b215 | ||
|
|
47f5943cf7 | ||
|
|
73d30242c9 | ||
|
|
5a4e33a02d | ||
|
|
edbbbec8f3 |
4
.gitignore
vendored
4
.gitignore
vendored
@@ -17,7 +17,7 @@
|
||||
|
||||
# Ignore object files.
|
||||
*.o
|
||||
build/ripple*.js
|
||||
build/*.js
|
||||
tags
|
||||
bin/rippled
|
||||
Debug/*.*
|
||||
@@ -51,4 +51,4 @@ test/config.js
|
||||
npm-debug.log
|
||||
|
||||
# Ignore dist folder, build for bower
|
||||
dist/
|
||||
dist/
|
||||
|
||||
15
HISTORY.md
15
HISTORY.md
@@ -1,9 +1,19 @@
|
||||
##0.8.1
|
||||
|
||||
+ Wallet: Add Wallet class that generates wallets
|
||||
|
||||
+ Make npm test runnable in Windows.
|
||||
|
||||
+ Fix several stability issues, see merged PR's for details
|
||||
|
||||
##0.8.0
|
||||
|
||||
+ Orderbook: Added tracking of offer funds for determining when offers are not funded
|
||||
|
||||
+ Orderbook: Added tests
|
||||
|
||||
+ Orderbook: Update owner funds
|
||||
|
||||
+ Transactions: If transaction errs with `tefALREADY`, wait until all possible submissions err with the same before emitting `error`. Fixes a client "Transaction malformed" bug.
|
||||
|
||||
+ Transactions: Track submissions, don't bother submitting to unconnected servers
|
||||
@@ -14,6 +24,11 @@
|
||||
|
||||
+ Server: Acquire host information from server without additional request
|
||||
|
||||
+ Amount: Add a constant for the maximum canonical value that can be expressed as a Ripple value
|
||||
|
||||
+ Amount: Make Constants static fields on the class, instead of a seperate export
|
||||
|
||||
|
||||
##0.7.39
|
||||
|
||||
+ Improvements to multi-server support. Fixed an issue where a server's score was not reset and connections would keep dropping after being connected for a significant amount of time.
|
||||
|
||||
64
README.md
64
README.md
@@ -1,37 +1,36 @@
|
||||
#The Ripple JavaScript Library
|
||||
#ripple-lib
|
||||
|
||||
JavaScript client for [rippled](https://github.com/ripple/rippled)
|
||||
|
||||
[](https://travis-ci.org/ripple/ripple-lib) [](https://coveralls.io/r/ripple/ripple-lib?branch=develop)
|
||||
|
||||
[](https://www.npmjs.org/package/ripple-lib)
|
||||
|
||||
`ripple-lib` connects to the Ripple network via the WebSocket protocol and runs in Node.js as well as in the browser.
|
||||
###Features
|
||||
|
||||
###Use ripple-lib for:
|
||||
+ Connect to a rippled server in JavaScript (Node.js or browser)
|
||||
+ Issue [rippled API](https://ripple.com/wiki/JSON_Messages) requests
|
||||
+ Listen to events on the Ripple network (transaction, ledger, etc.)
|
||||
+ Sign and submit transactions to the Ripple network
|
||||
|
||||
+ Connecting to a local or remote rippled in JavaScript (Node.js or browser)
|
||||
+ Issuing [rippled API](https://ripple.com/wiki/JSON_Messages) requests
|
||||
+ Listening to events on the Ripple network (transaction, ledger, etc.)
|
||||
+ Signing and submitting transactions to the Ripple network
|
||||
###In this file
|
||||
|
||||
###In this file:
|
||||
1. [Installation](README.md#installation)
|
||||
2. [Quickstart](README.md#quickstart)
|
||||
3. [Running tests](https://github.com/ripple/ripple-lib#running-tests)
|
||||
|
||||
1. Overview
|
||||
2. [Getting `ripple-lib`](README.md#getting-ripple-lib)
|
||||
3. [Quickstart](README.md#quickstart)
|
||||
4. [Running tests](https://github.com/ripple/ripple-lib#running-tests)
|
||||
###Additional documentation
|
||||
|
||||
###For additional documentation see:
|
||||
1. [Guides](docs/GUIDES.md)
|
||||
2. [API Reference](docs/REFERENCE.md)
|
||||
3. [Wiki](https://ripple.com/wiki/Ripple_JavaScript_library)
|
||||
|
||||
1. [The `ripple-lib` Guides (docs/GUIDES.md)](docs/GUIDES.md)
|
||||
2. [The `ripple-lib` API Reference (docs/REFERENCE.md)](docs/REFERENCE.md)
|
||||
3. https://ripple.com/wiki/Ripple_JavaScript_library
|
||||
###Also see
|
||||
|
||||
###Also see:
|
||||
+ [The Ripple wiki](https://ripple.com/wiki)
|
||||
+ [ripple.com](https://ripple.com)
|
||||
|
||||
+ https://ripple.com/wiki
|
||||
+ https://ripple.com
|
||||
|
||||
##Getting `ripple-lib`
|
||||
##Installation
|
||||
|
||||
**Via npm for Node.js**
|
||||
|
||||
@@ -39,7 +38,7 @@
|
||||
$ npm install ripple-lib
|
||||
```
|
||||
|
||||
**Building ripple-lib for browser client**
|
||||
**Building ripple-lib for browser use**
|
||||
|
||||
```
|
||||
$ git clone https://github.com/ripple/ripple-lib
|
||||
@@ -51,7 +50,7 @@ Then use the minified `build/ripple-*-min.js` in your webpage
|
||||
|
||||
##Quickstart
|
||||
|
||||
`Remote` ([remote.js](https://github.com/ripple/ripple-lib/blob/develop/src/js/ripple/remote.js)) is the module responsible for managing connections to `rippled` servers:
|
||||
`Remote.js` ([remote.js](https://github.com/ripple/ripple-lib/blob/develop/src/js/ripple/remote.js)) is the point of entry for interacting with rippled
|
||||
|
||||
```js
|
||||
/* Loading ripple-lib with Node.js */
|
||||
@@ -62,35 +61,24 @@ var Remote = require('ripple-lib').Remote;
|
||||
|
||||
var remote = new Remote({
|
||||
// see the API Reference for available options
|
||||
trusted: true,
|
||||
local_signing: true,
|
||||
local_fee: true,
|
||||
fee_cushion: 1.5,
|
||||
servers: [
|
||||
{
|
||||
host: 's1.ripple.com'
|
||||
, port: 443
|
||||
, secure: true
|
||||
}
|
||||
]
|
||||
servers: [ 'wss://s1.ripple.com:443' ]
|
||||
});
|
||||
|
||||
remote.connect(function() {
|
||||
/* remote connected */
|
||||
remote.request('server_info', function(err, info) {
|
||||
|
||||
// see the API Reference for available functions
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
See [The `ripple-lib` Guides](docs/GUIDES.md) and [The `ripple-lib` API Reference](docs/REFERENCE.md) for walkthroughs and details about all of the available functions and options.
|
||||
|
||||
##Running tests
|
||||
|
||||
1. Clone the repository
|
||||
|
||||
2. `cd` into the repository and install dependencies with `npm install`
|
||||
|
||||
3. `npm test` or `make test` or `node_modules\.bin\mocha test\*-test.js`
|
||||
3. `npm test` or `node_modules/.bin/mocha test/*-test.js`
|
||||
|
||||
**Generating code coverage**
|
||||
|
||||
|
||||
4550
build/sjcl.js
4550
build/sjcl.js
File diff suppressed because it is too large
Load Diff
149
docs/GUIDES.md
149
docs/GUIDES.md
@@ -1,26 +1,36 @@
|
||||
#`ripple-lib` Guides
|
||||
#Guides
|
||||
|
||||
This file provides step-by-step walkthroughs for some of the most common usages of `ripple-lib`.
|
||||
|
||||
###Guides in this document:
|
||||
###In this document
|
||||
|
||||
1. [Connecting to the Ripple network with `Remote`](GUIDES.md#1-connecting-to-the-ripple-network-with-remote)
|
||||
2. [Using `Remote` functions and `Request` objects](GUIDES.md#2-using-remote-functions-and-request-objects)
|
||||
3. [Submitting a payment to the network](GUIDES.md#3-submitting-a-payment-to-the-network)
|
||||
1. [Connecting to the Ripple network with `Remote`](GUIDES.md#connecting-to-the-ripple-network)
|
||||
2. [Using `Remote` functions and `Request` objects](GUIDES.md#sending-rippled-API-requests)
|
||||
3. [Listening to the network](GUIDES.md#listening-to-the-network)
|
||||
4. [Submitting a payment to the network](GUIDES.md#submitting-a-payment-to-the-network)
|
||||
* [A note on transaction fees](GUIDES.md#a-note-on-transaction-fees)
|
||||
4. [Submitting a trade offer to the network](GUIDES.md#4-submitting-a-trade-offer-to-the-network)
|
||||
5. [Listening to the network](GUIDES.md#5-listening-to-the-network)
|
||||
5. [Submitting a trade offer to the network](GUIDES.md#submitting-a-trade-offer-to-the-network)
|
||||
|
||||
###Also see
|
||||
|
||||
###Also see:
|
||||
1. [The ripple-lib README](../README.md)
|
||||
2. [The ripple-lib API Reference](REFERENCE.md)
|
||||
|
||||
1. [The `ripple-lib` README](../README.md)
|
||||
2. [The `ripple-lib` API Reference](REFERENCE.md)
|
||||
##Generating a new Ripple Wallet
|
||||
|
||||
##1. Connecting to the Ripple network with `Remote`
|
||||
```js
|
||||
var Wallet = require('ripple-lib').Wallet;
|
||||
|
||||
1. [Get `ripple-lib`](README.md#getting-ripple-lib)
|
||||
2. Load the `ripple-lib` module into a Node.js file or webpage:
|
||||
var wallet = Wallet.generate();
|
||||
console.log(wallet);
|
||||
// { address: 'rEf4sbVobiiDGExrNj2PkNHGMA8eS6jWh3',
|
||||
// secret: 'shFh4a38EZpEdZxrLifEnVPAoBRce' }
|
||||
```
|
||||
|
||||
##Connecting to the Ripple network
|
||||
|
||||
1. [Get ripple-lib](README.md#getting-ripple-lib)
|
||||
2. Load the ripple-lib module into a Node.js file or webpage:
|
||||
```js
|
||||
/* Loading ripple-lib with Node.js */
|
||||
var Remote = require('ripple-lib').Remote;
|
||||
@@ -37,32 +47,36 @@ This file provides step-by-step walkthroughs for some of the most common usages
|
||||
});
|
||||
```
|
||||
__NOTE:__ See the API Reference for available [`Remote` options](REFERENCE.md#1-remote-options)
|
||||
|
||||
4. You're connected! Read on to see what to do now.
|
||||
|
||||
|
||||
##2. Using `Remote` functions and `Request` objects
|
||||
##Sending rippled API requests
|
||||
|
||||
All `Remote` functions return a `Request` object.
|
||||
`Remote` contains functions for constructing a `Request` object.
|
||||
|
||||
A `Request` is an `EventEmitter` so you can listen for success or failure events -- or, instead, you can provide a callback to the `Remote` function.
|
||||
A `Request` is an `EventEmitter` so you can listen for success or failure events -- or, instead, you can provide a callback.
|
||||
|
||||
Here is an example, using `request_server_info()`, of how `Remote` functions can be used with event listeners (the first code block) or with a callback (the second block):
|
||||
Here is an example, using [request_server_info](https://ripple.com/wiki/JSON_Messages#server_info).
|
||||
|
||||
+ Using a `Remote` function with `Request` event listeners:
|
||||
+ Constructing a `Request` with event listeners
|
||||
```js
|
||||
var request = remote.request_server_info();
|
||||
request.on('success', function(res) {
|
||||
var request = remote.request('server_info');
|
||||
|
||||
request.on('success', function onSuccess(res) {
|
||||
//handle success
|
||||
});
|
||||
request.on('error', function(err) {
|
||||
|
||||
request.on('error', function onError(err) {
|
||||
//handle error
|
||||
});
|
||||
request.request(); // this triggers the request if it has not already been sent to the server
|
||||
|
||||
request.request();
|
||||
```
|
||||
|
||||
+ Using a `Remote` function with a callback:
|
||||
+ Using a callback:
|
||||
```js
|
||||
remote.request_server_info(function(err, res) {
|
||||
remote.request('server_info', function(err, res) {
|
||||
if (err) {
|
||||
//handle error
|
||||
} else {
|
||||
@@ -74,9 +88,44 @@ remote.request_server_info(function(err, res) {
|
||||
__NOTE:__ See the API Reference for available [`Remote` functions](REFERENCE.md#2-remote-functions)
|
||||
|
||||
|
||||
##Listening to the network
|
||||
|
||||
See the [wiki](https://ripple.com/wiki/JSON_Messages#subscribe) for details on subscription requests.
|
||||
|
||||
##3. Submitting a payment to the network
|
||||
```js
|
||||
/* Loading ripple-lib with Node.js */
|
||||
var Remote = require('ripple-lib').Remote;
|
||||
|
||||
/* Loading ripple-lib in a webpage */
|
||||
// var Remote = ripple.Remote;
|
||||
|
||||
var remote = new Remote({options});
|
||||
|
||||
remote.connect(function() {
|
||||
var request = remote.request('subscribe');
|
||||
|
||||
request.addStream('ledger'); //remote will emit `ledger_closed`
|
||||
request.addStream('transactions'); //remote will emit `transaction`
|
||||
|
||||
request.on('ledger_closed', function onLedgerClosed(ledgerData) {
|
||||
//handle ledger
|
||||
});
|
||||
|
||||
remote.on('transaction', function onTransacstion(transaction) {
|
||||
//handle transaction
|
||||
});
|
||||
|
||||
remote.request(function(err) {
|
||||
if (err) {
|
||||
} else {
|
||||
}
|
||||
});
|
||||
});
|
||||
```
|
||||
* https://ripple.com/wiki/RPC_API#transactions_stream_messages
|
||||
* https://ripple.com/wiki/RPC_API#ledger_stream_messages
|
||||
|
||||
##Submitting a payment to the network
|
||||
|
||||
Submitting a payment transaction to the Ripple network involves connecting to a `Remote`, creating a transaction, signing it with the user's secret, and submitting it to the `rippled` server. Note that the `Amount` module is used to convert human-readable amounts like '1XRP' or '10.50USD' to the type of Amount object used by the Ripple network.
|
||||
|
||||
@@ -97,13 +146,11 @@ var AMOUNT = Amount.from_human('1XRP');
|
||||
var remote = new Remote({ /* Remote options */ });
|
||||
|
||||
remote.connect(function() {
|
||||
remote.set_secret(MY_ADDRESS, MY_SECRET);
|
||||
remote.setSecret(MY_ADDRESS, MY_SECRET);
|
||||
|
||||
var transaction = remote.transaction();
|
||||
|
||||
transaction.payment({
|
||||
from: MY_ADDRESS,
|
||||
to: RECIPIENT,
|
||||
var transaction = remote.createTransaction('Payment', {
|
||||
account: MY_ADDRESS,
|
||||
destination: RECIPIENT,
|
||||
amount: AMOUNT
|
||||
});
|
||||
|
||||
@@ -151,12 +198,10 @@ var EXPIRATION = tomorrow;
|
||||
var remote = new Remote({ /* Remote options */ });
|
||||
|
||||
remote.connect(function() {
|
||||
remote.set_secret(MY_ADDRESS, MY_SECRET);
|
||||
remote.setSecret(MY_ADDRESS, MY_SECRET);
|
||||
|
||||
var transaction = remote.transaction();
|
||||
|
||||
transaction.offer_create({
|
||||
from: MY_ADDRESS,
|
||||
var transaction = remote.createTransaction('OfferCreate', {
|
||||
account: MY_ADDRESS,
|
||||
buy: BUY_AMOUNT,
|
||||
sell: SELL_AMOUNT,
|
||||
expiration: EXPIRATION
|
||||
@@ -167,35 +212,3 @@ remote.connect(function() {
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
##5. Listening to the network
|
||||
|
||||
In some (relatively rare) cases you may want to subscribe to the network event feed and listen for transactions and the ledger closings. [Ripple.com](http://www.ripple.com) uses this feature of `ripple-lib` to display the live feed on the top of each page and the ledger closing visualization on the [Developers page](http://ripple.com/devs).
|
||||
|
||||
```js
|
||||
/* Loading ripple-lib with Node.js */
|
||||
var Remote = require('ripple-lib').Remote;
|
||||
|
||||
/* Loading ripple-lib in a webpage */
|
||||
// var Remote = ripple.Remote;
|
||||
|
||||
var remote = new Remote({options});
|
||||
|
||||
remote.connect(function() {
|
||||
remote.on('transaction_all', transactionListener);
|
||||
remote.on('ledger_closed', ledgerListener);
|
||||
});
|
||||
|
||||
function transactionListener (transaction_data) {
|
||||
// handle transaction_data
|
||||
// see https://ripple.com/wiki/RPC_API#transactions_stream_messages for the format of transaction_data
|
||||
}
|
||||
|
||||
function ledgerListener (ledger_data) {
|
||||
// handle ledger_data
|
||||
// see https://ripple.com/wiki/RPC_API#ledger_stream_messages for the format of ledger_data
|
||||
}
|
||||
```
|
||||
* https://ripple.com/wiki/RPC_API#transactions_stream_messages
|
||||
* https://ripple.com/wiki/RPC_API#ledger_stream_messages
|
||||
|
||||
|
||||
@@ -1,28 +1,26 @@
|
||||
#`ripple-lib` API Reference
|
||||
#API Reference
|
||||
|
||||
__(More examples coming soon!)__
|
||||
|
||||
###In this document:
|
||||
|
||||
1. [`Remote` options](REFERENCE.md#1-remote-options)
|
||||
2. [`Remote` functions](REFERENCE.md#2-remote-functions)
|
||||
+ [Server info functions](REFERENCE.md#server-info-functions)
|
||||
+ [Ledger query functions](REFERENCE.md#ledger-query-functions)
|
||||
+ [Transaction query functions](REFERENCE.md#transaction-query-functions)
|
||||
+ [Account query functions](REFERENCE.md#account-query-functions)
|
||||
+ [Order book query functions](REFERENCE.md#order-book-query-functions)
|
||||
+ [Transaction submission functions](REFERENCE.md#transaction-submission-functions)
|
||||
3. [`Transaction` events](REFERENCE.md#3-transaction-events)
|
||||
4. [`Amount` objects](REFERENCE.md#4-amount-objects)
|
||||
|
||||
1. [`Remote` options](REFERENCE.md#remote-options)
|
||||
2. [`Request` constructors](REFERENCE.md#request-constructor-functions)
|
||||
+ [Server requests](REFERENCE.md#server-requests)
|
||||
+ [Ledger requests](REFERENCE.md#ledger-requests)
|
||||
+ [Transaction requests](REFERENCE.md#transaction-requests)
|
||||
+ [Account requests](REFERENCE.md#account-requests)
|
||||
+ [Orderbook requests](REFERENCE.md#orderbook-requests)
|
||||
+ [Transaction requests](REFERENCE.md#transaction-requests)
|
||||
3. [`Transaction` constructors](REFERENCE.md#transaction-constructors)
|
||||
+ [Transaction events](REFERENCE.md#transaction-events)
|
||||
|
||||
###Also see:
|
||||
|
||||
1. [The `ripple-lib` README](../README.md)
|
||||
2. [The `ripple-lib` GUIDES](GUIDES.md)
|
||||
1. [The ripple-lib README](../README.md)
|
||||
2. [The ripple-lib GUIDES](GUIDES.md)
|
||||
|
||||
|
||||
#1. `Remote` options
|
||||
#Remote options
|
||||
|
||||
```js
|
||||
/* Loading ripple-lib with Node.js */
|
||||
@@ -31,98 +29,86 @@ var Remote = require('ripple-lib').Remote;
|
||||
/* Loading ripple-lib in a webpage */
|
||||
// var Remote = ripple.Remote;
|
||||
|
||||
var remote = new Remote({options});
|
||||
var options = { };
|
||||
|
||||
var remote = new Remote(options);
|
||||
```
|
||||
|
||||
A new `Remote` can be created with the following options:
|
||||
|
||||
+ `trace` Log all of the events emitted (boolean)
|
||||
+ `max_listeners` Set maxListeners for remote; prevents EventEmitter warnings (number)
|
||||
+ `connection_offset` Connect to remote servers on supplied interval (number in seconds)
|
||||
+ `trusted` truthy, if remote is trusted (boolean)
|
||||
+ `local_fee` Set whether the transaction fee range will be set locally (boolean, default is true, see [A note on transaction fees](GUIDES.md#a-note-on-transaction-fees))
|
||||
+ `fee_cushion` Extra fee multiplier to account for async fee changes (number, e.g. 1.5, see [A note on transaction fees](GUIDES.md#a-note-on-transaction-fees))
|
||||
+ `max_fee` Maximum acceptable transaction fee (number in [XRP drops](https://ripple.com/wiki/Ripple_credits#Notes_on_drops), see [A note on transaction fees](GUIDES.md#a-note-on-transaction-fees))
|
||||
+ `servers` Array of server objects of the following form:
|
||||
+ `trace` *boolean default: false* Log all of the events emitted
|
||||
+ `max_listeners` *number default: 0* Set maxListeners for servers
|
||||
+ `trusted` *boolean default: false*, if remote is trusted (boolean)
|
||||
+ `local_signing` *boolean default: true*
|
||||
+ `local_fee` *boolean default: true* Set whether the transaction fee range will be set locally, see [A note on transaction fees](GUIDES.md#a-note-on-transaction-fees))
|
||||
+ `fee_cushion` *number default: 1.2* Extra fee multiplier to account for async fee changes, see [A note on transaction fees](GUIDES.md#a-note-on-transaction-fees))
|
||||
+ `max_fee` *number default: Infinity* Maximum acceptable transaction fee, see [A note on transaction fees](GUIDES.md#a-note-on-transaction-fees)
|
||||
+ `servers` *array* Array of server objects of the following form:
|
||||
|
||||
```js
|
||||
{
|
||||
host: <string>
|
||||
, port: <number>
|
||||
, secure: <boolean>
|
||||
{
|
||||
host: <string>,
|
||||
port: <number>,
|
||||
secure: <boolean>
|
||||
}
|
||||
```
|
||||
+ `local_signing`
|
||||
|
||||
#2. `Remote` functions
|
||||
|
||||
|
||||
|
||||
##Server info functions
|
||||
|
||||
**[requestServerInfo([callback])](https://ripple.com/wiki/RPC_API#server_info)**
|
||||
|
||||
Returns information about the state of the server. If you are connected to multiple servers and want to select by a particular host, use `request.set_server`. Example:
|
||||
or
|
||||
|
||||
```js
|
||||
var request = remote.request_server_info();
|
||||
request.set_server('my.hostname');
|
||||
request.callback(function(err, res) {
|
||||
|
||||
});
|
||||
request.request();
|
||||
'wss://host:port'
|
||||
```
|
||||
|
||||
**[requestUnlList([callback])](https://ripple.com/wiki/RPC_API#unl_list)**
|
||||
#Request constructor functions
|
||||
|
||||
**[requestUnlAdd(addr, comment, [callback])](https://ripple.com/wiki/RPC_API#unl_add)**
|
||||
##Server requests
|
||||
|
||||
**[requestUnlDelete(node, [callback])](https://ripple.com/wiki/RPC_API#unl_delete)**
|
||||
**[server_info([callback])](https://ripple.com/wiki/JSON_Messages#server_info)**
|
||||
|
||||
**[requestPeers([callback])](https://ripple.com/wiki/RPC_API#peers)**
|
||||
Returns information about the state of the server. If you are connected to multiple servers and want to select by a particular host, use `request.setServer`. Example:
|
||||
|
||||
```js
|
||||
var request = remote.request('server_info');
|
||||
|
||||
request.setServer('wss://s1.ripple.com');
|
||||
|
||||
request.request(function(err, res) {
|
||||
|
||||
});
|
||||
```
|
||||
|
||||
**[unl_list([callback])](https://ripple.com/wiki/JSON_Messages#unl_list)**
|
||||
|
||||
**[unl_add(addr, comment, [callback])](https://ripple.com/wiki/JSON_Messages#unl_add)**
|
||||
|
||||
**[unl_delete(node, [callback])](https://ripple.com/wiki/JSON_Messages#unl_delete)**
|
||||
|
||||
**[requestPeers([callback])](https://ripple.com/wiki/JSON_Messages#peers)**
|
||||
|
||||
|
||||
**[requestConnect(ip, port, [callback])](https://ripple.com/wiki/RPC_API#connect)**
|
||||
**[connect(ip, port, [callback])](https://ripple.com/wiki/JSON_Messages#connect)**
|
||||
|
||||
##Ledger requests
|
||||
|
||||
**[ledger(ledger, [opts], [callback])](https://ripple.com/wiki/JSON_Messages#ledger)**
|
||||
|
||||
##Ledger query functions
|
||||
**ledger_header([callback])**
|
||||
|
||||
**[requestLedger(ledger, [opts], [callback])](https://ripple.com/wiki/RPC_API#ledger)**
|
||||
**[ledger_current([callback])](https://ripple.com/wiki/JSON_Messages#ledger_current)**
|
||||
|
||||
**requestLedgerHeader([callback])**
|
||||
**[ledger_entry(type, [callback])](https://ripple.com/wiki/JSON_Messages#ledger_entry)**
|
||||
|
||||
**[requestLedgerCurrent([callback])](https://ripple.com/wiki/RPC_API#ledger_current)**
|
||||
|
||||
**[requestLedgerEntry(type, [callback])](https://ripple.com/wiki/RPC_API#ledger_entry)**
|
||||
|
||||
**[requestSubscribe(streams, [callback])](https://ripple.com/wiki/RPC_API#subscribe)**
|
||||
**[subscribe([streams], [callback])](https://ripple.com/wiki/JSON_Messages#subscribe)**
|
||||
|
||||
Start receiving selected streams from the server.
|
||||
|
||||
**[requestUnsubscribe(streams, [callback])](https://ripple.com/wiki/RPC_API#unsubscribe)**
|
||||
**[unsubscribe([streams], [callback])](https://ripple.com/wiki/JSON_Messages#unsubscribe)**
|
||||
|
||||
Stop receiving selected streams from the server.
|
||||
|
||||
##Account requests
|
||||
|
||||
|
||||
|
||||
##Transaction query functions
|
||||
|
||||
**[requestTransactionEntry(hash, [ledger_hash], [callback])](https://ripple.com/wiki/RPC_API#transaction_entry)**
|
||||
|
||||
Searches a particular ledger for a transaction hash. Default ledger is the open ledger.
|
||||
|
||||
**[requestTx(hash, [callback])](https://ripple.com/wiki/RPC_API#tx)**
|
||||
|
||||
Searches ledger history for validated transaction hashes.
|
||||
|
||||
|
||||
|
||||
|
||||
##Account query functions
|
||||
|
||||
**[requestAccountInfo(account, [callback])](https://ripple.com/wiki/RPC_API#account_info)**
|
||||
**[account_info(account, [callback])](https://ripple.com/wiki/JSON_Messages#account_info)**
|
||||
|
||||
Return information about the specified account.
|
||||
|
||||
@@ -143,13 +129,13 @@ Return information about the specified account.
|
||||
}
|
||||
```
|
||||
|
||||
**[requestAccountLines(accountID, account_index, current, [callback])](https://ripple.com/wiki/RPC_API#account_lines)**
|
||||
**[account_lines(accountID, [account_index], [ledger], [callback])](https://ripple.com/wiki/JSON_Messages#account_lines)**
|
||||
|
||||
**[requestAccountOffers(accountID, account_index, current, [callback])](https://ripple.com/wiki/RPC_API#account_offers)**
|
||||
**[account_offers(accountID, [account_index], [ledger], [callback])](https://ripple.com/wiki/JSON_Messages#account_offers)**
|
||||
|
||||
Return the specified account's outstanding offers.
|
||||
|
||||
**[requestAccountTx(opts, [callback])](https://ripple.com/wiki/RPC_API#account_tx)**
|
||||
**[account_tx(options, [callback])](https://ripple.com/wiki/JSON_Messages#account_tx)**
|
||||
|
||||
Fetch a list of transactions that applied to this account.
|
||||
|
||||
@@ -167,92 +153,150 @@ Options:
|
||||
+ `fwd_marker`
|
||||
+ `rev_marker`
|
||||
|
||||
**[requestWalletAccounts(seed, [callback])](https://ripple.com/wiki/RPC_API#wallet_accounts)**
|
||||
**[wallet_accounts(seed, [callback])](https://ripple.com/wiki/JSON_Messages#wallet_accounts)**
|
||||
|
||||
Return a list of accounts for a wallet.
|
||||
Return a list of accounts for a wallet. *Requires trusted remote*
|
||||
|
||||
+ requires trusted remote
|
||||
|
||||
**requestAccountBalance(account, ledger, [callback])**
|
||||
**account_balance(account, [ledger], [callback])**
|
||||
|
||||
Get the balance for an account. Returns an [Amount](https://github.com/ripple/ripple-lib/blob/develop/src/js/ripple/amount.js) object.
|
||||
|
||||
**requestAccountFlags(account, current, [callback])**
|
||||
**account_flags(account, [ledger], [callback])**
|
||||
|
||||
Return the flags for an account.
|
||||
|
||||
**requestOwnerCount(account, current, [callback])**
|
||||
**owner_count(account, [ledger], [callback])**
|
||||
|
||||
Return the owner count for an account.
|
||||
|
||||
**requestRippleBalance(account, issuer, currency, current, [callback])**
|
||||
**ripple_balance(account, issuer, currency, [ledger], [callback])**
|
||||
|
||||
Return a request to get a ripple balance
|
||||
|
||||
##Orderbook requests
|
||||
|
||||
**[book_offers(options, [callback])](https://ripple.com/wiki/JSON_Messages#book_offers)**
|
||||
|
||||
|
||||
##Order book query functions
|
||||
|
||||
**[requestBookOffers(gets, pays, taker, [callback])](https://ripple.com/wiki/RPC_API#book_offers)**
|
||||
|
||||
Return the offers for an order book as one or more pages.
|
||||
Return the offers for an order book, also called a *snapshot*
|
||||
|
||||
```js
|
||||
var request = remote.request_book_offers({
|
||||
gets: {
|
||||
var request = remote.request('book_offers', {
|
||||
taker_gets: {
|
||||
'currency':'XRP'
|
||||
},
|
||||
pays: {
|
||||
taker_pays: {
|
||||
'currency':'USD',
|
||||
'issuer': 'rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B'
|
||||
}
|
||||
});
|
||||
|
||||
request.request();
|
||||
request.request(function(err, offers) {
|
||||
//handle offers
|
||||
});
|
||||
```
|
||||
|
||||
##Transaction requests
|
||||
|
||||
**[transaction_entry(hash, [ledger_hash], [callback])](https://ripple.com/wiki/JSON_Messages#transaction_entry)**
|
||||
|
||||
Searches a particular ledger for a transaction hash. Default ledger is the open ledger.
|
||||
|
||||
##Transaction submission functions
|
||||
**[tx(hash, [callback])](https://ripple.com/wiki/JSON_Messages#tx)**
|
||||
|
||||
**[requestSign(secret, tx_json, [callback])](https://ripple.com/wiki/RPC_API#sign)**
|
||||
Searches ledger history for validated transaction hashes.
|
||||
|
||||
Sign a transaction.
|
||||
**[sign(secret, tx_json, [callback])](https://ripple.com/wiki/JSON_Messages#sign)**
|
||||
|
||||
+ requires trusted remote
|
||||
Sign a transaction. *Requires trusted remote*
|
||||
|
||||
**[requestSubmit([callback])](https://ripple.com/wiki/RPC_API#submit)**
|
||||
**[submit([callback])](https://ripple.com/wiki/JSON_Messages#submit)**
|
||||
|
||||
Submit a transaction to the network. This command is used internally to submit transactions with a greater degree of reliability. See [Submitting a payment to the network](GUIDES.md#3-submitting-a-payment-to-the-network) for details.
|
||||
|
||||
**[ripple_path_find(src_account, dst_account, dst_amount, src_currencies, [callback])](https://ripple.com/wiki/JSON_Messages#path_find)**
|
||||
|
||||
**[requestRipplePathFind(src_account, dst_account, dst_amount, src_currencies, [callback])](https://ripple.com/wiki/RPC_API#path_find)**
|
||||
#Transaction constructors
|
||||
|
||||
Use `remote.createTransaction('TransactionType', [options])` to construct a transaction. To submit, use `transaction.submit([callback])`.
|
||||
|
||||
**transaction([destination], [source], [amount], [callback])**
|
||||
**Payment**
|
||||
|
||||
Returns a [Transaction](https://github.com/ripple/ripple-lib/blob/develop/src/js/ripple/transaction.js) object
|
||||
```js
|
||||
var transaction = remote.createTransaction('Payment', {
|
||||
account: MY_ADDRESS,
|
||||
destination: DEST_ADDRESS,
|
||||
amount: AMOUNT
|
||||
});
|
||||
```
|
||||
|
||||
**AccountSet**
|
||||
|
||||
#3. Transaction events
|
||||
```js
|
||||
var transaction = remote.createTransaction('AccountSet', {
|
||||
account: MY_ADDRESS,
|
||||
set: 'RequireDest',
|
||||
clear: 'RequireAuth'
|
||||
});
|
||||
```
|
||||
|
||||
**TrustSet**
|
||||
|
||||
```js
|
||||
var transaction = remote.createTransaction('TrustSet', {
|
||||
account: MY_ADDRESS,
|
||||
limit: '1/USD/rrrrrrrrrrrrrrrrrrrrBZbvji'
|
||||
});
|
||||
```
|
||||
|
||||
**OfferCreate**
|
||||
|
||||
```js
|
||||
var transaction = remote.createTransaction('OfferCreate', {
|
||||
account: MY_ADDRESS,
|
||||
taker_pays: '1',
|
||||
taker_gets: '1/USD/rrrrrrrrrrrrrrrrrrrrBZbvji'
|
||||
});
|
||||
```
|
||||
|
||||
##Transaction events
|
||||
|
||||
[Transaction](https://github.com/ripple/ripple-lib/blob/develop/src/js/ripple/transaction.js) objects are EventEmitters. They may emit the following events.
|
||||
|
||||
+ `final` Transaction has erred or succeeded. This event indicates that the transaction has finished processing.
|
||||
+ `error` Transaction has erred. This event is a final state.
|
||||
+ `success` Transaction succeeded. This event is a final state.
|
||||
+ `presubmit` Immediately before transaction is submitted
|
||||
+ `postsubmit` Immediately after transaction is submitted
|
||||
+ `submitted` Transaction has been submitted to the network. The submission may result in a remote error or success.
|
||||
+ `resubmitted` Transaction is beginning resubmission.
|
||||
+ `proposed` Transaction has been submitted *successfully* to the network. The transaction at this point is awaiting validation in a ledger.
|
||||
+ `timeout` Transaction submission timed out. The transaction will be resubmitted.
|
||||
+ `resubmit` Transaction is beginning resubmission.
|
||||
+ `fee_adjusted` Transaction fee has been adjusted during its pending state. The transaction fee will only be adjusted if the remote is configured for local fees, which it is by default.
|
||||
+ `abort` Transaction has been aborted. Transactions are only aborted by manual calls to `#abort`.
|
||||
+ `missing` Four ledgers have closed without detecting validated transaction
|
||||
+ `lost` Eight ledgers have closed without detecting validated transaction. Consider the transaction lost and err/finalize.
|
||||
|
||||
##Complete payment example
|
||||
|
||||
#4. Amount objects
|
||||
```js
|
||||
remote.setSecret(MY_ADDRESS, MY_SECRET);
|
||||
|
||||
var transaction = remote.createTransaction('Payment', {
|
||||
account: MY_ADDRESS,
|
||||
destination: DEST_ADDRESS,
|
||||
amount: AMOUNT
|
||||
});
|
||||
|
||||
transaction.on('resubmitted', function() {
|
||||
// initial submission failed, resubmitting
|
||||
});
|
||||
|
||||
transaction.submit(function(err, res) {
|
||||
// submission has finalized with either an error or success.
|
||||
// the transaction will not be retried after this point
|
||||
});
|
||||
```
|
||||
|
||||
#Amount objects
|
||||
|
||||
Coming Soon
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "ripple-lib",
|
||||
"version": "0.8.0-beta",
|
||||
"version": "0.8.1-rc1",
|
||||
"description": "Ripple JavaScript client library",
|
||||
"files": [
|
||||
"src/js/*",
|
||||
@@ -20,7 +20,8 @@
|
||||
"extend": "~1.2.1",
|
||||
"lru-cache": "~2.5.0",
|
||||
"superagent": "^0.18.0",
|
||||
"gulp-bump": "~0.1.10"
|
||||
"gulp-bump": "~0.1.10",
|
||||
"ripple-wallet-generator": "1.0.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"mocha": "~1.14.0",
|
||||
@@ -37,9 +38,10 @@
|
||||
"yargs": "~1.3.1"
|
||||
},
|
||||
"scripts": {
|
||||
"postinstall": "node_modules/.bin/gulp concat-sjcl",
|
||||
"build": "node_modules/.bin/gulp",
|
||||
"pretest": "node_modules/.bin/gulp concat-sjcl",
|
||||
"test": "./node_modules/.bin/istanbul test -x build/sjcl.js -x src/js/jsbn/* ./node_modules/.bin/_mocha -- --reporter spec test/*-test.js",
|
||||
"test": "./node_modules/.bin/istanbul test -x build/sjcl.js -x src/js/jsbn/* ./node_modules/mocha/bin/mocha -- --reporter spec test/*-test.js",
|
||||
"coveralls": "cat ./coverage/lcov.info | ./node_modules/.bin/coveralls"
|
||||
},
|
||||
"repository": {
|
||||
|
||||
40
scripts/publish
Normal file
40
scripts/publish
Normal file
@@ -0,0 +1,40 @@
|
||||
function exit_on_error {
|
||||
res=$?
|
||||
[[ ${res:-99} -eq 0 ]] || exit $res
|
||||
}
|
||||
|
||||
rm -rf build
|
||||
|
||||
gulp
|
||||
npm test
|
||||
exit_on_error
|
||||
|
||||
echo ""
|
||||
echo "publish to npm"
|
||||
npm publish
|
||||
exit_on_error
|
||||
|
||||
rm -rf dist
|
||||
echo ""
|
||||
echo "publish to bower"
|
||||
|
||||
git clone git@github.com:ripple/bower-ripple.git dist
|
||||
gulp bower
|
||||
exit_on_error
|
||||
|
||||
cd dist
|
||||
version=$(cat bower.json | grep -Eo '([0-9]\.?)+(-rc[0-9])?')
|
||||
echo "version: $version"
|
||||
git add ripple.js ripple-debug.js ripple-min.js bower.json
|
||||
exit_on_error
|
||||
|
||||
git commit -m "[TASK] add v$version"
|
||||
exit_on_error
|
||||
|
||||
git tag "v$version"
|
||||
exit_on_error
|
||||
|
||||
git push origin master
|
||||
git push --tags origin master
|
||||
|
||||
cd ..
|
||||
12
scripts/publish_to_bower
Normal file
12
scripts/publish_to_bower
Normal file
@@ -0,0 +1,12 @@
|
||||
rm -rf dist
|
||||
git clone git@github.com:ripple/bower-ripple.git dist
|
||||
gulp bower
|
||||
cd dist
|
||||
version=$(cat bower.json | grep -Eo '([0-9]\.?)+(-rc[0-9])?')
|
||||
echo "version: $version"
|
||||
git add ripple.js ripple-debug.js ripple-min.js bower.json
|
||||
git commit -m "[TASK] add v$version"
|
||||
git tag "v$version"
|
||||
git push origin master
|
||||
git push --tags origin master
|
||||
cd ..
|
||||
@@ -12,7 +12,25 @@ var UInt160 = require('./uint160').UInt160;
|
||||
var Seed = require('./seed').Seed;
|
||||
var Currency = require('./currency').Currency;
|
||||
|
||||
var consts = exports.consts = {
|
||||
//
|
||||
// Amount class in the style of Java's BigInteger class
|
||||
// http://docs.oracle.com/javase/1.3/docs/api/java/math/BigInteger.html
|
||||
//
|
||||
|
||||
function Amount() {
|
||||
// Json format:
|
||||
// integer : XRP
|
||||
// { 'value' : ..., 'currency' : ..., 'issuer' : ...}
|
||||
|
||||
this._value = new BigInteger(); // NaN for bad value. Always positive.
|
||||
this._offset = 0; // Always 0 for XRP.
|
||||
this._is_native = true; // Default to XRP. Only valid if value is not NaN.
|
||||
this._is_negative = false;
|
||||
this._currency = new Currency();
|
||||
this._issuer = new UInt160();
|
||||
};
|
||||
|
||||
var consts = {
|
||||
currency_xns: 0,
|
||||
currency_one: 1,
|
||||
xns_precision: 6,
|
||||
@@ -33,26 +51,17 @@ var consts = exports.consts = {
|
||||
|
||||
cMinOffset: -96,
|
||||
cMaxOffset: 80,
|
||||
|
||||
// Maximum possible amount for non-XRP currencies using the maximum mantissa
|
||||
// with maximum exponent. Corresponds to hex 0xEC6386F26FC0FFFF.
|
||||
max_value: '9999999999999999e80'
|
||||
};
|
||||
|
||||
// Add constants to Amount class
|
||||
extend(Amount, consts);
|
||||
|
||||
//
|
||||
// Amount class in the style of Java's BigInteger class
|
||||
// http://docs.oracle.com/javase/1.3/docs/api/java/math/BigInteger.html
|
||||
//
|
||||
|
||||
function Amount() {
|
||||
// Json format:
|
||||
// integer : XRP
|
||||
// { 'value' : ..., 'currency' : ..., 'issuer' : ...}
|
||||
|
||||
this._value = new BigInteger(); // NaN for bad value. Always positive.
|
||||
this._offset = 0; // Always 0 for XRP.
|
||||
this._is_native = true; // Default to XRP. Only valid if value is not NaN.
|
||||
this._is_negative = false;
|
||||
this._currency = new Currency();
|
||||
this._issuer = new UInt160();
|
||||
};
|
||||
// DEPRECATED: Use Amount instead, e.g. Amount.currency_xns
|
||||
exports.consts = consts;
|
||||
|
||||
// Given '100/USD/mtgox' return the a string with mtgox remapped.
|
||||
Amount.text_full_rewrite = function(j) {
|
||||
@@ -132,12 +141,12 @@ Amount.prototype.add = function(v) {
|
||||
var o2 = v._offset;
|
||||
|
||||
while (o1 < o2) {
|
||||
v1 = v1.divide(consts.bi_10);
|
||||
v1 = v1.divide(Amount.bi_10);
|
||||
o1 += 1;
|
||||
}
|
||||
|
||||
while (o2 < o1) {
|
||||
v2 = v2.divide(consts.bi_10);
|
||||
v2 = v2.divide(Amount.bi_10);
|
||||
o2 += 1;
|
||||
}
|
||||
|
||||
@@ -185,22 +194,22 @@ Amount.prototype.multiply = function(v) {
|
||||
var o2 = v._offset;
|
||||
|
||||
if (this.is_native()) {
|
||||
while (v1.compareTo(consts.bi_man_min_value) < 0) {
|
||||
v1 = v1.multiply(consts.bi_10);
|
||||
while (v1.compareTo(Amount.bi_man_min_value) < 0) {
|
||||
v1 = v1.multiply(Amount.bi_10);
|
||||
o1 -= 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (v.is_native()) {
|
||||
while (v2.compareTo(consts.bi_man_min_value) < 0) {
|
||||
v2 = v2.multiply(consts.bi_10);
|
||||
while (v2.compareTo(Amount.bi_man_min_value) < 0) {
|
||||
v2 = v2.multiply(Amount.bi_10);
|
||||
o2 -= 1;
|
||||
}
|
||||
}
|
||||
|
||||
result = new Amount();
|
||||
result._offset = o1 + o2 + 14;
|
||||
result._value = v1.multiply(v2).divide(consts.bi_1e14).add(consts.bi_7);
|
||||
result._value = v1.multiply(v2).divide(Amount.bi_1e14).add(Amount.bi_7);
|
||||
result._is_native = this._is_native;
|
||||
result._is_negative = this._is_negative !== v._is_negative;
|
||||
result._currency = this._currency;
|
||||
@@ -234,8 +243,8 @@ Amount.prototype.divide = function(d) {
|
||||
if (_n.is_native()) {
|
||||
_n = _n.clone();
|
||||
|
||||
while (_n._value.compareTo(consts.bi_man_min_value) < 0) {
|
||||
_n._value = _n._value.multiply(consts.bi_10);
|
||||
while (_n._value.compareTo(Amount.bi_man_min_value) < 0) {
|
||||
_n._value = _n._value.multiply(Amount.bi_10);
|
||||
_n._offset -= 1;
|
||||
}
|
||||
}
|
||||
@@ -245,15 +254,15 @@ Amount.prototype.divide = function(d) {
|
||||
if (_d.is_native()) {
|
||||
_d = _d.clone();
|
||||
|
||||
while (_d._value.compareTo(consts.bi_man_min_value) < 0) {
|
||||
_d._value = _d._value.multiply(consts.bi_10);
|
||||
while (_d._value.compareTo(Amount.bi_man_min_value) < 0) {
|
||||
_d._value = _d._value.multiply(Amount.bi_10);
|
||||
_d._offset -= 1;
|
||||
}
|
||||
}
|
||||
|
||||
result = new Amount();
|
||||
result._offset = _n._offset - _d._offset - 17;
|
||||
result._value = _n._value.multiply(consts.bi_1e17).divide(_d._value).add(consts.bi_5);
|
||||
result._value = _n._value.multiply(Amount.bi_1e17).divide(_d._value).add(Amount.bi_5);
|
||||
result._is_native = _n._is_native;
|
||||
result._is_negative = _n._is_negative !== _d._is_negative;
|
||||
result._currency = _n._currency;
|
||||
@@ -327,7 +336,7 @@ Amount.prototype.ratio_human = function(denominator, opts) {
|
||||
// To compensate, we multiply the numerator by 10^xns_precision.
|
||||
if (denominator._is_native) {
|
||||
numerator = numerator.clone();
|
||||
numerator._value = numerator._value.multiply(consts.bi_xns_unit);
|
||||
numerator._value = numerator._value.multiply(Amount.bi_xns_unit);
|
||||
numerator.canonicalize();
|
||||
}
|
||||
|
||||
@@ -385,7 +394,7 @@ Amount.prototype.product_human = function(factor, opts) {
|
||||
//
|
||||
// See also Amount#ratio_human.
|
||||
if (factor._is_native) {
|
||||
product._value = product._value.divide(consts.bi_xns_unit);
|
||||
product._value = product._value.divide(Amount.bi_xns_unit);
|
||||
product.canonicalize();
|
||||
}
|
||||
|
||||
@@ -398,7 +407,7 @@ Amount.prototype.product_human = function(factor, opts) {
|
||||
* @private
|
||||
*/
|
||||
Amount.prototype._invert = function() {
|
||||
this._value = consts.bi_1e32.divide(this._value);
|
||||
this._value = Amount.bi_1e32.divide(this._value);
|
||||
this._offset = -32 - this._offset;
|
||||
this.canonicalize();
|
||||
|
||||
@@ -428,12 +437,12 @@ Amount.prototype.canonicalize = function() {
|
||||
// Normalize _offset to 0.
|
||||
|
||||
while (this._offset < 0) {
|
||||
this._value = this._value.divide(consts.bi_10);
|
||||
this._value = this._value.divide(Amount.bi_10);
|
||||
this._offset += 1;
|
||||
}
|
||||
|
||||
while (this._offset > 0) {
|
||||
this._value = this._value.multiply(consts.bi_10);
|
||||
this._value = this._value.multiply(Amount.bi_10);
|
||||
this._offset -= 1;
|
||||
}
|
||||
}
|
||||
@@ -445,13 +454,13 @@ Amount.prototype.canonicalize = function() {
|
||||
} else {
|
||||
// Normalize mantissa to valid range.
|
||||
|
||||
while (this._value.compareTo(consts.bi_man_min_value) < 0) {
|
||||
this._value = this._value.multiply(consts.bi_10);
|
||||
while (this._value.compareTo(Amount.bi_man_min_value) < 0) {
|
||||
this._value = this._value.multiply(Amount.bi_10);
|
||||
this._offset -= 1;
|
||||
}
|
||||
|
||||
while (this._value.compareTo(consts.bi_man_max_value) > 0) {
|
||||
this._value = this._value.divide(consts.bi_10);
|
||||
while (this._value.compareTo(Amount.bi_man_max_value) > 0) {
|
||||
this._value = this._value.divide(Amount.bi_10);
|
||||
this._offset += 1;
|
||||
}
|
||||
}
|
||||
@@ -669,14 +678,14 @@ Amount.prototype.parse_human = function(j, opts) {
|
||||
fraction += '0';
|
||||
}
|
||||
this._is_native = true;
|
||||
this._value = this._value.multiply(consts.bi_xns_unit).add(new BigInteger(fraction));
|
||||
this._value = this._value.multiply(Amount.bi_xns_unit).add(new BigInteger(fraction));
|
||||
} else {
|
||||
// Other currencies have arbitrary precision
|
||||
fraction = fraction.replace(/0+$/, '');
|
||||
precision = fraction.length;
|
||||
|
||||
this._is_native = false;
|
||||
var multiplier = consts.bi_10.clone().pow(precision);
|
||||
var multiplier = Amount.bi_10.clone().pow(precision);
|
||||
this._value = this._value.multiply(multiplier).add(new BigInteger(fraction));
|
||||
this._offset = -precision;
|
||||
|
||||
@@ -875,8 +884,8 @@ Amount.prototype.parse_native = function(j) {
|
||||
this._value = new BigInteger(m[2]);
|
||||
} else {
|
||||
// Float notation : values multiplied by 1,000,000.
|
||||
var int_part = (new BigInteger(m[2])).multiply(consts.bi_xns_unit);
|
||||
var fraction_part = (new BigInteger(m[3])).multiply(new BigInteger(String(Math.pow(10, 1+consts.xns_precision-m[3].length))));
|
||||
var int_part = (new BigInteger(m[2])).multiply(Amount.bi_xns_unit);
|
||||
var fraction_part = (new BigInteger(m[3])).multiply(new BigInteger(String(Math.pow(10, 1+Amount.xns_precision-m[3].length))));
|
||||
|
||||
this._value = int_part.add(fraction_part);
|
||||
}
|
||||
@@ -885,7 +894,7 @@ Amount.prototype.parse_native = function(j) {
|
||||
this._offset = 0;
|
||||
this._is_negative = !!m[1] && this._value.compareTo(BigInteger.ZERO) !== 0;
|
||||
|
||||
if (this._value.compareTo(consts.bi_xns_max) > 0) {
|
||||
if (this._value.compareTo(Amount.bi_xns_max) > 0) {
|
||||
this._value = NaN;
|
||||
}
|
||||
} else {
|
||||
@@ -927,7 +936,7 @@ Amount.prototype.parse_value = function(j) {
|
||||
var fraction = new BigInteger(d[3]);
|
||||
var precision = d[3].length;
|
||||
|
||||
this._value = integer.multiply(consts.bi_10.clone().pow(precision)).add(fraction);
|
||||
this._value = integer.multiply(Amount.bi_10.clone().pow(precision)).add(fraction);
|
||||
this._offset = -precision;
|
||||
this._is_negative = !!d[1];
|
||||
|
||||
@@ -978,7 +987,7 @@ Amount.prototype.to_text = function(allow_nan) {
|
||||
var result = NaN;
|
||||
|
||||
if (this._is_native) {
|
||||
if (this.is_valid() && this._value.compareTo(consts.bi_xns_max) <= 0){
|
||||
if (this.is_valid() && this._value.compareTo(Amount.bi_xns_max) <= 0){
|
||||
result = this._value.toString();
|
||||
}
|
||||
} else if (this.is_zero()) {
|
||||
@@ -1086,8 +1095,8 @@ Amount.prototype.to_human = function(opts) {
|
||||
ref = this.applyInterest(opts.reference_date);
|
||||
}
|
||||
|
||||
var order = ref._is_native ? consts.xns_precision : -ref._offset;
|
||||
var denominator = consts.bi_10.clone().pow(order);
|
||||
var order = ref._is_native ? Amount.xns_precision : -ref._offset;
|
||||
var denominator = Amount.bi_10.clone().pow(order);
|
||||
var int_part = ref._value.divide(denominator).toString();
|
||||
var fraction_part = ref._value.mod(denominator).toString();
|
||||
|
||||
|
||||
@@ -527,17 +527,17 @@ BlobObj.prototype.postUpdate = function(op, pointer, params, fn) {
|
||||
};
|
||||
|
||||
/**
|
||||
* get2FA - ECDSA signed request
|
||||
* get2FA - HMAC signed request
|
||||
*/
|
||||
|
||||
BlobObj.prototype.get2FA = function (masterkey, fn) {
|
||||
BlobObj.prototype.get2FA = function (fn) {
|
||||
var config = {
|
||||
method : 'GET',
|
||||
url : this.url + '/v1/blob/' + this.id + '/2FA?device_id=' + this.device_id,
|
||||
};
|
||||
|
||||
var signedRequest = new SignedRequest(config);
|
||||
var signed = signedRequest.signAsymmetric(masterkey, this.data.account_id, this.id);
|
||||
var signed = signedRequest.signHmac(this.data.auth_secret, this.id);
|
||||
|
||||
request.get(signed.url)
|
||||
.end(function(err, resp) {
|
||||
@@ -916,14 +916,22 @@ BlobClient.get = function (options, fn) {
|
||||
* request new token to be sent for 2FA
|
||||
* @param {string} url
|
||||
* @param {string} id
|
||||
* @param {string} force_sms
|
||||
*/
|
||||
|
||||
BlobClient.requestToken = function (url, id, fn) {
|
||||
BlobClient.requestToken = function (url, id, force_sms, fn) {
|
||||
var config = {
|
||||
method : 'GET',
|
||||
url : url + '/v1/blob/' + id + '/2FA/requestToken'
|
||||
};
|
||||
|
||||
|
||||
if (force_sms && force_sms instanceof Function) {
|
||||
fn = force_sms;
|
||||
} else if (force_sms) {
|
||||
config.url += "?force_sms=true";
|
||||
}
|
||||
|
||||
request.get(config.url)
|
||||
.end(function(err, resp) {
|
||||
if (err) {
|
||||
|
||||
@@ -56,6 +56,18 @@ function keyHash(key, token) {
|
||||
return sjcl.codec.hex.fromBits(sjcl.bitArray.bitSlice(hmac.encrypt(token), 0, 256));
|
||||
};
|
||||
|
||||
/**
|
||||
* add entropy at each call to get random words
|
||||
* @param {number} nWords
|
||||
*/
|
||||
function randomWords (nWords) {
|
||||
for (var i = 0; i < 8; i++) {
|
||||
sjcl.random.addEntropy(Math.random(), 32, "Math.random()");
|
||||
}
|
||||
|
||||
return sjcl.random.randomWords(nWords);
|
||||
}
|
||||
|
||||
/****** exposed functions ******/
|
||||
|
||||
/**
|
||||
@@ -213,7 +225,7 @@ Crypt.isValidAddress = function (address) {
|
||||
*/
|
||||
|
||||
Crypt.createSecret = function (nWords) {
|
||||
return sjcl.codec.hex.fromBits(sjcl.random.randomWords(nWords));
|
||||
return sjcl.codec.hex.fromBits(randomWords(nWords));
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -221,7 +233,7 @@ Crypt.createSecret = function (nWords) {
|
||||
*/
|
||||
|
||||
Crypt.createMaster = function () {
|
||||
return base.encode_check(33, sjcl.codec.bytes.fromBits(sjcl.random.randomWords(4)));
|
||||
return base.encode_check(33, sjcl.codec.bytes.fromBits(randomWords(4)));
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@ exports.RippleTxt = require('./rippletxt').RippleTxt;
|
||||
exports.binformat = require('./binformat');
|
||||
exports.utils = require('./utils');
|
||||
exports.Server = require('./server').Server;
|
||||
exports.Wallet = require('./wallet');
|
||||
|
||||
// Important: We do not guarantee any specific version of SJCL or for any
|
||||
// specific features to be included. The version and configuration may change at
|
||||
|
||||
@@ -89,6 +89,7 @@ function OrderBook(remote, getsC, getsI, paysC, paysI, key) {
|
||||
|
||||
this._remote.on('disconnect', function() {
|
||||
self._ownerFunds = { };
|
||||
self._offerCounts = { };
|
||||
self._synchronized = false;
|
||||
});
|
||||
|
||||
@@ -153,10 +154,20 @@ OrderBook.prototype.subscribe = function() {
|
||||
log.info('subscribing', this._key);
|
||||
}
|
||||
|
||||
this.requestTransferRate();
|
||||
var steps = [
|
||||
function(callback) {
|
||||
self.requestTransferRate(callback);
|
||||
},
|
||||
function(callback) {
|
||||
self.requestOffers(callback);
|
||||
},
|
||||
function(callback) {
|
||||
self.subscribeTransactions(callback);
|
||||
}
|
||||
];
|
||||
|
||||
this.requestOffers().once('success', function() {
|
||||
self.subscribeTransactions();
|
||||
async.series(steps, function(err) {
|
||||
//XXX What now?
|
||||
});
|
||||
};
|
||||
|
||||
@@ -176,8 +187,10 @@ OrderBook.prototype.unsubscribe = function() {
|
||||
this._shouldSubscribe = false;
|
||||
|
||||
OrderBook.EVENTS.forEach(function(event) {
|
||||
this.removeAllListeners(event);
|
||||
}, this);
|
||||
if (self.listeners(event).length > 0) {
|
||||
self.removeAllListeners(event);
|
||||
}
|
||||
});
|
||||
|
||||
this.emit('unsubscribe');
|
||||
};
|
||||
@@ -284,10 +297,8 @@ OrderBook.prototype.applyTransferRate = function(balance, transferRate) {
|
||||
return balance;
|
||||
}
|
||||
|
||||
var adjustedBalance = Amount.from_json(balance
|
||||
+ '/' + this._currencyGets.to_json()
|
||||
+ '/' + this._issuerGets
|
||||
)
|
||||
var iouSuffix = '/USD/rrrrrrrrrrrrrrrrrrrrBZbvji';
|
||||
var adjustedBalance = Amount.from_json(balance + iouSuffix)
|
||||
.divide(transferRate)
|
||||
.multiply(Amount.from_json(OrderBook.DEFAULT_TRANSFER_RATE))
|
||||
.to_json()
|
||||
@@ -304,7 +315,6 @@ OrderBook.prototype.applyTransferRate = function(balance, transferRate) {
|
||||
|
||||
OrderBook.prototype.requestTransferRate = function(callback) {
|
||||
var self = this;
|
||||
|
||||
var issuer = this._issuerGets;
|
||||
|
||||
this.once('transfer_rate', function(rate) {
|
||||
@@ -352,22 +362,56 @@ OrderBook.prototype.setFundedAmount = function(offer, fundedAmount) {
|
||||
assert.strictEqual(typeof offer, 'object', 'Offer is invalid');
|
||||
assert(!isNaN(fundedAmount), 'Funds is invalid');
|
||||
|
||||
var iouSuffix = '/USD/rrrrrrrrrrrrrrrrrrrrBZbvji';
|
||||
if (fundedAmount === '0') {
|
||||
offer.taker_gets_funded = '0';
|
||||
offer.taker_pays_funded = '0';
|
||||
offer.is_fully_funded = false;
|
||||
return offer;
|
||||
}
|
||||
|
||||
var takerGetsValue = (typeof offer.TakerGets === 'object')
|
||||
var iouSuffix = '/' + this._currencyGets.to_json()
|
||||
+ '/' + this._issuerGets;
|
||||
|
||||
offer.is_fully_funded = Amount.from_json(
|
||||
this._currencyGets.is_native() ? fundedAmount : fundedAmount + iouSuffix
|
||||
).compareTo(Amount.from_json(offer.TakerGets)) >= 0;
|
||||
|
||||
if (offer.is_fully_funded) {
|
||||
offer.taker_gets_funded = Amount.from_json(offer.TakerGets).to_text();
|
||||
offer.taker_pays_funded = Amount.from_json(offer.TakerPays).to_text();
|
||||
return offer;
|
||||
}
|
||||
|
||||
offer.taker_gets_funded = fundedAmount;
|
||||
|
||||
var takerPaysValue = typeof offer.TakerPays === 'object'
|
||||
? offer.TakerPays.value
|
||||
: offer.TakerPays;
|
||||
|
||||
var takerGetsValue = typeof offer.TakerGets === 'object'
|
||||
? offer.TakerGets.value
|
||||
: offer.TakerGets;
|
||||
|
||||
var takerGets = Amount.from_json(takerGetsValue + iouSuffix);
|
||||
var takerPays = Amount.from_json(
|
||||
takerPaysValue + '/000/rrrrrrrrrrrrrrrrrrrrBZbvji'
|
||||
);
|
||||
|
||||
offer.is_fully_funded = Amount.from_json(
|
||||
fundedAmount + iouSuffix
|
||||
).compareTo(takerGets) >= 0;
|
||||
var takerGets = Amount.from_json(
|
||||
takerGetsValue + '/000/rrrrrrrrrrrrrrrrrrrrBZbvji'
|
||||
);
|
||||
|
||||
if (offer.is_fully_funded) {
|
||||
offer.taker_gets_funded = takerGetsValue;
|
||||
var fundedPays = Amount.from_json(
|
||||
fundedAmount + '/000/rrrrrrrrrrrrrrrrrrrrBZbvji'
|
||||
);
|
||||
|
||||
var rate = takerPays.divide(takerGets);
|
||||
|
||||
fundedPays = fundedPays.multiply(rate);
|
||||
|
||||
if (fundedPays.compareTo(takerPays) < 0) {
|
||||
offer.taker_pays_funded = fundedPays.to_json().value;
|
||||
} else {
|
||||
offer.taker_gets_funded = fundedAmount;
|
||||
offer.taker_pays_funded = takerPays.to_json().value;
|
||||
}
|
||||
|
||||
return offer;
|
||||
@@ -568,6 +612,8 @@ OrderBook.prototype.updateFundedAmounts = function(message) {
|
||||
this.once('transfer_rate', function() {
|
||||
self.updateFundedAmounts(message);
|
||||
});
|
||||
|
||||
this.requestTransferRate();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -586,12 +632,8 @@ OrderBook.prototype.updateFundedAmounts = function(message) {
|
||||
var result = this.getBalanceChange(node);
|
||||
|
||||
if (result.isValid) {
|
||||
var account = result.account;
|
||||
var balance = result.balance;
|
||||
|
||||
if (this.hasCachedFunds(account)) {
|
||||
var fundedAmount = this.applyTransferRate(balance);
|
||||
this.updateOfferFunds(account, fundedAmount);
|
||||
if (this.hasCachedFunds(result.account)) {
|
||||
this.updateOfferFunds(result.account, result.balance);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -628,11 +670,15 @@ OrderBook.prototype.updateTransferRate = function(message) {
|
||||
* Request orderbook entries from server
|
||||
*/
|
||||
|
||||
OrderBook.prototype.requestOffers = function() {
|
||||
OrderBook.prototype.requestOffers = function(callback) {
|
||||
var self = this;
|
||||
|
||||
if (typeof callback !== 'function') {
|
||||
callback = function(){};
|
||||
}
|
||||
|
||||
if (!this._shouldSubscribe) {
|
||||
return;
|
||||
return callback(new Error('Should not request offers'));
|
||||
}
|
||||
|
||||
if (this._remote.trace) {
|
||||
@@ -642,26 +688,15 @@ OrderBook.prototype.requestOffers = function() {
|
||||
function handleOffers(res) {
|
||||
if (!Array.isArray(res.offers)) {
|
||||
// XXX What now?
|
||||
return;
|
||||
}
|
||||
|
||||
if (!self._currencyGets.is_native() && !self._issuerTransferRate) {
|
||||
// Defer until transfer rate is requested
|
||||
if (self._remote.trace) {
|
||||
log.info('waiting for transfer rate');
|
||||
}
|
||||
|
||||
self.once('transfer_rate', function() {
|
||||
handleOffers(res);
|
||||
});
|
||||
return;
|
||||
return callback(new Error('Invalid response'));
|
||||
}
|
||||
|
||||
if (self._remote.trace) {
|
||||
log.info('requested offers', self._key, 'offers: ' + res.offers.length);
|
||||
}
|
||||
|
||||
self._offers = res.offers.map(function addOffer(offer) {
|
||||
for (var i=0, l=res.offers.length; i<l; i++) {
|
||||
var offer = res.offers[i];
|
||||
var fundedAmount;
|
||||
|
||||
if (self.hasCachedFunds(offer.Account)) {
|
||||
@@ -673,13 +708,14 @@ OrderBook.prototype.requestOffers = function() {
|
||||
|
||||
self.setFundedAmount(offer, fundedAmount);
|
||||
self.incrementOfferCount(offer.Account);
|
||||
|
||||
return offer;
|
||||
});
|
||||
self._offers.push(offer);
|
||||
}
|
||||
|
||||
self._synchronized = true;
|
||||
|
||||
self.emit('model', self._offers);
|
||||
|
||||
callback(null, self._offers);
|
||||
};
|
||||
|
||||
function handleError(err) {
|
||||
@@ -687,6 +723,8 @@ OrderBook.prototype.requestOffers = function() {
|
||||
if (self._remote.trace) {
|
||||
log.info('failed to request offers', self._key, err);
|
||||
}
|
||||
|
||||
callback(err);
|
||||
};
|
||||
|
||||
var request = this._remote.requestBookOffers(this.toJSON());
|
||||
@@ -701,28 +739,37 @@ OrderBook.prototype.requestOffers = function() {
|
||||
* Subscribe to transactions stream
|
||||
*/
|
||||
|
||||
OrderBook.prototype.subscribeTransactions = function() {
|
||||
OrderBook.prototype.subscribeTransactions = function(callback) {
|
||||
var self = this;
|
||||
|
||||
if (typeof callback !== 'function') {
|
||||
callback = function(){};
|
||||
}
|
||||
|
||||
if (!this._shouldSubscribe) {
|
||||
return;
|
||||
return callback('Should not subscribe');
|
||||
}
|
||||
|
||||
if (this._remote.trace) {
|
||||
log.info('subscribing to transactions');
|
||||
}
|
||||
|
||||
function handleSubscribed() {
|
||||
self._subscribed = true;
|
||||
function handleSubscribed(res) {
|
||||
if (self._remote.trace) {
|
||||
log.info('subscribed to transactions');
|
||||
}
|
||||
|
||||
self._subscribed = true;
|
||||
|
||||
callback(null, res);
|
||||
};
|
||||
|
||||
function handleError(err) {
|
||||
if (self._remote.trace) {
|
||||
log.info('failed to subscribe to transactions', self._key, err);
|
||||
}
|
||||
|
||||
callback(err);
|
||||
};
|
||||
|
||||
var request = this._remote.requestSubscribe();
|
||||
@@ -868,29 +915,44 @@ OrderBook.prototype.modifyOffer = function(node, isDeletedNode) {
|
||||
* @param {String|Object} offer funds
|
||||
*/
|
||||
|
||||
OrderBook.prototype.updateOfferFunds = function(account, fundedAmount) {
|
||||
OrderBook.prototype.updateOfferFunds = function(account, balance) {
|
||||
assert(UInt160.is_valid(account), 'Account is invalid');
|
||||
assert(!isNaN(fundedAmount), 'Funded amount is invalid');
|
||||
assert(!isNaN(balance), 'Funded amount is invalid');
|
||||
|
||||
if (this._remote.trace) {
|
||||
log.info('updating offer funds', this._key, account, fundedAmount);
|
||||
}
|
||||
|
||||
var fundedAmount = this.applyTransferRate(balance);
|
||||
|
||||
// Update cached account funds
|
||||
this.addCachedFunds(account, fundedAmount);
|
||||
|
||||
for (var i=0; i<this._offers.length; i++) {
|
||||
var offer = this._offers[i];
|
||||
|
||||
if (offer.Account === account) {
|
||||
// Update funds for account's offer
|
||||
var previousOffer = extend({}, offer);
|
||||
var previousAmount = offer.taker_gets_funded;
|
||||
if (offer.Account !== account) {
|
||||
continue;
|
||||
}
|
||||
|
||||
this.setFundedAmount(offer, fundedAmount);
|
||||
var suffix = '/USD/rrrrrrrrrrrrrrrrrrrrBZbvji';
|
||||
var previousOffer = extend({}, offer);
|
||||
var previousFundedGets = Amount.from_json(offer.taker_gets_funded + suffix);
|
||||
|
||||
offer.owner_funds = balance;
|
||||
this.setFundedAmount(offer, fundedAmount);
|
||||
|
||||
var hasChangedFunds = !previousFundedGets.equals(
|
||||
Amount.from_json(offer.taker_gets_funded + suffix)
|
||||
);
|
||||
|
||||
if (hasChangedFunds) {
|
||||
this.emit('offer_changed', previousOffer, offer);
|
||||
this.emit('offer_funds_changed', offer, previousAmount, fundedAmount);
|
||||
this.emit(
|
||||
'offer_funds_changed', offer,
|
||||
previousOffer.taker_gets_funded,
|
||||
offer.taker_gets_funded
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
|
||||
var EventEmitter = require('events').EventEmitter;
|
||||
var util = require('util');
|
||||
var assert = require('assert');
|
||||
var LRU = require('lru-cache');
|
||||
var Server = require('./server').Server;
|
||||
var Request = require('./request').Request;
|
||||
@@ -83,6 +84,7 @@ function Remote(opts, trace) {
|
||||
this.trusted = Boolean(opts.trusted);
|
||||
this.state = 'offline'; // 'online', 'offline'
|
||||
this._server_fatal = false; // True, if we know server exited.
|
||||
this._allow_partial_history = (typeof opts.allow_partial_history === 'boolean') ? opts.allow_partial_history : true;
|
||||
|
||||
this.local_sequence = Boolean(opts.local_sequence); // Locally track sequence numbers
|
||||
this.local_fee = (typeof opts.local_fee === 'boolean') ? opts.local_fee : true;// Locally set fees
|
||||
@@ -208,7 +210,7 @@ function Remote(opts, trace) {
|
||||
|
||||
function listenerAdded(type, listener) {
|
||||
if (type === 'transaction_all') {
|
||||
if (!self._transaction_subs && self._connected) {
|
||||
if (!self._transaction_subs && self.isConnected()) {
|
||||
self.request_subscribe('transactions').request();
|
||||
}
|
||||
self._transaction_subs += 1;
|
||||
@@ -220,7 +222,7 @@ function Remote(opts, trace) {
|
||||
function listenerRemoved(type, listener) {
|
||||
if (type === 'transaction_all') {
|
||||
self._transaction_subs -= 1;
|
||||
if (!self._transaction_subs && self._connected) {
|
||||
if (!self._transaction_subs && self.isConnected()) {
|
||||
self.request_unsubscribe('transactions').request();
|
||||
}
|
||||
}
|
||||
@@ -242,7 +244,7 @@ function Remote(opts, trace) {
|
||||
};
|
||||
|
||||
if (opts.ping) {
|
||||
this.once('connect', pingServers);
|
||||
this.on('connect', pingServers);
|
||||
}
|
||||
|
||||
function reconnect() {
|
||||
@@ -337,13 +339,11 @@ Remote.isValidLedgerData = function(message) {
|
||||
return (typeof message === 'object')
|
||||
&& (typeof message.fee_base === 'number')
|
||||
&& (typeof message.fee_ref === 'number')
|
||||
&& (typeof message.fee_base === 'number')
|
||||
&& (typeof message.ledger_hash === 'string')
|
||||
&& (typeof message.ledger_index === 'number')
|
||||
&& (typeof message.ledger_time === 'number')
|
||||
&& (typeof message.reserve_base === 'number')
|
||||
&& (typeof message.reserve_inc === 'number')
|
||||
&& (typeof message.txn_count === 'number');
|
||||
&& (typeof message.reserve_inc === 'number');
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -513,12 +513,9 @@ Remote.prototype.reconnect = function() {
|
||||
|
||||
log.info('reconnecting');
|
||||
|
||||
;(function nextServer(i) {
|
||||
self._servers[i].reconnect();
|
||||
if (++i < self._servers.length) {
|
||||
nextServer(i);
|
||||
}
|
||||
})(0);
|
||||
this._servers.forEach(function(server) {
|
||||
server.reconnect();
|
||||
});
|
||||
|
||||
return this;
|
||||
};
|
||||
@@ -552,12 +549,9 @@ Remote.prototype.connect = function(online) {
|
||||
|
||||
this._should_connect = true;
|
||||
|
||||
;(function nextServer(i) {
|
||||
self._servers[i].connect();
|
||||
if (++i < self._servers.length) {
|
||||
nextServer(i);
|
||||
}
|
||||
})(0);
|
||||
this._servers.forEach(function(server) {
|
||||
server.connect();
|
||||
});
|
||||
|
||||
return this;
|
||||
};
|
||||
@@ -576,7 +570,7 @@ Remote.prototype.disconnect = function(callback) {
|
||||
|
||||
var callback = (typeof callback === 'function') ? callback : function(){};
|
||||
|
||||
if (!this._connected) {
|
||||
if (!this.isConnected()) {
|
||||
callback();
|
||||
return this;
|
||||
}
|
||||
@@ -659,11 +653,19 @@ Remote.prototype._handleLedgerClosed = function(message) {
|
||||
|
||||
var ledgerAdvanced = message.ledger_index >= this._ledger_current_index;
|
||||
|
||||
if (ledgerAdvanced) {
|
||||
if (isNaN(this._ledger_current_index) || ledgerAdvanced) {
|
||||
this._ledger_time = message.ledger_time;
|
||||
this._ledger_hash = message.ledger_hash;
|
||||
this._ledger_current_index = message.ledger_index + 1;
|
||||
this.emit('ledger_closed', message);
|
||||
|
||||
if (this.isConnected()) {
|
||||
this.emit('ledger_closed', message);
|
||||
} else {
|
||||
this.once('connect', function() {
|
||||
// Delay until server is 'online'
|
||||
self.emit('ledger_closed', message);
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -818,6 +820,10 @@ Remote.prototype.getServer = function() {
|
||||
}
|
||||
|
||||
var connectedServers = this.getConnectedServers();
|
||||
if (connectedServers.length === 0 || !connectedServers[0]) {
|
||||
return null;
|
||||
}
|
||||
|
||||
var server = connectedServers[0];
|
||||
var cScore = server._score + server._fee;
|
||||
|
||||
@@ -860,7 +866,7 @@ Remote.prototype.request = function(request) {
|
||||
|
||||
if (!this._servers.length) {
|
||||
request.emit('error', new Error('No servers available'));
|
||||
} else if (!this._connected) {
|
||||
} else if (!this.isConnected()) {
|
||||
this.once('connect', this.request.bind(this, request));
|
||||
} else if (request.server === null) {
|
||||
request.emit('error', new Error('Server does not exist'));
|
||||
@@ -1607,12 +1613,7 @@ Remote.prototype._serverPrepareSubscribe = function(callback) {
|
||||
self.emit('random', utils.hexToArray(message.random));
|
||||
}
|
||||
|
||||
if (message.ledger_hash && message.ledger_index) {
|
||||
self._ledger_time = message.ledger_time;
|
||||
self._ledger_hash = message.ledger_hash;
|
||||
self._ledger_current_index = message.ledger_index+1;
|
||||
self.emit('ledger_closed', message);
|
||||
}
|
||||
self._handleLedgerClosed(message);
|
||||
|
||||
self.emit('subscribed');
|
||||
};
|
||||
@@ -2036,7 +2037,7 @@ Remote.prepareCurrencies = function(currency) {
|
||||
}
|
||||
|
||||
if (currency.hasOwnProperty('currency')) {
|
||||
newCurrency.currency = Currency.json_rewrite(currency.currency);
|
||||
newCurrency.currency = Currency.json_rewrite(currency.currency, {force_hex:true});
|
||||
}
|
||||
|
||||
return newCurrency;
|
||||
@@ -2214,65 +2215,40 @@ Remote.prototype.requestConnect = function(ip, port, callback) {
|
||||
/**
|
||||
* Create a Transaction
|
||||
*
|
||||
* @param {String} source
|
||||
* @param {String} TransactionType
|
||||
* @param {Object} options
|
||||
* @param [Function] callback
|
||||
* @return {Request}
|
||||
* @return {Transaction}
|
||||
*/
|
||||
|
||||
Remote.prototype.transaction =
|
||||
Remote.prototype.createTransaction = function(source, options, callback) {
|
||||
Remote.prototype.createTransaction = function(type, options) {
|
||||
if (arguments.length === 0) {
|
||||
// Fallback
|
||||
return new Transaction(this);
|
||||
}
|
||||
|
||||
assert.strictEqual(typeof type, 'string', 'TransactionType must be a string');
|
||||
assert.strictEqual(typeof options, 'object', 'Transaction options must be an object');
|
||||
|
||||
var transaction = new Transaction(this);
|
||||
|
||||
var transactionTypes = {
|
||||
payment: 'payment',
|
||||
accountset: 'accountSet',
|
||||
trustset: 'trustSet',
|
||||
offercreate: 'offerCreate',
|
||||
offercancel: 'offerCancel',
|
||||
claim: 'claim',
|
||||
passwordfund: 'passwordFund',
|
||||
passwordset: 'passwordSet',
|
||||
setregularkey: 'setRegularKey',
|
||||
walletadd: 'walletAdd',
|
||||
sign: 'sign'
|
||||
payment: 'payment',
|
||||
accountset: 'accountSet',
|
||||
trustset: 'trustSet',
|
||||
offercreate: 'offerCreate',
|
||||
offercancel: 'offerCancel',
|
||||
setregularkey: 'setRegularKey',
|
||||
sign: 'sign'
|
||||
};
|
||||
|
||||
var transactionType;
|
||||
var transactionConstructor = transactionTypes[type.toLowerCase()];
|
||||
|
||||
switch (typeof source) {
|
||||
case 'object':
|
||||
if (typeof source.type !== 'string') {
|
||||
throw new Error('Missing transaction type');
|
||||
}
|
||||
|
||||
transactionType = transactionTypes[source.type.toLowerCase()];
|
||||
|
||||
if (!transactionType) {
|
||||
throw new Error('Invalid transaction type: ' + transactionType);
|
||||
}
|
||||
|
||||
transaction = transaction[transactionType](source);
|
||||
break;
|
||||
|
||||
case 'string':
|
||||
transactionType = transactionTypes[source.toLowerCase()];
|
||||
|
||||
if (!transactionType) {
|
||||
throw new Error('Invalid transaction type: ' + transactionType);
|
||||
}
|
||||
|
||||
transaction = transaction[transactionType](options);
|
||||
break;
|
||||
if (!transactionConstructor) {
|
||||
throw new Error('Invalid transaction type: ' + type);
|
||||
}
|
||||
|
||||
var lastArg = arguments[arguments.length - 1];
|
||||
|
||||
if (typeof lastArg === 'function') {
|
||||
transaction.submit(lastArg);
|
||||
}
|
||||
|
||||
return transaction;
|
||||
return transaction[transactionConstructor](options);
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -33,8 +33,8 @@ Seed.prototype.parse_json = function (j) {
|
||||
// XXX Should actually always try and continue if it failed.
|
||||
} else if (j[0] === 's') {
|
||||
this._value = Base.decode_check(Base.VER_FAMILY_SEED, j);
|
||||
} else if (j.length === 32) {
|
||||
this._value = this.parse_hex(j);
|
||||
} else if (/^[0-9a-fA-f]{32}$/.test(j)) {
|
||||
this.parse_hex(j);
|
||||
// XXX Should also try 1751
|
||||
} else {
|
||||
this.parse_passphrase(j);
|
||||
|
||||
@@ -117,7 +117,7 @@ SerializedType.prototype.parse_varint = function (so) {
|
||||
*/
|
||||
function append_byte_array(so, val, bytes) {
|
||||
if (!isNumber(val)) {
|
||||
throw new Error('Value is not a number');
|
||||
throw new Error('Value is not a number', bytes);
|
||||
}
|
||||
|
||||
if (val < 0 || val >= Math.pow(256, bytes)) {
|
||||
@@ -345,7 +345,7 @@ var STAmount = exports.Amount = new SerializedType({
|
||||
|
||||
// Next eight bits: offset/exponent
|
||||
hi |= ((97 + amount._offset) & 0xff) << 22;
|
||||
// Remaining 52 bits: mantissa
|
||||
// Remaining 54 bits: mantissa
|
||||
hi |= amount._value.shiftRight(32).intValue() & 0x3fffff;
|
||||
lo = amount._value.intValue() & 0xffffffff;
|
||||
}
|
||||
@@ -625,7 +625,13 @@ function serialize(so, field_name, value) {
|
||||
// Get the serializer class (ST...) for a field based on the type bits.
|
||||
var serialized_object_type = exports[binformat.types[type_bits]];
|
||||
//do something with val[keys] and val[keys[i]];
|
||||
serialized_object_type.serialize(so, value);
|
||||
|
||||
try {
|
||||
serialized_object_type.serialize(so, value);
|
||||
} catch (e) {
|
||||
e.message += ' (' + field_name + ')';
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
//Take the serialized object, figure out what type/field it is, and return the parsing of that.
|
||||
|
||||
@@ -76,7 +76,6 @@ function Server(remote, opts) {
|
||||
this._lastLedgerClose = NaN;
|
||||
|
||||
this._score = 0;
|
||||
|
||||
this._scoreWeights = {
|
||||
ledgerclose: 5,
|
||||
response: 1
|
||||
@@ -354,7 +353,7 @@ Server.prototype.reconnect = function() {
|
||||
if (this.isConnected()) {
|
||||
this.once('disconnect', reconnect);
|
||||
this.disconnect();
|
||||
} else {
|
||||
} else {
|
||||
reconnect();
|
||||
}
|
||||
}
|
||||
@@ -627,6 +626,12 @@ Server.prototype._handlePathFind = function(message) {
|
||||
*/
|
||||
|
||||
Server.prototype._handleResponseSubscribe = function(message) {
|
||||
if (!this._remote._allow_partial_history
|
||||
&& !Server.hasFullLedgerHistory(message)) {
|
||||
// Server has partial history and Remote has been configured to disallow
|
||||
// servers with incomplete history
|
||||
return this.reconnect();
|
||||
}
|
||||
if (Server.isLoadStatus(message)) {
|
||||
this._load_base = message.load_base || 256;
|
||||
this._load_factor = message.load_factor || 256;
|
||||
@@ -643,10 +648,25 @@ Server.prototype._handleResponseSubscribe = function(message) {
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Check that server message indicates that server has complete ledger history
|
||||
*
|
||||
* @param {Object} message
|
||||
* @return {Boolean}
|
||||
*/
|
||||
|
||||
Server.hasFullLedgerHistory = function(message) {
|
||||
return (typeof message === 'object')
|
||||
&& (message.server_status === 'full')
|
||||
&& (typeof message.validated_ledgers === 'string')
|
||||
&& (message.validated_ledgers.split('-').length === 2);
|
||||
};
|
||||
|
||||
/**
|
||||
* Check that received message from rippled is valid
|
||||
*
|
||||
* @api private
|
||||
* @param {Object} message
|
||||
* @return {Boolean}
|
||||
*/
|
||||
|
||||
Server.isValidMessage = function(message) {
|
||||
@@ -655,14 +675,15 @@ Server.isValidMessage = function(message) {
|
||||
};
|
||||
|
||||
/**
|
||||
* Check that received serverStatus message contains
|
||||
* load status information
|
||||
* Check that received serverStatus message contains load status information
|
||||
*
|
||||
* @api private
|
||||
* @param {Object} message
|
||||
* @return {Boolean}
|
||||
*/
|
||||
|
||||
Server.isLoadStatus = function(message) {
|
||||
return (typeof message.load_base === 'number')
|
||||
return (typeof message === 'object')
|
||||
&& (typeof message.load_base === 'number')
|
||||
&& (typeof message.load_factor === 'number');
|
||||
};
|
||||
|
||||
@@ -683,10 +704,10 @@ Server.prototype._sendMessage = function(message) {
|
||||
};
|
||||
|
||||
/**
|
||||
* Submit a Request object.
|
||||
* Submit a Request object
|
||||
*
|
||||
* Requests are indexed by message ID, which is repeated
|
||||
* in the response from rippled WebSocket server
|
||||
* Requests are indexed by message ID, which is repeated in the response from
|
||||
* rippled WebSocket server
|
||||
*
|
||||
* @param {Request} request
|
||||
* @api private
|
||||
|
||||
@@ -167,6 +167,9 @@ Transaction.set_clear_flags = {
|
||||
}
|
||||
};
|
||||
|
||||
Transaction.MEMO_TYPES = {
|
||||
};
|
||||
|
||||
Transaction.formats = require('./binformat').tx;
|
||||
|
||||
Transaction.prototype.consts = {
|
||||
@@ -477,7 +480,7 @@ Transaction.prototype.clientID = function(id) {
|
||||
};
|
||||
|
||||
Transaction.prototype.lastLedger = function(sequence) {
|
||||
if (typeof sequence === 'number') {
|
||||
if (typeof sequence === 'number' && isFinite(sequence)) {
|
||||
this._setLastLedger = true;
|
||||
this.tx_json.LastLedgerSequence = sequence;
|
||||
}
|
||||
@@ -595,6 +598,48 @@ Transaction.prototype.setFlags = function(flags) {
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Add a Memo to transaction. Memos can be used as key-value,
|
||||
* using the MemoType as a key
|
||||
*
|
||||
* @param {String} type
|
||||
* @param {String} data
|
||||
*/
|
||||
|
||||
Transaction.prototype.addMemo = function(type, data) {
|
||||
if (!/(undefined|string)/.test(typeof type)) {
|
||||
throw new Error('MemoType must be a string');
|
||||
}
|
||||
|
||||
if (!/(undefined|string)/.test(typeof data)) {
|
||||
throw new Error('MemoData must be a string');
|
||||
}
|
||||
|
||||
function toHex(str) {
|
||||
return sjcl.codec.hex.fromBits(sjcl.codec.utf8String.toBits(str));
|
||||
};
|
||||
|
||||
var memo = { };
|
||||
|
||||
if (type) {
|
||||
if (Transaction.MEMO_TYPES[type]) {
|
||||
//XXX Maybe in the future we want a schema validator for
|
||||
//memo types
|
||||
memo.MemoType = Transaction.MEMO_TYPES[type];
|
||||
} else {
|
||||
memo.MemoType = toHex(type);
|
||||
}
|
||||
}
|
||||
|
||||
if (data) {
|
||||
memo.MemoData = toHex(data);
|
||||
}
|
||||
|
||||
this.tx_json.Memos = (this.tx_json.Memos || []).concat({ Memo: memo });
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
// Options:
|
||||
// .domain() NYI
|
||||
// .flags()
|
||||
|
||||
8
src/js/ripple/wallet.js
Normal file
8
src/js/ripple/wallet.js
Normal file
@@ -0,0 +1,8 @@
|
||||
var sjcl = require('./utils').sjcl;
|
||||
|
||||
var WalletGenerator = require('ripple-wallet-generator')({
|
||||
sjcl: sjcl
|
||||
});
|
||||
|
||||
module.exports = WalletGenerator;
|
||||
|
||||
@@ -82,8 +82,17 @@ describe('Message', function(){
|
||||
});
|
||||
|
||||
it('should throw an error if given an invalid secret key', function(){
|
||||
// Annoyingly non hex can be fed to the BigInteger(s, 16) constructor and
|
||||
// it will parse as a number. Before the commit of this comment, this test
|
||||
// involved a fixture of 32 chars, which was assumed to be hex. The test
|
||||
// passed, but for the wrong wreasons. There was a bug in Seed.parse_json.
|
||||
|
||||
var secret_string = 'badsafRpB5euNL52PZPTSqrE9gvuFwTC';
|
||||
// Seed.from_json only creates invalid seeds from empty strings or invalid
|
||||
// base58 starting with an s, which it tries to base 58 decode/check sum.
|
||||
// The rest will be assumed to be a passphrase.
|
||||
|
||||
// This is a bad b58 seed
|
||||
var secret_string = 'sbadsafRpB5euNL52PZPTSqrE9gvuFwTC';
|
||||
var hash = 'e865bcc63a86ef21585ac8340a7cc8590ed85175a2a718c6fb2bfb2715d13778';
|
||||
|
||||
assert.throws(function(){
|
||||
|
||||
@@ -21,6 +21,20 @@ describe('OrderBook', function() {
|
||||
issuer: 'rrrrrrrrrrrrrrrrrrrrBZbvji'
|
||||
}
|
||||
});
|
||||
book = new Remote().createOrderBook({
|
||||
issuer_gets: 'rrrrrrrrrrrrrrrrrrrrBZbvji',
|
||||
currency_gets: 'BTC',
|
||||
currency_pays: 'XRP'
|
||||
});
|
||||
assert.deepEqual(book.toJSON(), {
|
||||
taker_gets: {
|
||||
currency: Currency.from_json('BTC').to_hex(),
|
||||
issuer: 'rrrrrrrrrrrrrrrrrrrrBZbvji'
|
||||
},
|
||||
taker_pays: {
|
||||
currency: Currency.from_json('XRP').to_hex()
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('Check orderbook validity', function() {
|
||||
@@ -51,26 +65,16 @@ describe('OrderBook', function() {
|
||||
currency_pays: 'BTC'
|
||||
});
|
||||
|
||||
var requestedTransferRate = false;
|
||||
var requestedOffers = false;
|
||||
|
||||
book.subscribeTransactions = function() {
|
||||
assert(requestedTransferRate);
|
||||
assert(requestedOffers);
|
||||
done();
|
||||
};
|
||||
|
||||
book.requestTransferRate = function(callback) {
|
||||
requestedTransferRate = true;
|
||||
};
|
||||
|
||||
book.requestOffers = function() {
|
||||
book.requestOffers = function(callback) {
|
||||
requestedOffers = true;
|
||||
var em = new process.EventEmitter();
|
||||
setImmediate(function() {
|
||||
em.emit('success', { offers: [ ] });
|
||||
});
|
||||
return em;
|
||||
callback();
|
||||
};
|
||||
|
||||
book.subscribe();
|
||||
@@ -339,16 +343,21 @@ describe('OrderBook', function() {
|
||||
value: '100',
|
||||
currency: 'BTC',
|
||||
issuer: 'rrrrrrrrrrrrrrrrrrrrBZbvji'
|
||||
}
|
||||
},
|
||||
TakerPays: '123456'
|
||||
};
|
||||
|
||||
book.setFundedAmount(offer, '100.1234');
|
||||
|
||||
assert.deepEqual(offer, {
|
||||
var expected = {
|
||||
TakerGets: offer.TakerGets,
|
||||
TakerPays: offer.TakerPays,
|
||||
is_fully_funded: true,
|
||||
taker_gets_funded: '100',
|
||||
is_fully_funded: true
|
||||
});
|
||||
taker_pays_funded: '123456'
|
||||
};
|
||||
|
||||
assert.deepEqual(offer, expected);
|
||||
});
|
||||
|
||||
it('Set funded amount - unfunded', function() {
|
||||
@@ -359,62 +368,113 @@ describe('OrderBook', function() {
|
||||
currency_pays: 'BTC'
|
||||
});
|
||||
|
||||
var offer = {
|
||||
TakerGets: '100',
|
||||
TakerPays: {
|
||||
value: '123456',
|
||||
currency: 'BTC',
|
||||
issuer: 'rrrrrrrrrrrrrrrrrrrrBZbvji'
|
||||
}
|
||||
};
|
||||
|
||||
book.setFundedAmount(offer, '99');
|
||||
|
||||
var expected = {
|
||||
TakerGets: offer.TakerGets,
|
||||
TakerPays: offer.TakerPays,
|
||||
is_fully_funded: false,
|
||||
taker_gets_funded: '99',
|
||||
taker_pays_funded: '122221.44'
|
||||
};
|
||||
|
||||
assert.deepEqual(offer, expected);
|
||||
});
|
||||
|
||||
it('Set funded amount - native currency - funded', function() {
|
||||
var remote = new Remote();
|
||||
var book = remote.createOrderBook({
|
||||
currency_gets: 'XRP',
|
||||
issuer_pays: 'rrrrrrrrrrrrrrrrrrrrBZbvji',
|
||||
currency_pays: 'BTC'
|
||||
});
|
||||
|
||||
var offer = {
|
||||
TakerGets: '100',
|
||||
TakerPays: {
|
||||
value: '100.1234',
|
||||
currency: 'USD',
|
||||
issuer: 'rrrrrrrrrrrrrrrrrrrrBZbvji'
|
||||
}
|
||||
};
|
||||
|
||||
book.setFundedAmount(offer, '100');
|
||||
|
||||
var expected = {
|
||||
TakerGets: offer.TakerGets,
|
||||
TakerPays: offer.TakerPays,
|
||||
is_fully_funded: true,
|
||||
taker_gets_funded: '100',
|
||||
taker_pays_funded: '100.1234'
|
||||
};
|
||||
|
||||
assert.deepEqual(offer, expected);
|
||||
});
|
||||
|
||||
it('Set funded amount - native currency - unfunded', function() {
|
||||
var remote = new Remote();
|
||||
var book = remote.createOrderBook({
|
||||
currency_gets: 'XRP',
|
||||
issuer_pays: 'rrrrrrrrrrrrrrrrrrrrBZbvji',
|
||||
currency_pays: 'USD'
|
||||
});
|
||||
|
||||
var offer = {
|
||||
TakerGets: {
|
||||
value: '100.1234',
|
||||
currency: 'USD',
|
||||
issuer: 'rrrrrrrrrrrrrrrrrrrrBZbvji'
|
||||
},
|
||||
TakerPays: '123'
|
||||
};
|
||||
|
||||
book.setFundedAmount(offer, '100');
|
||||
|
||||
var expected = {
|
||||
TakerGets: offer.TakerGets,
|
||||
TakerPays: offer.TakerPays,
|
||||
is_fully_funded: false,
|
||||
taker_gets_funded: '100',
|
||||
taker_pays_funded: '122.8484050681459'
|
||||
};
|
||||
|
||||
assert.deepEqual(offer, expected);
|
||||
});
|
||||
|
||||
it('Set funded amount - zero funds', function() {
|
||||
var remote = new Remote();
|
||||
var book = remote.createOrderBook({
|
||||
currency_gets: 'XRP',
|
||||
issuer_pays: 'rrrrrrrrrrrrrrrrrrrrBZbvji',
|
||||
currency_pays: 'BTC'
|
||||
});
|
||||
|
||||
var offer = {
|
||||
TakerGets: {
|
||||
value: '100',
|
||||
currency: 'BTC',
|
||||
issuer: 'rrrrrrrrrrrrrrrrrrrrBZbvji'
|
||||
}
|
||||
},
|
||||
TakerPays: '123456'
|
||||
};
|
||||
|
||||
book.setFundedAmount(offer, '99');
|
||||
book.setFundedAmount(offer, '0');
|
||||
|
||||
assert.deepEqual(offer, {
|
||||
TakerGets: offer.TakerGets,
|
||||
taker_gets_funded: '99',
|
||||
is_fully_funded: false
|
||||
});
|
||||
});
|
||||
|
||||
it('Set funded amount - native currency - funded', function() {
|
||||
var remote = new Remote();
|
||||
var book = remote.createOrderBook({
|
||||
currency_gets: 'XRP',
|
||||
issuer_pays: 'rrrrrrrrrrrrrrrrrrrrBZbvji',
|
||||
currency_pays: 'BTC'
|
||||
});
|
||||
|
||||
var offer = {
|
||||
TakerGets: '100'
|
||||
};
|
||||
|
||||
book.setFundedAmount(offer, '100');
|
||||
|
||||
assert.deepEqual(offer, {
|
||||
TakerGets: '100',
|
||||
taker_gets_funded: '100',
|
||||
is_fully_funded: true
|
||||
});
|
||||
});
|
||||
|
||||
it('Set funded amount - native currency - unfunded', function() {
|
||||
var remote = new Remote();
|
||||
var book = remote.createOrderBook({
|
||||
currency_gets: 'XRP',
|
||||
issuer_pays: 'rrrrrrrrrrrrrrrrrrrrBZbvji',
|
||||
currency_pays: 'BTC'
|
||||
});
|
||||
|
||||
var offer = {
|
||||
TakerGets: '100'
|
||||
};
|
||||
|
||||
book.setFundedAmount(offer, '99');
|
||||
|
||||
assert.deepEqual(offer, {
|
||||
TakerGets: '100',
|
||||
taker_gets_funded: '99',
|
||||
is_fully_funded: false
|
||||
TakerPays: offer.TakerPays,
|
||||
is_fully_funded: false,
|
||||
taker_gets_funded: '0',
|
||||
taker_pays_funded: '0'
|
||||
});
|
||||
});
|
||||
|
||||
@@ -686,7 +746,8 @@ describe('OrderBook', function() {
|
||||
});
|
||||
|
||||
var meta = new Meta({
|
||||
AffectedNodes: [{
|
||||
AffectedNodes: [
|
||||
{
|
||||
ModifiedNode: {
|
||||
FinalFields: {
|
||||
Balance: {
|
||||
@@ -720,7 +781,43 @@ describe('OrderBook', function() {
|
||||
PreviousTxnID: '53354D84BAE8FDFC3F4DA879D984D24B929E7FEB9100D2AD9EFCD2E126BCCDC8',
|
||||
PreviousTxnLgrSeq: 343570
|
||||
}
|
||||
}]
|
||||
},
|
||||
{
|
||||
ModifiedNode: {
|
||||
FinalFields: {
|
||||
Balance: {
|
||||
currency: 'USD',
|
||||
issuer: 'rrrrrrrrrrrrrrrrrrrrBZbvji',
|
||||
value: '-10'
|
||||
},
|
||||
Flags: 131072,
|
||||
HighLimit: {
|
||||
currency: 'USD',
|
||||
issuer: 'r3PDtZSa5LiYp1Ysn1vMuMzB59RzV3W9QH',
|
||||
value: '100'
|
||||
},
|
||||
HighNode: '0000000000000000',
|
||||
LowLimit: {
|
||||
currency: 'USD',
|
||||
issuer: 'rrrrrrrrrrrrrrrrrrrrBZbvji',
|
||||
value: '0'
|
||||
},
|
||||
LowNode: '0000000000000000'
|
||||
},
|
||||
LedgerEntryType: 'RippleState',
|
||||
LedgerIndex: 'EA4BF03B4700123CDFFB6EB09DC1D6E28D5CEB7F680FB00FC24BC1C3BB2DB959',
|
||||
PreviousFields: {
|
||||
Balance: {
|
||||
currency: 'USD',
|
||||
issuer: 'rrrrrrrrrrrrrrrrrrrrBZbvji',
|
||||
value: '0'
|
||||
}
|
||||
},
|
||||
PreviousTxnID: '53354D84BAE8FDFC3F4DA879D984D24B929E7FEB9100D2AD9EFCD2E126BCCDC8',
|
||||
PreviousTxnLgrSeq: 343570
|
||||
}
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
assert.deepEqual(book.getBalanceChange(meta.getNodes()[0]), {
|
||||
@@ -728,6 +825,12 @@ describe('OrderBook', function() {
|
||||
balance: '10',
|
||||
isValid: true
|
||||
});
|
||||
|
||||
assert.deepEqual(book.getBalanceChange(meta.getNodes()[1]), {
|
||||
account: 'r3PDtZSa5LiYp1Ysn1vMuMzB59RzV3W9QH',
|
||||
balance: '10',
|
||||
isValid: true
|
||||
});
|
||||
});
|
||||
|
||||
it('Get balance change - native currency', function() {
|
||||
@@ -899,7 +1002,8 @@ describe('OrderBook', function() {
|
||||
|
||||
book.on('offer_funds_changed', function(offer, previousFunds, newFunds) {
|
||||
assert.strictEqual(previousFunds, '100');
|
||||
assert.strictEqual(newFunds, '10');
|
||||
assert.strictEqual(newFunds, offer.taker_gets_funded);
|
||||
assert.notStrictEqual(previousFunds, newFunds);
|
||||
switch (++receivedFundsChangedEvents) {
|
||||
case 1:
|
||||
assert(!offer.is_fully_funded);
|
||||
@@ -1033,7 +1137,8 @@ describe('OrderBook', function() {
|
||||
|
||||
book.on('offer_funds_changed', function(offer, previousFunds, newFunds) {
|
||||
assert.strictEqual(previousFunds, '100');
|
||||
assert.strictEqual(newFunds, '25');
|
||||
assert.strictEqual(newFunds, offer.taker_gets_funded);
|
||||
assert.notStrictEqual(previousFunds, newFunds);
|
||||
switch (++receivedFundsChangedEvents) {
|
||||
case 1:
|
||||
assert(!offer.is_fully_funded);
|
||||
@@ -1054,4 +1159,397 @@ describe('OrderBook', function() {
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('Update funded amounts - no affected account', function(done) {
|
||||
var remote = new Remote();
|
||||
|
||||
var book = remote.createOrderBook({
|
||||
currency_gets: 'XRP',
|
||||
issuer_pays: 'rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B',
|
||||
currency_pays: 'USD'
|
||||
});
|
||||
|
||||
var message = {
|
||||
mmeta: new Meta({
|
||||
AffectedNodes: [{
|
||||
ModifiedNode: {
|
||||
FinalFields: {
|
||||
Account: 'r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59',
|
||||
Balance: '25',
|
||||
Flags: 0,
|
||||
OwnerCount: 1,
|
||||
Sequence: 2
|
||||
},
|
||||
LedgerEntryType: 'AccountRoot',
|
||||
LedgerIndex: '4F83A2CF7E70F77F79A307E6A472BFC2585B806A70833CCD1C26105BAE0D6E05',
|
||||
PreviousFields: {
|
||||
Balance: '100',
|
||||
OwnerCount: 0,
|
||||
Sequence: 1
|
||||
},
|
||||
PreviousTxnID: 'B24159F8552C355D35E43623F0E5AD965ADBF034D482421529E2703904E1EC09',
|
||||
PreviousTxnLgrSeq: 16154
|
||||
}
|
||||
}]
|
||||
})
|
||||
};
|
||||
|
||||
book._synchronized = true;
|
||||
|
||||
book._offers = [
|
||||
{
|
||||
Account: 'rrrrrrrrrrrrrrrrrrrrBZbvji',
|
||||
BookDirectory: 'DFA3B6DDAB58C7E8E5D944E736DA4B7046C30E4F460FD9DE4C124AF94ED1781B',
|
||||
BookNode: '0000000000000000',
|
||||
Flags: 0,
|
||||
LedgerEntryType: 'Offer',
|
||||
OwnerNode: '00000000000063CA',
|
||||
PreviousTxnID: '51C64E0B300E9C0E877BA3E79B4ED1DBD5FDDCE58FA1A8FDA5F8DDF139787A24',
|
||||
PreviousTxnLgrSeq: 8265275,
|
||||
Sequence: 1138918,
|
||||
TakerGets: '50',
|
||||
taker_gets_funded: '100',
|
||||
is_fully_funded: true,
|
||||
TakerPays: {
|
||||
currency: 'USD',
|
||||
issuer: 'rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B',
|
||||
value: '5'
|
||||
},
|
||||
index: 'DC003E09AD1306FBBD1957C955EE668E429CC85B0EC0EC17297F6676E6108DE7',
|
||||
owner_funds: '162110617177',
|
||||
quality: '0.000000005148984210454555'
|
||||
}
|
||||
];
|
||||
|
||||
book._offers.__defineGetter__(0, function() {
|
||||
assert(false, 'Iteration of offers for unaffected account');
|
||||
});
|
||||
|
||||
book.on('offer_changed', function() {
|
||||
assert(false, 'offer_changed event emitted');
|
||||
});
|
||||
|
||||
book.on('offer_funds_changed', function() {
|
||||
assert(false, 'offer_funds_changed event emitted');
|
||||
});
|
||||
|
||||
book.updateFundedAmounts(message);
|
||||
|
||||
setImmediate(done);
|
||||
});
|
||||
|
||||
it('Update funded amounts - no balance change', function(done) {
|
||||
var remote = new Remote();
|
||||
|
||||
var book = remote.createOrderBook({
|
||||
currency_gets: 'XRP',
|
||||
issuer_pays: 'rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B',
|
||||
currency_pays: 'USD'
|
||||
});
|
||||
|
||||
var message = {
|
||||
mmeta: new Meta({
|
||||
AffectedNodes: [{
|
||||
ModifiedNode: {
|
||||
FinalFields: {
|
||||
Account: 'r3kmLJN5D28dHuH8vZNUZpMC43pEHpaocV',
|
||||
Balance: '78991384535796',
|
||||
Flags: 0,
|
||||
OwnerCount: 3,
|
||||
Sequence: 188
|
||||
},
|
||||
LedgerEntryType: 'AccountRoot',
|
||||
LedgerIndex: 'B33FDD5CF3445E1A7F2BE9B06336BEBD73A5E3EE885D3EF93F7E3E2992E46F1A',
|
||||
PreviousTxnID: 'E9E1988A0F061679E5D14DE77DB0163CE0BBDC00F29E396FFD1DA0366E7D8904',
|
||||
PreviousTxnLgrSeq: 195455
|
||||
}
|
||||
}]
|
||||
})
|
||||
};
|
||||
|
||||
book._synchronized = true;
|
||||
|
||||
book._offers = [
|
||||
{
|
||||
Account: 'r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59',
|
||||
BookDirectory: 'DFA3B6DDAB58C7E8E5D944E736DA4B7046C30E4F460FD9DE4C124AF94ED1781B',
|
||||
BookNode: '0000000000000000',
|
||||
Flags: 0,
|
||||
LedgerEntryType: 'Offer',
|
||||
OwnerNode: '00000000000063CA',
|
||||
PreviousTxnID: '51C64E0B300E9C0E877BA3E79B4ED1DBD5FDDCE58FA1A8FDA5F8DDF139787A24',
|
||||
PreviousTxnLgrSeq: 8265275,
|
||||
Sequence: 1138918,
|
||||
TakerGets: '50',
|
||||
taker_gets_funded: '100',
|
||||
is_fully_funded: true,
|
||||
TakerPays: {
|
||||
currency: 'USD',
|
||||
issuer: 'rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B',
|
||||
value: '5'
|
||||
},
|
||||
index: 'DC003E09AD1306FBBD1957C955EE668E429CC85B0EC0EC17297F6676E6108DE7',
|
||||
owner_funds: '162110617177',
|
||||
quality: '0.000000005148984210454555'
|
||||
}
|
||||
];
|
||||
|
||||
book.on('offer_changed', function() {
|
||||
assert(false, 'offer_changed event emitted');
|
||||
});
|
||||
|
||||
book.on('offer_funds_changed', function() {
|
||||
assert(false, 'offer_funds_changed event emitted');
|
||||
});
|
||||
|
||||
assert.strictEqual(typeof book.getBalanceChange, 'function');
|
||||
|
||||
book.getBalanceChange = function() {
|
||||
assert(false, 'getBalanceChange should not be called');
|
||||
};
|
||||
|
||||
book.addCachedFunds('r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59', '100');
|
||||
book.updateFundedAmounts(message);
|
||||
|
||||
setImmediate(done);
|
||||
});
|
||||
|
||||
it('Update funded amounts - deferred TransferRate', function(done) {
|
||||
var remote = new Remote();
|
||||
|
||||
var book = remote.createOrderBook({
|
||||
currency_gets: 'USD',
|
||||
issuer_gets: 'rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B',
|
||||
currency_pays: 'XRP'
|
||||
});
|
||||
|
||||
var message = {
|
||||
mmeta: new Meta({
|
||||
AffectedNodes: [{
|
||||
ModifiedNode: {
|
||||
FinalFields: {
|
||||
Balance: {
|
||||
currency: 'USD',
|
||||
issuer: 'rrrrrrrrrrrrrrrrrrrrBZbvji',
|
||||
value: '10'
|
||||
},
|
||||
Flags: 131072,
|
||||
HighLimit: {
|
||||
currency: 'USD',
|
||||
issuer: 'rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B',
|
||||
value: '100'
|
||||
},
|
||||
HighNode: '0000000000000000',
|
||||
LowLimit: {
|
||||
currency: 'USD',
|
||||
issuer: 'r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59',
|
||||
value: '0'
|
||||
},
|
||||
LowNode: '0000000000000000'
|
||||
},
|
||||
LedgerEntryType: 'RippleState',
|
||||
LedgerIndex: 'EA4BF03B4700123CDFFB6EB09DC1D6E28D5CEB7F680FB00FC24BC1C3BB2DB959',
|
||||
PreviousTxnID: '53354D84BAE8FDFC3F4DA879D984D24B929E7FEB9100D2AD9EFCD2E126BCCDC8',
|
||||
PreviousTxnLgrSeq: 343570
|
||||
}
|
||||
}]
|
||||
})
|
||||
};
|
||||
|
||||
remote.request = function(request) {
|
||||
assert.deepEqual(request.message, {
|
||||
command: 'account_info',
|
||||
id: undefined,
|
||||
ident: 'rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B',
|
||||
account: 'rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B'
|
||||
});
|
||||
|
||||
request.emit('success', {
|
||||
account_data: {
|
||||
Account: 'rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B',
|
||||
Balance: '6156166959471',
|
||||
Domain: '6269747374616D702E6E6574',
|
||||
EmailHash: '5B33B93C7FFE384D53450FC666BB11FB',
|
||||
Flags: 131072,
|
||||
LedgerEntryType: 'AccountRoot',
|
||||
OwnerCount: 0,
|
||||
PreviousTxnID: '6A7D0AB36CBA6884FDC398254BC67DE7E0B4887E9B0252568391102FBB854C09',
|
||||
PreviousTxnLgrSeq: 8344426,
|
||||
Sequence: 561,
|
||||
TransferRate: 1002000000,
|
||||
index: 'B7D526FDDF9E3B3F95C3DC97C353065B0482302500BBB8051A5C090B596C6133',
|
||||
urlgravatar: 'http:www.gravatar.com/avatar/5b33b93c7ffe384d53450fc666bb11fb'
|
||||
}
|
||||
});
|
||||
|
||||
assert.strictEqual(book._issuerTransferRate, 1002000000);
|
||||
done();
|
||||
};
|
||||
|
||||
book.addCachedFunds('r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59', '100');
|
||||
book.updateFundedAmounts(message);
|
||||
});
|
||||
|
||||
it('Request offers', function(done) {
|
||||
var remote = new Remote();
|
||||
|
||||
var offers = {
|
||||
offers: [
|
||||
{
|
||||
Account: 'rGCHV41NxoK7wHQJhmao2RqjWZvBrTUhW1',
|
||||
BookDirectory: '6EAB7C172DEFA430DBFAD120FDC373B5F5AF8B191649EC985711A3A4254F5000',
|
||||
BookNode: '0000000000000000',
|
||||
Flags: 131072,
|
||||
LedgerEntryType: 'Offer',
|
||||
OwnerNode: '0000000000000000',
|
||||
PreviousTxnID: '9BB337CC8B34DC8D1A3FFF468556C8BA70977C37F7436439D8DA19610F214AD1',
|
||||
PreviousTxnLgrSeq: 8342933,
|
||||
Sequence: 195,
|
||||
TakerGets: {
|
||||
currency: 'BTC',
|
||||
issuer: 'rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B',
|
||||
value: '0.1129232560043778'
|
||||
},
|
||||
TakerPays: {
|
||||
currency: 'USD',
|
||||
issuer: 'rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B',
|
||||
value: '56.06639660617357'
|
||||
},
|
||||
index: 'B6BC3B0F87976370EE11F5575593FE63AA5DC1D602830DC96F04B2D597F044BF',
|
||||
owner_funds: '0.1129267125000245',
|
||||
quality: '496.5',
|
||||
taker_gets_funded: {
|
||||
currency: 'BTC',
|
||||
issuer: 'rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B',
|
||||
value: '0.1127013098802639'
|
||||
},
|
||||
taker_pays_funded: {
|
||||
currency: 'USD',
|
||||
issuer: 'rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B',
|
||||
value: '55.95620035555103'
|
||||
}
|
||||
},
|
||||
{
|
||||
Account: 'raudnGKfTK23YKfnS7ixejHrqGERTYNFXk',
|
||||
BookDirectory: '6EAB7C172DEFA430DBFAD120FDC373B5F5AF8B191649EC985711B6D8C62EF414',
|
||||
BookNode: '0000000000000000',
|
||||
Expiration: 461498565,
|
||||
Flags: 131072,
|
||||
LedgerEntryType: 'Offer',
|
||||
OwnerNode: '0000000000000144',
|
||||
PreviousTxnID: 'C8296B9CCA6DC594C7CD271C5D8FD11FEE380021A07768B25935642CDB37048A',
|
||||
PreviousTxnLgrSeq: 8342469,
|
||||
Sequence: 29354,
|
||||
TakerGets: {
|
||||
currency: 'BTC',
|
||||
issuer: 'rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B',
|
||||
value: '0.2'
|
||||
},
|
||||
TakerPays: {
|
||||
currency: 'USD',
|
||||
issuer: 'rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B',
|
||||
value: '99.72233516476456'
|
||||
},
|
||||
index: 'A437D85DF80D250F79308F2B613CF5391C7CF8EE9099BC4E553942651CD9FA86',
|
||||
owner_funds: '0.950363009783092',
|
||||
quality: '498.6116758238228'
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
remote.request = function(request) {
|
||||
switch (request.message.command) {
|
||||
case 'book_offers':
|
||||
assert.deepEqual(request.message, {
|
||||
command: 'book_offers',
|
||||
id: void(0),
|
||||
taker_gets: {
|
||||
currency: '0000000000000000000000004254430000000000',
|
||||
issuer: 'rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B'
|
||||
},
|
||||
taker_pays: {
|
||||
currency: '0000000000000000000000005553440000000000',
|
||||
issuer: 'rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B'
|
||||
},
|
||||
taker: 'rrrrrrrrrrrrrrrrrrrrBZbvji'
|
||||
});
|
||||
|
||||
setImmediate(function() {
|
||||
request.emit('success', offers);
|
||||
});
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
var book = remote.createOrderBook({
|
||||
currency_gets: 'BTC',
|
||||
issuer_gets: 'rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B',
|
||||
currency_pays: 'USD',
|
||||
issuer_pays: 'rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B'
|
||||
});
|
||||
|
||||
book._issuerTransferRate = 1002000000;
|
||||
|
||||
var expected = [
|
||||
{ Account: 'rGCHV41NxoK7wHQJhmao2RqjWZvBrTUhW1',
|
||||
BookDirectory: '6EAB7C172DEFA430DBFAD120FDC373B5F5AF8B191649EC985711A3A4254F5000',
|
||||
BookNode: '0000000000000000',
|
||||
Flags: 131072,
|
||||
LedgerEntryType: 'Offer',
|
||||
OwnerNode: '0000000000000000',
|
||||
PreviousTxnID: '9BB337CC8B34DC8D1A3FFF468556C8BA70977C37F7436439D8DA19610F214AD1',
|
||||
PreviousTxnLgrSeq: 8342933,
|
||||
Sequence: 195,
|
||||
TakerGets: { currency: 'BTC',
|
||||
issuer: 'rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B',
|
||||
value: '0.1129232560043778'
|
||||
},
|
||||
TakerPays: {
|
||||
currency: 'USD',
|
||||
issuer: 'rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B',
|
||||
value: '56.06639660617357'
|
||||
},
|
||||
index: 'B6BC3B0F87976370EE11F5575593FE63AA5DC1D602830DC96F04B2D597F044BF',
|
||||
owner_funds: '0.1129267125000245',
|
||||
quality: '496.5',
|
||||
taker_gets_funded: '0.1127013098802639',
|
||||
taker_pays_funded: '55.95620035555102',
|
||||
is_fully_funded: false },
|
||||
|
||||
{ Account: 'raudnGKfTK23YKfnS7ixejHrqGERTYNFXk',
|
||||
BookDirectory: '6EAB7C172DEFA430DBFAD120FDC373B5F5AF8B191649EC985711B6D8C62EF414',
|
||||
BookNode: '0000000000000000',
|
||||
Expiration: 461498565,
|
||||
Flags: 131072,
|
||||
LedgerEntryType: 'Offer',
|
||||
OwnerNode: '0000000000000144',
|
||||
PreviousTxnID: 'C8296B9CCA6DC594C7CD271C5D8FD11FEE380021A07768B25935642CDB37048A',
|
||||
PreviousTxnLgrSeq: 8342469,
|
||||
Sequence: 29354,
|
||||
TakerGets: {
|
||||
currency: 'BTC',
|
||||
issuer: 'rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B',
|
||||
value: '0.2'
|
||||
},
|
||||
TakerPays: {
|
||||
currency: 'USD',
|
||||
issuer: 'rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B',
|
||||
value: '99.72233516476456'
|
||||
},
|
||||
index: 'A437D85DF80D250F79308F2B613CF5391C7CF8EE9099BC4E553942651CD9FA86',
|
||||
owner_funds: '0.950363009783092',
|
||||
quality: '498.6116758238228',
|
||||
is_fully_funded: true,
|
||||
taker_gets_funded: '0.2',
|
||||
taker_pays_funded: '99.72233516476456'
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
book.once('model', function(model) {
|
||||
assert.deepEqual(model, expected);
|
||||
assert.strictEqual(book._synchronized, true);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -7,55 +7,45 @@ describe('Seed', function() {
|
||||
it('can generate many addresses', function () {
|
||||
var seed = Seed.from_json("masterpassphrase");
|
||||
|
||||
// Note: Created with jRippleAPI code
|
||||
// Link: https://github.com/pmarches/jRippleAPI/blob/master/src/jrippleapi/keys/RippleDeterministicKeyGenerator.java
|
||||
var test_data = [
|
||||
// Format:
|
||||
// [passphrase, address, nth-for-seed, expected-public-key]
|
||||
["masterpassphrase", "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh", 0,
|
||||
"0330E7FC9D56BB25D6893BA3F317AE5BCF33B3291BD63DB32654A313222F7FD020"],
|
||||
["masterpassphrase", "r4bYF7SLUMD7QgSLLpgJx38WJSY12ViRjP", 1,
|
||||
"02CD8C4CE87F86AAD1D9D18B03DE28E6E756F040BD72A9C127862833EB90D60BAD"],
|
||||
["masterpassphrase", "rLpAd4peHUMBPbVJASMYK5GTBUSwXRD9nx", 2,
|
||||
"0259A57642A6F4AEFC9B8062AF453FDEEEAC5572BA602BB1DBD5EF011394C6F9FC"],
|
||||
["otherpassphrase", "rpe3YWSVwGU2PmUzebAPg2deBXHtmba7hJ", 0,
|
||||
"022235A3DB2CAE57C60B7831929611D58867F86D28C0AD3C82473CC4A84990D01B"],
|
||||
["otherpassphrase", "raAPC2gALSmsTkXR4wUwQcPgX66kJuLv2S", 5,
|
||||
"03F0619AFABE08D22D98C8721895FE3673B6174168949976F2573CE1138C124994"],
|
||||
["yetanotherpassphrase", "rKnM44fS48qrGiDxB5fB5u64vHVJwjDPUo", 0,
|
||||
"0385AD049327EF7E5EC429350A15CEB23955037DE99660F6E70C11C5ABF4407036"],
|
||||
["yetanotherpassphrase", "rMvkT1RHPfsZwTFbKDKBEisa5U4d2a9V8n", 1,
|
||||
"023A2876EA130CBE7BBA0573C2DB4C4CEB9A7547666915BD40366CDC6150CF54DC"]
|
||||
];
|
||||
|
||||
// To reviewer/merger: Even generating keypairs for thi this small amount of
|
||||
// addresses makes the test suite run SO SLOW. Consider cutting at the tail.
|
||||
var first_nth_addresses = [
|
||||
// 0th implied
|
||||
"rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
|
||||
// 1th implied
|
||||
"r4bYF7SLUMD7QgSLLpgJx38WJSY12ViRjP",
|
||||
// 2th implied
|
||||
"rLpAd4peHUMBPbVJASMYK5GTBUSwXRD9nx",
|
||||
// ...
|
||||
"rN9HWHPAftXC6xNxJRqgVr1HexpUi6FBUa",
|
||||
"rhwPfeEkeQh2vqoKGPBpPyS9nKmKZdpypu",
|
||||
"rKdNfPfrzfisDCPCqK67YhDLezuVKGKXNm",
|
||||
"rsn4NeGzMRvMn5ABQxmt9VNEkSknVneBK4",
|
||||
"rUSJGFH1kQnaKpoAhkArfyw6HZVe8GT5w3",
|
||||
"r3EYp6isx2Row5pu19C4Ujvp68oEEabhuA",
|
||||
"rGiYpnAn9DTXiM78CCJcYAuL6QHEDdazHA",
|
||||
"rVrRVeqqEJHAove5B9TqBAHEXBTzMi5eu",
|
||||
"rJbarThDXYXxtCgDxRoTCDf2NoYdgSjuVk",
|
||||
"rfuWNJ1TkQzoZZcc4K8wmtsUiGwURFbed1",
|
||||
"rpuTqebfbZZdKEUV3bjecoViNL4W4gepWZ",
|
||||
"r42ywHUco4C2AjgaSmYM7uX13Kbz6GdDKp",
|
||||
"rnWb45MLd5hQiB6Arr94DdY2z95quL7XNf",
|
||||
"rMukaQ87bfAeddcT16RtgS3RbFmaQWrSGu",
|
||||
"rN6dMHU51K9y8eVMhjQMsQVp5gPwt3eYYx",
|
||||
"rfRFyeJDNqpfZVFmjNj9tNzFVsCNAWKtNc",
|
||||
"rMoGSX48KrT31HKx9KfMSnYhjvRw3YYzQW",
|
||||
"rfTiEfbeVsYU7QEe1Zfogiz7h43x6ChKGC"
|
||||
]
|
||||
|
||||
function assert_helper(account_id, expected) {
|
||||
var keypair = seed.get_key(account_id);
|
||||
assert.strictEqual(keypair.get_address().to_json(),
|
||||
function assert_helper(seed_json, address_or_nth, expected) {
|
||||
var seed = Seed.from_json(seed_json);
|
||||
var keypair = seed.get_key(address_or_nth);
|
||||
assert.strictEqual(keypair.to_hex_pub(),
|
||||
expected);
|
||||
}
|
||||
for (var nth = 0; nth < first_nth_addresses.length; nth++) {
|
||||
var expected = first_nth_addresses[nth], looking_for;
|
||||
for (var nth = 0; nth < test_data.length; nth++) {
|
||||
var seed_json = test_data[nth][0];
|
||||
var address = test_data[nth][1];
|
||||
var nth_for_seed = test_data[nth][2];
|
||||
var expected = test_data[nth][3];
|
||||
|
||||
//`seed.get_key($ripple_address)` is arguably an ill concieved feature
|
||||
// as it needs to generate `nth` many keypairs and generate hashed public
|
||||
// keys (addresses) for equality tests ??
|
||||
// Would need remote.set_secret(address, private_key_not_seed) ??
|
||||
assert_helper((looking_for = expected), expected)
|
||||
assert_helper(seed_json, address, expected);
|
||||
|
||||
// This isn't too bad as it only needs to generate one keypair `seq`
|
||||
assert_helper((looking_for = nth), expected)
|
||||
assert_helper(seed_json, nth_for_seed, expected);
|
||||
};
|
||||
});
|
||||
});
|
||||
|
||||
@@ -2,6 +2,7 @@ var utils = require('./testutils');
|
||||
var assert = require('assert');
|
||||
var SerializedObject = utils.load_module('serializedobject').SerializedObject;
|
||||
var types = utils.load_module('serializedtypes');
|
||||
var amountConstants = require('../src/js/ripple/amount').consts;
|
||||
var BigInteger = require('../src/js/jsbn/jsbn').BigInteger;
|
||||
|
||||
var config = require('./testutils').get_config();
|
||||
@@ -550,6 +551,11 @@ describe('Serialized types', function() {
|
||||
});
|
||||
assert.strictEqual(so.to_hex(), 'D5438D7EA4C68000015841551A748AD23FEFFFFFFFEA028000000000E4FE687C90257D3D2D694C8531CDEECBE84F3367');
|
||||
});
|
||||
it('Serialize max_value/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh', function () {
|
||||
var so = new SerializedObject();
|
||||
types.Amount.serialize(so, amountConstants.max_value+'/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh');
|
||||
assert.strictEqual(so.to_hex(), 'EC6386F26FC0FFFF0000000000000000000000005553440000000000B5F762798A53D543A014CAF8B297CFF8F2F937E8');
|
||||
});
|
||||
it('Parse 1 XRP', function () {
|
||||
var so = new SerializedObject('4000000000000001');
|
||||
assert.strictEqual(types.Amount.parse(so).to_json(), '1');
|
||||
@@ -582,6 +588,10 @@ describe('Serialized types', function() {
|
||||
var so = new SerializedObject('94838D7EA4C680000000000000000000000000005553440000000000B5F762798A53D543A014CAF8B297CFF8F2F937E8');
|
||||
assert.strictEqual(types.Amount.parse(so).to_text_full(), '-1/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh');
|
||||
});
|
||||
it('Parse max_value/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh', function () {
|
||||
var so = new SerializedObject('EC6386F26FC0FFFF0000000000000000000000005553440000000000B5F762798A53D543A014CAF8B297CFF8F2F937E8');
|
||||
assert.strictEqual(types.Amount.parse(so).to_text_full(), amountConstants.max_value+'/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Account', function() {
|
||||
|
||||
@@ -478,6 +478,58 @@ describe('Server', function() {
|
||||
Server.websocketConstructor = websocketConstructor;
|
||||
});
|
||||
|
||||
it('Connect - partial history disabled', function(done) {
|
||||
var wss = new ws.Server({ port: 5748 });
|
||||
|
||||
wss.once('connection', function(ws) {
|
||||
ws.once('message', function(message) {
|
||||
var m = JSON.parse(message);
|
||||
|
||||
assert.deepEqual(m, {
|
||||
command: 'subscribe',
|
||||
id: 0,
|
||||
streams: [ 'ledger', 'server' ]
|
||||
});
|
||||
|
||||
ws.send(JSON.stringify({
|
||||
id: 0,
|
||||
status: 'success',
|
||||
type: 'response',
|
||||
result: {
|
||||
fee_base: 10,
|
||||
fee_ref: 10,
|
||||
ledger_hash: '1838539EE12463C36F2C53B079D807C697E3D93A1936B717E565A4A912E11776',
|
||||
ledger_index: 7053695,
|
||||
ledger_time: 455414390,
|
||||
load_base: 256,
|
||||
load_factor: 256,
|
||||
random: 'E56C9154D9BE94D49C581179356C2E084E16D18D74E8B09093F2D61207625E6A',
|
||||
reserve_base: 20000000,
|
||||
reserve_inc: 5000000,
|
||||
server_status: 'syncing',
|
||||
validated_ledgers: '3175520-3176615'
|
||||
}
|
||||
}));
|
||||
|
||||
wss.close();
|
||||
});
|
||||
});
|
||||
|
||||
var server = new Server(new Remote({ allow_partial_history: false }), 'ws://localhost:5748');
|
||||
|
||||
server.reconnect = function() {
|
||||
setImmediate(function() {
|
||||
done();
|
||||
});
|
||||
};
|
||||
|
||||
server.once('connect', function() {
|
||||
assert(false, 'Should not connect');
|
||||
});
|
||||
|
||||
server.connect();
|
||||
});
|
||||
|
||||
it('Reconnect', function(done) {
|
||||
var server = new Server(new Remote(), 'ws://localhost:5748');
|
||||
server._connected = true;
|
||||
|
||||
@@ -26,8 +26,9 @@ describe('Signing', function() {
|
||||
assert(_isNaN(new Seed().parse_json('').to_json()));
|
||||
});
|
||||
it('hex string', function() {
|
||||
// 32 0s is a valid hex repr of seed bytes
|
||||
var str = new Array(33).join('0');
|
||||
assert(_isNaN(new Seed().parse_json(str).to_json()));
|
||||
assert.strictEqual((new Seed().parse_json(str).to_json()), 'sp6JS7f14BuwFY8Mw6bTtLKWauoUs');
|
||||
});
|
||||
it('passphrase', function() {
|
||||
var str = new Array(60).join('0');
|
||||
|
||||
@@ -292,9 +292,7 @@ describe('Transaction', function() {
|
||||
var s3 = new Server(remote, 'wss://s-west.ripple.com:443');
|
||||
s3._connected = true;
|
||||
|
||||
// <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
|
||||
s3._load_factor = (256 * 7) + 1;
|
||||
// Is this ever possible? <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
|
||||
|
||||
var s4 = new Server(remote, 'wss://s-west.ripple.com:443');
|
||||
s4._connected = true;
|
||||
@@ -813,6 +811,10 @@ describe('Transaction', function() {
|
||||
assert.strictEqual(transaction.tx_json.LastLedgerSequence, void(0));
|
||||
assert(!transaction._setLastLedger);
|
||||
|
||||
transaction.lastLedger(NaN);
|
||||
assert.strictEqual(transaction.tx_json.LastLedgerSequence, void(0));
|
||||
assert(!transaction._setLastLedger);
|
||||
|
||||
transaction.lastLedger(12);
|
||||
assert.strictEqual(transaction.tx_json.LastLedgerSequence, 12);
|
||||
assert(transaction._setLastLedger);
|
||||
@@ -1017,6 +1019,53 @@ describe('Transaction', function() {
|
||||
transaction.setFlags('test');
|
||||
});
|
||||
|
||||
it('Add Memo', function() {
|
||||
var transaction = new Transaction();
|
||||
transaction.tx_json.TransactionType = 'Payment';
|
||||
|
||||
transaction.addMemo('testkey', 'testvalue');
|
||||
transaction.addMemo('testkey2', 'testvalue2');
|
||||
transaction.addMemo('testkey3');
|
||||
transaction.addMemo(void(0), 'testvalue4');
|
||||
|
||||
var expected = [
|
||||
{ Memo: {
|
||||
MemoType: new Buffer('testkey').toString('hex'),
|
||||
MemoData: new Buffer('testvalue').toString('hex')
|
||||
}},
|
||||
{ Memo: {
|
||||
MemoType: new Buffer('testkey2').toString('hex'),
|
||||
MemoData: new Buffer('testvalue2').toString('hex')
|
||||
}},
|
||||
{ Memo: {
|
||||
MemoType: new Buffer('testkey3').toString('hex')
|
||||
}},
|
||||
{ Memo: {
|
||||
MemoData: new Buffer('testvalue4').toString('hex')
|
||||
} }
|
||||
];
|
||||
|
||||
assert.deepEqual(transaction.tx_json.Memos, expected);
|
||||
});
|
||||
|
||||
it('Add Memo - invalid MemoType', function() {
|
||||
var transaction = new Transaction();
|
||||
transaction.tx_json.TransactionType = 'Payment';
|
||||
|
||||
assert.throws(function() {
|
||||
transaction.addMemo(1);
|
||||
}, /^Error: MemoType must be a string$/);
|
||||
});
|
||||
|
||||
it('Add Memo - invalid MemoData', function() {
|
||||
var transaction = new Transaction();
|
||||
transaction.tx_json.TransactionType = 'Payment';
|
||||
|
||||
assert.throws(function() {
|
||||
transaction.addMemo('key', 1);
|
||||
}, /^Error: MemoData must be a string$/);
|
||||
});
|
||||
|
||||
it('Construct AccountSet transaction', function() {
|
||||
var transaction = new Transaction().accountSet('rsLEU1TPdCJPPysqhWYw9jD97xtG5WqSJm');
|
||||
|
||||
|
||||
@@ -709,7 +709,7 @@ describe('Blob', function () {
|
||||
});
|
||||
|
||||
it('#2FA_get2FA', function (done) {
|
||||
blob.get2FA(exampleData.masterkey, function(err, resp) {
|
||||
blob.get2FA(function(err, resp) {
|
||||
assert.ifError(err);
|
||||
assert.strictEqual(typeof resp, 'object');
|
||||
assert.strictEqual(typeof resp.result, 'string');
|
||||
|
||||
Reference in New Issue
Block a user