mirror of
https://github.com/Xahau/xahau.js.git
synced 2025-11-20 04:05:52 +00:00
Fixes and cleanup for Remote, update readme
This commit is contained in:
103
README.md
103
README.md
@@ -3,9 +3,108 @@ Ripple JavaScript Library - ripple-lib
|
|||||||
|
|
||||||
This library can connect to the Ripple network via the WebSocket protocol and runs in Node.js as well as in the browser.
|
This library can connect to the Ripple network via the WebSocket protocol and runs in Node.js as well as in the browser.
|
||||||
|
|
||||||
Build instructions:
|
##Building
|
||||||
|
|
||||||
* https://ripple.com/wiki/Ripple_JavaScript_library
|
* https://ripple.com/wiki/Ripple_JavaScript_library
|
||||||
|
|
||||||
For more information:
|
##See also
|
||||||
|
|
||||||
* https://ripple.com
|
* https://ripple.com
|
||||||
* https://ripple.com/wiki
|
* https://ripple.com/wiki
|
||||||
|
|
||||||
|
##Initializing a remote connection
|
||||||
|
|
||||||
|
[ripple-lib.remote](https://github.com/ripple/ripple-lib/blob/develop/src/js/ripple/remote.js) is responsible for managing connections to rippled servers.
|
||||||
|
|
||||||
|
```js
|
||||||
|
var remote = require('ripple-lib').Remote({
|
||||||
|
servers: [
|
||||||
|
{
|
||||||
|
host: ''
|
||||||
|
, port: 1111,
|
||||||
|
, secure: true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
##Remote functions
|
||||||
|
|
||||||
|
Each remote function returns a `Request` object. This object is an `EventEmitter`. You may listen for success or failure events from each request, or provide a callback. Example:
|
||||||
|
|
||||||
|
```js
|
||||||
|
var request = remote.request_server_info();
|
||||||
|
request.on('success', function(res) {
|
||||||
|
//handle success conditions
|
||||||
|
});
|
||||||
|
request.on('error', function(err) {
|
||||||
|
//handle error conditions
|
||||||
|
});
|
||||||
|
request.request();
|
||||||
|
```
|
||||||
|
|
||||||
|
Or:
|
||||||
|
|
||||||
|
```js
|
||||||
|
remote.request_server_info(function(err, res) {
|
||||||
|
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
**remote.request_server_info([callback])**
|
||||||
|
|
||||||
|
**remote.request_ledger(ledger, [opts], [callback])**
|
||||||
|
|
||||||
|
**remote.request_ledger_hash([callback])**
|
||||||
|
|
||||||
|
**remote.request_ledger_header([callback])**
|
||||||
|
|
||||||
|
**remote.request_ledger_current([callback])**
|
||||||
|
|
||||||
|
**remote.request_ledger_entry(type, [callback])**
|
||||||
|
|
||||||
|
**remote.request_subscribe(streams, [callback])**
|
||||||
|
|
||||||
|
**remote.request_unsubscribe(streams, [callback])**
|
||||||
|
|
||||||
|
**remote.request_transaction_entry(hash, [callback])**
|
||||||
|
|
||||||
|
**remote.request_tx(hash, [callback])**
|
||||||
|
|
||||||
|
**remote.request_account_info(accountID, [callback])**
|
||||||
|
|
||||||
|
**remote.request_account_lines(accountID, account_index, current, [callback])**
|
||||||
|
|
||||||
|
**remote.request_account_offers(accountID, account_index, current, [callback])**
|
||||||
|
|
||||||
|
**remote.request_account_tx(opts, [callback])**
|
||||||
|
|
||||||
|
**remote.request_book_offers(gets, pays, taker, [callback])**
|
||||||
|
|
||||||
|
**remote.request_wallet_accounts(seed, [callback])**
|
||||||
|
|
||||||
|
**remote.request_sign(secret, tx_json, [callback])**
|
||||||
|
|
||||||
|
**remote.request_submit([callback])**
|
||||||
|
|
||||||
|
**remote.request_account_balance(account, current, [callback])**
|
||||||
|
|
||||||
|
**remote.request_account_flags(account, current, [callback])**
|
||||||
|
|
||||||
|
**remote.request_owner_count(account, current, [callback])**
|
||||||
|
|
||||||
|
**remote.request_ripple_balance(account, issuer, currency, current, [callback])**
|
||||||
|
|
||||||
|
**remote.request_ripple_path_find(src_account, dst_account, dst_amount, src_currencies, [callback])**
|
||||||
|
|
||||||
|
**remote.request_unl_list([callback])**
|
||||||
|
|
||||||
|
**remote.request_unl_add(addr, comment, [callback])**
|
||||||
|
|
||||||
|
**remote.request_unl_delete(node, [callback])**
|
||||||
|
|
||||||
|
**remote.request_peers([callback])**
|
||||||
|
|
||||||
|
**remote.request_connect(ip, port, [callback])**
|
||||||
|
|
||||||
|
**remote.transaction()**
|
||||||
|
|||||||
@@ -9,35 +9,32 @@
|
|||||||
// var network = require("./network.js");
|
// var network = require("./network.js");
|
||||||
|
|
||||||
var EventEmitter = require('events').EventEmitter;
|
var EventEmitter = require('events').EventEmitter;
|
||||||
var util = require('util');
|
var util = require('util');
|
||||||
|
|
||||||
var Amount = require('./amount').Amount;
|
var Amount = require('./amount').Amount;
|
||||||
var UInt160 = require('./uint160').UInt160;
|
var UInt160 = require('./uint160').UInt160;
|
||||||
var Currency = require('./currency').Currency;
|
var Currency = require('./currency').Currency;
|
||||||
|
|
||||||
var extend = require('extend');
|
var extend = require('extend');
|
||||||
|
|
||||||
var OrderBook = function (remote,
|
var OrderBook = function (remote, currency_gets, issuer_gets, currency_pays, issuer_pays) {
|
||||||
currency_gets, issuer_gets,
|
|
||||||
currency_pays, issuer_pays) {
|
|
||||||
EventEmitter.call(this);
|
EventEmitter.call(this);
|
||||||
|
|
||||||
var self = this;
|
var self = this;
|
||||||
|
|
||||||
this._remote = remote;
|
this._remote = remote;
|
||||||
this._currency_gets = currency_gets;
|
this._currency_gets = currency_gets;
|
||||||
this._issuer_gets = issuer_gets;
|
this._issuer_gets = issuer_gets;
|
||||||
this._currency_pays = currency_pays;
|
this._currency_pays = currency_pays;
|
||||||
this._issuer_pays = issuer_pays;
|
this._issuer_pays = issuer_pays;
|
||||||
|
this._subs = 0;
|
||||||
this._subs = 0;
|
|
||||||
|
|
||||||
// We consider ourselves synchronized if we have a current copy of the offers,
|
// We consider ourselves synchronized if we have a current copy of the offers,
|
||||||
// we are online and subscribed to updates.
|
// we are online and subscribed to updates.
|
||||||
this._sync = false;
|
this._sync = false;
|
||||||
|
|
||||||
// Offers
|
// Offers
|
||||||
this._offers = [];
|
this._offers = [];
|
||||||
|
|
||||||
this.on('newListener', function (type, listener) {
|
this.on('newListener', function (type, listener) {
|
||||||
if (OrderBook.subscribe_events.indexOf(type) !== -1) {
|
if (OrderBook.subscribe_events.indexOf(type) !== -1) {
|
||||||
@@ -49,10 +46,9 @@ var OrderBook = function (remote,
|
|||||||
});
|
});
|
||||||
|
|
||||||
this.on('removeListener', function (type, listener) {
|
this.on('removeListener', function (type, listener) {
|
||||||
if (OrderBook.subscribe_events.indexOf(type) !== -1) {
|
if (~OrderBook.subscribe_events.indexOf(type)) {
|
||||||
self._subs -= 1;
|
self._subs -= 1;
|
||||||
|
if (!self._subs && self._remote._connected) {
|
||||||
if (!self._subs && 'open' === self._remote._online_state) {
|
|
||||||
self._sync = false;
|
self._sync = false;
|
||||||
self._remote.request_unsubscribe()
|
self._remote.request_unsubscribe()
|
||||||
.books([self.to_json()])
|
.books([self.to_json()])
|
||||||
@@ -86,8 +82,7 @@ OrderBook.subscribe_events = ['transaction', 'model', 'trade'];
|
|||||||
*
|
*
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
OrderBook.prototype._subscribe = function ()
|
OrderBook.prototype._subscribe = function () {
|
||||||
{
|
|
||||||
var self = this;
|
var self = this;
|
||||||
self._remote.request_subscribe()
|
self._remote.request_subscribe()
|
||||||
.books([self.to_json()], true)
|
.books([self.to_json()], true)
|
||||||
@@ -95,26 +90,28 @@ OrderBook.prototype._subscribe = function ()
|
|||||||
// XXX What now?
|
// XXX What now?
|
||||||
})
|
})
|
||||||
.on('success', function (res) {
|
.on('success', function (res) {
|
||||||
self._sync = true;
|
self._sync = true;
|
||||||
self._offers = res.offers;
|
self._offers = res.offers;
|
||||||
self.emit('model', self._offers);
|
self.emit('model', self._offers);
|
||||||
})
|
})
|
||||||
.request();
|
.request();
|
||||||
};
|
};
|
||||||
|
|
||||||
OrderBook.prototype.to_json = function ()
|
OrderBook.prototype.to_json = function () {
|
||||||
{
|
|
||||||
var json = {
|
var json = {
|
||||||
"taker_gets": {
|
'taker_gets': {
|
||||||
"currency": this._currency_gets
|
'currency': this._currency_gets
|
||||||
},
|
},
|
||||||
"taker_pays": {
|
'taker_pays': {
|
||||||
"currency": this._currency_pays
|
'currency': this._currency_pays
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if (this._currency_gets !== "XRP") json["taker_gets"]["issuer"] = this._issuer_gets;
|
if (this._currency_gets !== 'XRP')
|
||||||
if (this._currency_pays !== "XRP") json["taker_pays"]["issuer"] = this._issuer_pays;
|
json['taker_gets']['issuer'] = this._issuer_gets;
|
||||||
|
|
||||||
|
if (this._currency_pays !== 'XRP')
|
||||||
|
json['taker_pays']['issuer'] = this._issuer_pays;
|
||||||
|
|
||||||
return json;
|
return json;
|
||||||
};
|
};
|
||||||
@@ -125,78 +122,88 @@ OrderBook.prototype.to_json = function ()
|
|||||||
* Note: This only checks whether the parameters (currencies and issuer) are
|
* Note: This only checks whether the parameters (currencies and issuer) are
|
||||||
* syntactically valid. It does not check anything against the ledger.
|
* syntactically valid. It does not check anything against the ledger.
|
||||||
*/
|
*/
|
||||||
OrderBook.prototype.is_valid = function ()
|
OrderBook.prototype.is_valid = function () {
|
||||||
{
|
|
||||||
// XXX Should check for same currency (non-native) && same issuer
|
// XXX Should check for same currency (non-native) && same issuer
|
||||||
return (
|
return (
|
||||||
Currency.is_valid(this._currency_pays) &&
|
Currency.is_valid(this._currency_pays) &&
|
||||||
(this._currency_pays === "XRP" || UInt160.is_valid(this._issuer_pays)) &&
|
(this._currency_pays === 'XRP' || UInt160.is_valid(this._issuer_pays)) &&
|
||||||
Currency.is_valid(this._currency_gets) &&
|
Currency.is_valid(this._currency_gets) &&
|
||||||
(this._currency_gets === "XRP" || UInt160.is_valid(this._issuer_gets)) &&
|
(this._currency_gets === 'XRP' || UInt160.is_valid(this._issuer_gets)) &&
|
||||||
!(this._currency_pays === "XRP" && this._currency_gets === "XRP")
|
!(this._currency_pays === 'XRP' && this._currency_gets === 'XRP')
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
OrderBook.prototype.trade = function(type) {
|
||||||
|
var tradeStr = '0'
|
||||||
|
+ (this['_currency_' + type] === 'XRP') ? '' : '/'
|
||||||
|
+ this['_currency_' + type ] + '/'
|
||||||
|
+ this['_issuer_' + type];
|
||||||
|
return Amount.from_json(tradeStr);
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Notify object of a relevant transaction.
|
* Notify object of a relevant transaction.
|
||||||
*
|
*
|
||||||
* This is only meant to be called by the Remote class. You should never have to
|
* This is only meant to be called by the Remote class. You should never have to
|
||||||
* call this yourself.
|
* call this yourself.
|
||||||
*/
|
*/
|
||||||
OrderBook.prototype.notifyTx = function (message)
|
OrderBook.prototype.notifyTx = function (message) {
|
||||||
{
|
var self = this;
|
||||||
var self = this;
|
var changed = false;
|
||||||
|
var trade_gets = this.trade('gets');
|
||||||
var changed = false;
|
var trade_pays = this.trade('pays');
|
||||||
|
|
||||||
var trade_gets = Amount.from_json("0" + ((this._currency_gets === 'XRP') ? "" :
|
|
||||||
"/" + this._currency_gets +
|
|
||||||
"/" + this._issuer_gets));
|
|
||||||
var trade_pays = Amount.from_json("0" + ((this._currency_pays === 'XRP') ? "" :
|
|
||||||
"/" + this._currency_pays +
|
|
||||||
"/" + this._issuer_pays));
|
|
||||||
|
|
||||||
message.mmeta.each(function (an) {
|
message.mmeta.each(function (an) {
|
||||||
if (an.entryType !== 'Offer') return;
|
if (an.entryType !== 'Offer') return;
|
||||||
|
|
||||||
var i, l, offer;
|
var i, l, offer;
|
||||||
if (an.diffType === 'DeletedNode' ||
|
|
||||||
an.diffType === 'ModifiedNode') {
|
switch(an.diffType) {
|
||||||
for (i = 0, l = self._offers.length; i < l; i++) {
|
case 'DeletedNode':
|
||||||
offer = self._offers[i];
|
case 'ModifiedNode':
|
||||||
if (offer.index === an.ledgerIndex) {
|
var deletedNode = an.diffType === 'DeletedNode';
|
||||||
if (an.diffType === 'DeletedNode') {
|
|
||||||
self._offers.splice(i, 1);
|
for (i = 0, l = self._offers.length; i < l; i++) {
|
||||||
|
offer = self._offers[i];
|
||||||
|
if (offer.index === an.ledgerIndex) {
|
||||||
|
if (deletedNode) {
|
||||||
|
self._offers.splice(i, 1);
|
||||||
|
} else {
|
||||||
|
extend(offer, an.fieldsFinal);
|
||||||
|
}
|
||||||
|
changed = true;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
else extend(offer, an.fieldsFinal);
|
|
||||||
changed = true;
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// We don't want to count a OfferCancel as a trade
|
// We don't want to count a OfferCancel as a trade
|
||||||
if (message.transaction.TransactionType === "OfferCancel") return;
|
if (message.transaction.TransactionType === 'OfferCancel') return;
|
||||||
|
|
||||||
trade_gets = trade_gets.add(an.fieldsPrev.TakerGets);
|
trade_gets = trade_gets.add(an.fieldsPrev.TakerGets);
|
||||||
trade_pays = trade_pays.add(an.fieldsPrev.TakerPays);
|
trade_pays = trade_pays.add(an.fieldsPrev.TakerPays);
|
||||||
if (an.diffType === 'ModifiedNode') {
|
|
||||||
trade_gets = trade_gets.subtract(an.fieldsFinal.TakerGets);
|
|
||||||
trade_pays = trade_pays.subtract(an.fieldsFinal.TakerPays);
|
|
||||||
}
|
|
||||||
} else if (an.diffType === 'CreatedNode') {
|
|
||||||
var price = Amount.from_json(an.fields.TakerPays).ratio_human(an.fields.TakerGets);
|
|
||||||
for (i = 0, l = self._offers.length; i < l; i++) {
|
|
||||||
offer = self._offers[i];
|
|
||||||
var priceItem = Amount.from_json(offer.TakerPays).ratio_human(offer.TakerGets);
|
|
||||||
|
|
||||||
if (price.compareTo(priceItem) <= 0) {
|
if (!deletedNode) {
|
||||||
var obj = an.fields;
|
trade_gets = trade_gets.subtract(an.fieldsFinal.TakerGets);
|
||||||
obj.index = an.ledgerIndex;
|
trade_pays = trade_pays.subtract(an.fieldsFinal.TakerPays);
|
||||||
self._offers.splice(i, 0, an.fields);
|
|
||||||
changed = true;
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
break;
|
||||||
|
|
||||||
|
case 'CreatedNode':
|
||||||
|
var price = Amount.from_json(an.fields.TakerPays).ratio_human(an.fields.TakerGets);
|
||||||
|
|
||||||
|
for (i = 0, l = self._offers.length; i < l; i++) {
|
||||||
|
offer = self._offers[i];
|
||||||
|
var priceItem = Amount.from_json(offer.TakerPays).ratio_human(offer.TakerGets);
|
||||||
|
|
||||||
|
if (price.compareTo(priceItem) <= 0) {
|
||||||
|
var obj = an.fields;
|
||||||
|
obj.index = an.ledgerIndex;
|
||||||
|
self._offers.splice(i, 0, an.fields);
|
||||||
|
changed = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -218,17 +225,13 @@ OrderBook.prototype.notifyTx = function (message)
|
|||||||
*
|
*
|
||||||
* If the data is available immediately, the callback may be called synchronously.
|
* If the data is available immediately, the callback may be called synchronously.
|
||||||
*/
|
*/
|
||||||
OrderBook.prototype.offers = function (callback)
|
OrderBook.prototype.offers = function (callback) {
|
||||||
{
|
|
||||||
var self = this;
|
var self = this;
|
||||||
|
if (typeof callback === 'function') {
|
||||||
if ("function" === typeof callback) {
|
|
||||||
if (this._sync) {
|
if (this._sync) {
|
||||||
callback(this._offers);
|
callback(this._offers);
|
||||||
} else {
|
} else {
|
||||||
this.once('model', function (offers) {
|
this.once('model', callback);
|
||||||
callback(offers);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return this;
|
return this;
|
||||||
@@ -240,11 +243,10 @@ OrderBook.prototype.offers = function (callback)
|
|||||||
* Usually, this will just be an empty array if the order book hasn't been
|
* Usually, this will just be an empty array if the order book hasn't been
|
||||||
* loaded yet. But this accessor may be convenient in some circumstances.
|
* loaded yet. But this accessor may be convenient in some circumstances.
|
||||||
*/
|
*/
|
||||||
OrderBook.prototype.offersSync = function ()
|
OrderBook.prototype.offersSync = function () {
|
||||||
{
|
|
||||||
return this._offers;
|
return this._offers;
|
||||||
};
|
};
|
||||||
|
|
||||||
exports.OrderBook = OrderBook;
|
exports.OrderBook = OrderBook;
|
||||||
|
|
||||||
// vim:sw=2:sts=2:ts=8:et
|
// vim:sw=2:sts=2:ts=8:et
|
||||||
|
|||||||
@@ -37,16 +37,13 @@ var sjcl = require('../../../build/sjcl');
|
|||||||
// 'remoteError'
|
// 'remoteError'
|
||||||
// 'remoteUnexpected'
|
// 'remoteUnexpected'
|
||||||
// 'remoteDisconnected'
|
// 'remoteDisconnected'
|
||||||
var Request = function (remote, command) {
|
function Request(remote, command) {
|
||||||
EventEmitter.call(this);
|
EventEmitter.call(this);
|
||||||
|
|
||||||
var self = this;
|
|
||||||
|
|
||||||
this.remote = remote;
|
this.remote = remote;
|
||||||
this.requested = false;
|
this.requested = false;
|
||||||
this.message = {
|
this.message = {
|
||||||
'command' : command,
|
command : command,
|
||||||
'id' : undefined,
|
id : void(0)
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -67,6 +64,7 @@ Request.prototype.callback = function(callback, successEvent, errorEvent) {
|
|||||||
this.once(errorEvent || 'error', callback);
|
this.once(errorEvent || 'error', callback);
|
||||||
this.request();
|
this.request();
|
||||||
}
|
}
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -143,8 +141,8 @@ Request.prototype.index = function (hash) {
|
|||||||
// --> seq : sequence number of transaction creating offer (integer)
|
// --> seq : sequence number of transaction creating offer (integer)
|
||||||
Request.prototype.offer_id = function (account, seq) {
|
Request.prototype.offer_id = function (account, seq) {
|
||||||
this.message.offer = {
|
this.message.offer = {
|
||||||
'account' : UInt160.json_rewrite(account),
|
account: UInt160.json_rewrite(account),
|
||||||
'seq' : seq
|
seq: seq
|
||||||
};
|
};
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
@@ -228,20 +226,20 @@ Request.prototype.books = function (books, snapshot) {
|
|||||||
function processSide(side) {
|
function processSide(side) {
|
||||||
if (!book[side]) throw new Error('Missing '+side);
|
if (!book[side]) throw new Error('Missing '+side);
|
||||||
|
|
||||||
var obj = {};
|
var obj = json[side] = {
|
||||||
obj['currency'] = Currency.json_rewrite(book[side]['currency']);
|
currency: Currency.json_rewrite(book[side].currency)
|
||||||
if (obj['currency'] !== 'XRP') {
|
};
|
||||||
obj.issuer = UInt160.json_rewrite(book[side]['issuer']);
|
|
||||||
|
if (obj.currency !== 'XRP') {
|
||||||
|
obj.issuer = UInt160.json_rewrite(book[side].issuer);
|
||||||
}
|
}
|
||||||
|
|
||||||
json[side] = obj;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
processSide('taker_gets');
|
processSide('taker_gets');
|
||||||
processSide('taker_pays');
|
processSide('taker_pays');
|
||||||
|
|
||||||
if (snapshot || book['snapshot']) json['snapshot'] = true;
|
if (snapshot) json.snapshot = true;
|
||||||
if (book['both']) json['both'] = true;
|
if (book.both) json.both = true;
|
||||||
|
|
||||||
procBooks.push(json);
|
procBooks.push(json);
|
||||||
}
|
}
|
||||||
@@ -286,7 +284,7 @@ Request.prototype.books = function (books, snapshot) {
|
|||||||
@param trace
|
@param trace
|
||||||
*/
|
*/
|
||||||
|
|
||||||
var Remote = function (opts, trace) {
|
function Remote(opts, trace) {
|
||||||
EventEmitter.call(this);
|
EventEmitter.call(this);
|
||||||
|
|
||||||
var self = this;
|
var self = this;
|
||||||
@@ -321,7 +319,6 @@ var Remote = function (opts, trace) {
|
|||||||
this._connection_count = 0;
|
this._connection_count = 0;
|
||||||
this._connected = false;
|
this._connected = false;
|
||||||
|
|
||||||
|
|
||||||
this._last_tx = null;
|
this._last_tx = null;
|
||||||
|
|
||||||
// Local signing implies local fees and sequences
|
// Local signing implies local fees and sequences
|
||||||
@@ -661,6 +658,7 @@ Remote.prototype._server_is_available = function (server) {
|
|||||||
|
|
||||||
Remote.prototype._next_server = function () {
|
Remote.prototype._next_server = function () {
|
||||||
var result = null;
|
var result = null;
|
||||||
|
|
||||||
for (var i=0; i<this._servers.length; i++) {
|
for (var i=0; i<this._servers.length; i++) {
|
||||||
var server = this._servers[i];
|
var server = this._servers[i];
|
||||||
if (this._server_is_available(server)) {
|
if (this._server_is_available(server)) {
|
||||||
@@ -668,17 +666,20 @@ Remote.prototype._next_server = function () {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
};
|
};
|
||||||
|
|
||||||
Remote.prototype._get_server = function () {
|
Remote.prototype._get_server = function () {
|
||||||
var server;
|
var server;
|
||||||
|
|
||||||
if (this._server_is_available(this._primary_server)) {
|
if (this._server_is_available(this._primary_server)) {
|
||||||
server = this._primary_server;
|
server = this._primary_server;
|
||||||
} else {
|
} else {
|
||||||
server = this._next_server();
|
server = this._next_server();
|
||||||
if (server) this._set_primary_server(server);
|
if (server) this._set_primary_server(server);
|
||||||
}
|
}
|
||||||
|
|
||||||
return server;
|
return server;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -716,7 +717,7 @@ Remote.prototype.request_ledger = function (ledger, opts, callback) {
|
|||||||
request.message.ledger = ledger;
|
request.message.ledger = ledger;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ('object' == typeof opts) {
|
if (typeof opts === 'object') {
|
||||||
if (opts.full)
|
if (opts.full)
|
||||||
request.message.full = true;
|
request.message.full = true;
|
||||||
|
|
||||||
@@ -728,9 +729,7 @@ Remote.prototype.request_ledger = function (ledger, opts, callback) {
|
|||||||
|
|
||||||
if (opts.accounts)
|
if (opts.accounts)
|
||||||
request.message.accounts = true;
|
request.message.accounts = true;
|
||||||
}
|
} else if (opts) { // DEPRECATED:
|
||||||
// DEPRECATED:
|
|
||||||
else if (opts) {
|
|
||||||
console.log('request_ledger: full parameter is deprecated');
|
console.log('request_ledger: full parameter is deprecated');
|
||||||
request.message.full = true;
|
request.message.full = true;
|
||||||
}
|
}
|
||||||
@@ -841,10 +840,7 @@ Remote.prototype.request_subscribe = function (streams, callback) {
|
|||||||
var request = new Request(this, 'subscribe');
|
var request = new Request(this, 'subscribe');
|
||||||
|
|
||||||
if (streams) {
|
if (streams) {
|
||||||
if (!Array.isArray(streams)) {
|
request.message.streams = Array.isArray(streams) ? streams : [ streams ];
|
||||||
streams = [ streams ];
|
|
||||||
}
|
|
||||||
request.message.streams = streams;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return request.callback(callback);
|
return request.callback(callback);
|
||||||
@@ -855,10 +851,7 @@ Remote.prototype.request_unsubscribe = function (streams, callback) {
|
|||||||
var request = new Request(this, 'unsubscribe');
|
var request = new Request(this, 'unsubscribe');
|
||||||
|
|
||||||
if (streams) {
|
if (streams) {
|
||||||
if (!Array.isArray(streams)) {
|
request.message.streams = Array.isArray(streams) ? streams : [ streams ];
|
||||||
streams = [ streams ];
|
|
||||||
}
|
|
||||||
request.message.streams = streams;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return request.callback(callback);
|
return request.callback(callback);
|
||||||
@@ -1247,7 +1240,7 @@ Remote.prototype.account_seq_cache = function (account, current, callback) {
|
|||||||
|
|
||||||
// Mark an account's root node as dirty.
|
// Mark an account's root node as dirty.
|
||||||
Remote.prototype.dirty_account_root = function (account) {
|
Remote.prototype.dirty_account_root = function (account) {
|
||||||
var account = UInt160.json_rewrite(account);
|
var account = UInt160.json_rewrite(account);
|
||||||
|
|
||||||
delete this.ledgers.current.account_root[account];
|
delete this.ledgers.current.account_root[account];
|
||||||
};
|
};
|
||||||
@@ -1349,8 +1342,8 @@ Remote.prototype.request_unl_delete = function (node, callback) {
|
|||||||
return request.callback(callback);
|
return request.callback(callback);
|
||||||
};
|
};
|
||||||
|
|
||||||
Remote.prototype.request_peers = function () {
|
Remote.prototype.request_peers = function (callback) {
|
||||||
return new Request(this, 'peers');
|
return new Request(this, 'peers').callback(callback);
|
||||||
};
|
};
|
||||||
|
|
||||||
Remote.prototype.request_connect = function (ip, port, callback) {
|
Remote.prototype.request_connect = function (ip, port, callback) {
|
||||||
|
|||||||
@@ -184,6 +184,10 @@ Server.prototype.disconnect = function () {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Server.prototype.send_message = function (message) {
|
||||||
|
this._ws.send(JSON.stringify(message));
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Submit a Request object to this server.
|
* Submit a Request object to this server.
|
||||||
*/
|
*/
|
||||||
@@ -199,20 +203,19 @@ Server.prototype.request = function (request) {
|
|||||||
// Advance message ID
|
// Advance message ID
|
||||||
self._id++;
|
self._id++;
|
||||||
|
|
||||||
if (self._state === 'online' ||
|
if (self._connected || (request.message.command === 'subscribe' && self._ws.readyState === 1)) {
|
||||||
(request.message.command === 'subscribe' && self._ws.readyState === 1)) {
|
|
||||||
if (self._remote.trace) {
|
if (self._remote.trace) {
|
||||||
utils.logObject('server: request: %s', request.message);
|
utils.logObject('server: request: %s', request.message);
|
||||||
}
|
}
|
||||||
|
|
||||||
self._ws.send(JSON.stringify(request.message));
|
self.send_message(request.message);
|
||||||
} else {
|
} else {
|
||||||
// XXX There are many ways to make self smarter.
|
// XXX There are many ways to make self smarter.
|
||||||
self.once('connect', function () {
|
self.once('connect', function () {
|
||||||
if (self._remote.trace) {
|
if (self._remote.trace) {
|
||||||
utils.logObject('server: request: %s', request.message);
|
utils.logObject('server: request: %s', request.message);
|
||||||
}
|
}
|
||||||
self._ws.send(JSON.stringify(request.message));
|
self.send_message(request.message);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -206,6 +206,8 @@ Transaction.prototype.complete = function () {
|
|||||||
var key = seed.get_key(this.tx_json.Account);
|
var key = seed.get_key(this.tx_json.Account);
|
||||||
tx_json.SigningPubKey = key.to_hex_pub();
|
tx_json.SigningPubKey = key.to_hex_pub();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return this.tx_json;
|
||||||
};
|
};
|
||||||
|
|
||||||
Transaction.prototype.serialize = function () {
|
Transaction.prototype.serialize = function () {
|
||||||
@@ -250,14 +252,22 @@ Transaction.prototype._hasTransactionListeners = function() {
|
|||||||
// case 'tejLost': locally gave up looking
|
// case 'tejLost': locally gave up looking
|
||||||
// default: some other TER
|
// default: some other TER
|
||||||
// }
|
// }
|
||||||
|
|
||||||
Transaction.prototype.submit = function (callback) {
|
Transaction.prototype.submit = function (callback) {
|
||||||
var self = this;
|
var self = this;
|
||||||
var tx_json = this.tx_json;
|
var tx_json = this.tx_json;
|
||||||
|
|
||||||
this.callback = callback;
|
this.callback = typeof callback === 'function'
|
||||||
|
? callback
|
||||||
|
: function(){};
|
||||||
|
|
||||||
|
function finish(err) {
|
||||||
|
self.emit('error', err);
|
||||||
|
self.callback('error', err);
|
||||||
|
}
|
||||||
|
|
||||||
if (typeof tx_json.Account !== 'string') {
|
if (typeof tx_json.Account !== 'string') {
|
||||||
(this.callback || this.emit)('error', {
|
finish({
|
||||||
'error' : 'tejInvalidAccount',
|
'error' : 'tejInvalidAccount',
|
||||||
'error_message' : 'Bad account.'
|
'error_message' : 'Bad account.'
|
||||||
});
|
});
|
||||||
@@ -268,72 +278,72 @@ Transaction.prototype.submit = function (callback) {
|
|||||||
|
|
||||||
this.complete();
|
this.complete();
|
||||||
|
|
||||||
if (this.callback || this._hasTransactionListeners()) {
|
//console.log('Callback or has listeners');
|
||||||
// There are listeners for callback, 'final', 'lost', or 'pending' arrange to emit them.
|
|
||||||
|
|
||||||
this.submit_index = this.remote._ledger_current_index;
|
// There are listeners for callback, 'final', 'lost', or 'pending' arrange to emit them.
|
||||||
|
|
||||||
// When a ledger closes, look for the result.
|
this.submit_index = this.remote._ledger_current_index;
|
||||||
function on_ledger_closed(message) {
|
|
||||||
var ledger_hash = message.ledger_hash;
|
|
||||||
var ledger_index = message.ledger_index;
|
|
||||||
var stop = false;
|
|
||||||
|
|
||||||
// XXX make sure self.hash is available.
|
// When a ledger closes, look for the result.
|
||||||
var transaction_entry = self.remote.request_transaction_entry(self.hash)
|
function on_ledger_closed(message) {
|
||||||
transaction_entry.ledger_hash(ledger_hash)
|
var ledger_hash = message.ledger_hash;
|
||||||
transaction_entry.on('success', function (message) {
|
var ledger_index = message.ledger_index;
|
||||||
if (self.finalized) return;
|
var stop = false;
|
||||||
self.set_state(message.metadata.TransactionResult);
|
|
||||||
self.remote.removeListener('ledger_closed', on_ledger_closed);
|
// XXX make sure self.hash is available.
|
||||||
self.emit('final', message);
|
var transaction_entry = self.remote.request_transaction_entry(self.hash)
|
||||||
self.finalized = true;
|
|
||||||
if (self.callback) {
|
transaction_entry.ledger_hash(ledger_hash)
|
||||||
self.callback(message.metadata.TransactionResult, message);
|
|
||||||
|
transaction_entry.on('success', function (message) {
|
||||||
|
if (self.finalized) return;
|
||||||
|
self.set_state(message.metadata.TransactionResult);
|
||||||
|
self.remote.removeListener('ledger_closed', on_ledger_closed);
|
||||||
|
self.emit('final', message);
|
||||||
|
self.finalized = true;
|
||||||
|
self.callback(message.metadata.TransactionResult, message);
|
||||||
|
});
|
||||||
|
|
||||||
|
transaction_entry.on('error', function (message) {
|
||||||
|
if (self.finalized) return;
|
||||||
|
|
||||||
|
if (message.error === 'remoteError' && message.remote.error === 'transactionNotFound') {
|
||||||
|
if (self.submit_index + SUBMIT_LOST < ledger_index) {
|
||||||
|
self.set_state('client_lost'); // Gave up.
|
||||||
|
self.emit('lost');
|
||||||
|
self.callback('tejLost', message);
|
||||||
|
self.remote.removeListener('ledger_closed', on_ledger_closed);
|
||||||
|
self.emit('final', message);
|
||||||
|
self.finalized = true;
|
||||||
|
} else if (self.submit_index + SUBMIT_MISSING < ledger_index) {
|
||||||
|
self.set_state('client_missing'); // We don't know what happened to transaction, still might find.
|
||||||
|
self.emit('pending');
|
||||||
|
} else {
|
||||||
|
self.emit('pending');
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
transaction_entry.on('error', function (message) {
|
// XXX Could log other unexpectedness.
|
||||||
if (self.finalized) return;
|
});
|
||||||
|
|
||||||
if (message.error === 'remoteError' && message.remote.error === 'transactionNotFound') {
|
transaction_entry.request();
|
||||||
if (self.submit_index + SUBMIT_LOST < ledger_index) {
|
};
|
||||||
self.set_state('client_lost'); // Gave up.
|
|
||||||
self.emit('lost');
|
|
||||||
if (self.callback) {
|
|
||||||
self.callback('tejLost', message);
|
|
||||||
}
|
|
||||||
self.remote.removeListener('ledger_closed', on_ledger_closed);
|
|
||||||
self.emit('final', message);
|
|
||||||
self.finalized = true;
|
|
||||||
} else if (self.submit_index + SUBMIT_MISSING < ledger_index) {
|
|
||||||
self.set_state('client_missing'); // We don't know what happened to transaction, still might find.
|
|
||||||
self.emit('pending');
|
|
||||||
} else {
|
|
||||||
self.emit('pending');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// XXX Could log other unexpectedness.
|
|
||||||
});
|
|
||||||
|
|
||||||
transaction_entry.request();
|
this.remote.on('ledger_closed', on_ledger_closed);
|
||||||
};
|
|
||||||
|
|
||||||
this.remote.on('ledger_closed', on_ledger_closed);
|
this.once('error', function (message) {
|
||||||
|
self.callback(message.error, message);
|
||||||
if (this.callback) {
|
});
|
||||||
this.once('error', function (message) {
|
|
||||||
self.callback(message.error, message);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this.set_state('client_submitted');
|
this.set_state('client_submitted');
|
||||||
|
|
||||||
if (self.remote.local_sequence && !self.tx_json.Sequence) {
|
if (self.remote.local_sequence && !self.tx_json.Sequence) {
|
||||||
self.tx_json.Sequence = this.remote.account_seq(self.tx_json.Account, 'ADVANCE');
|
|
||||||
|
self.tx_json.Sequence = this.remote.account_seq(self.tx_json.Account, 'ADVANCE');
|
||||||
// console.log("Sequence: %s", self.tx_json.Sequence);
|
// console.log("Sequence: %s", self.tx_json.Sequence);
|
||||||
|
|
||||||
if (!self.tx_json.Sequence) {
|
if (!self.tx_json.Sequence) {
|
||||||
|
//console.log('NO SEQUENCE');
|
||||||
|
|
||||||
// Look in the last closed ledger.
|
// Look in the last closed ledger.
|
||||||
var account_seq = this.remote.account_seq_cache(self.tx_json.Account, false)
|
var account_seq = this.remote.account_seq_cache(self.tx_json.Account, false)
|
||||||
|
|
||||||
@@ -364,8 +374,8 @@ Transaction.prototype.submit = function (callback) {
|
|||||||
|
|
||||||
// If the transaction fails we want to either undo incrementing the sequence
|
// If the transaction fails we want to either undo incrementing the sequence
|
||||||
// or submit a noop transaction to consume the sequence remotely.
|
// or submit a noop transaction to consume the sequence remotely.
|
||||||
this.on('success', function (res) {
|
this.once('success', function (res) {
|
||||||
if (res && typeof res.engine_result === 'string') {
|
if (typeof res.engine_result === 'string') {
|
||||||
switch (res.engine_result.slice(0, 3)) {
|
switch (res.engine_result.slice(0, 3)) {
|
||||||
// Synchronous local error
|
// Synchronous local error
|
||||||
case 'tej':
|
case 'tej':
|
||||||
@@ -401,7 +411,7 @@ Transaction.prototype.submit = function (callback) {
|
|||||||
request.emit = this.emit.bind(this);
|
request.emit = this.emit.bind(this);
|
||||||
|
|
||||||
if (!this._secret && !this.tx_json.Signature) {
|
if (!this._secret && !this.tx_json.Signature) {
|
||||||
this.emit('error', {
|
finish({
|
||||||
'result' : 'tejSecretUnknown',
|
'result' : 'tejSecretUnknown',
|
||||||
'result_message' : "Could not sign transactions because we."
|
'result_message' : "Could not sign transactions because we."
|
||||||
});
|
});
|
||||||
@@ -411,11 +421,10 @@ Transaction.prototype.submit = function (callback) {
|
|||||||
request.tx_blob(this.serialize().to_hex());
|
request.tx_blob(this.serialize().to_hex());
|
||||||
} else {
|
} else {
|
||||||
if (!this.remote.trusted) {
|
if (!this.remote.trusted) {
|
||||||
this.emit('error', {
|
finish({
|
||||||
'result' : 'tejServerUntrusted',
|
'result' : 'tejServerUntrusted',
|
||||||
'result_message' : "Attempt to give a secret to an untrusted server."
|
'result_message' : "Attempt to give a secret to an untrusted server."
|
||||||
});
|
});
|
||||||
return this;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
request.secret(this._secret);
|
request.secret(this._secret);
|
||||||
|
|||||||
Reference in New Issue
Block a user