mirror of
https://github.com/Xahau/xahau.js.git
synced 2026-08-23 00:20:52 +00:00
Merge branch 'develop'
This commit is contained in:
15
package.json
15
package.json
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "ripple-lib",
|
||||
"version": "0.7.14",
|
||||
"version": "0.7.15",
|
||||
"description": "Ripple JavaScript client library",
|
||||
"files": [
|
||||
"src/js/ripple/*.js",
|
||||
@@ -12,10 +12,10 @@
|
||||
"test": "test"
|
||||
},
|
||||
"dependencies": {
|
||||
"async": "~0.1.22",
|
||||
"ws": "~0.4.22",
|
||||
"extend": "~1.1.1",
|
||||
"simple-jsonrpc": "~0.0.1"
|
||||
"async": "~0.2.9",
|
||||
"ws": "~0.4.25",
|
||||
"extend": "~1.1.3",
|
||||
"simple-jsonrpc": "~0.0.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"grunt": "~0.4.1",
|
||||
@@ -32,5 +32,8 @@
|
||||
"type": "git",
|
||||
"url": "git://github.com/rippleFoundation/ripple-lib.git"
|
||||
},
|
||||
"readmeFilename": "README.md"
|
||||
"readmeFilename": "README.md",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -64,6 +64,8 @@ Base.encode = function (input, alpha) {
|
||||
// --> input: String
|
||||
// <-- array of bytes or undefined.
|
||||
Base.decode = function (input, alpha) {
|
||||
if ("string" !== typeof input) return undefined;
|
||||
|
||||
var alphabet = alphabets[alpha || 'ripple'];
|
||||
var bi_base = new BigInteger(String(alphabet.length));
|
||||
var bi_value = nbi();
|
||||
@@ -105,6 +107,17 @@ Base.decode = function (input, alpha) {
|
||||
return [].concat(utils.arraySet(zeros, 0), bytes);
|
||||
};
|
||||
|
||||
Base.verify_checksum = function (bytes) {
|
||||
var computed = sha256hash(bytes.slice(0, -4)).slice(0, 4);
|
||||
var checksum = bytes.slice(-4);
|
||||
|
||||
for (var i = 0; i < 4; i++)
|
||||
if (computed[i] !== checksum[i])
|
||||
return false;
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
// --> input: Array
|
||||
// <-- String
|
||||
Base.encode_check = function (version, input, alphabet) {
|
||||
@@ -119,16 +132,24 @@ Base.encode_check = function (version, input, alphabet) {
|
||||
Base.decode_check = function (version, input, alphabet) {
|
||||
var buffer = Base.decode(input, alphabet);
|
||||
|
||||
if (!buffer || buffer[0] !== version || buffer.length < 5)
|
||||
if (!buffer || buffer.length < 5)
|
||||
return NaN;
|
||||
|
||||
var computed = sha256hash(buffer.slice(0, -4)).slice(0, 4);
|
||||
var checksum = buffer.slice(-4);
|
||||
var i;
|
||||
// Single valid version
|
||||
if ("number" === typeof version && buffer[0] !== version)
|
||||
return NaN;
|
||||
|
||||
for (i = 0; i != 4; i += 1)
|
||||
if (computed[i] !== checksum[i])
|
||||
return NaN;
|
||||
// Multiple allowed versions
|
||||
if ("object" === typeof version && Array.isArray(version)) {
|
||||
var match = false;
|
||||
for (var i = 0, l = version.length; i < l; i++) {
|
||||
match |= version[i] === buffer[0];
|
||||
}
|
||||
if (!match) return NaN;
|
||||
}
|
||||
|
||||
if (!Base.verify_checksum(buffer))
|
||||
return NaN;
|
||||
|
||||
// We'll use the version byte to add a leading zero, this ensures JSBN doesn't
|
||||
// intrepret the value as a negative number
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
exports.Remote = require('./remote').Remote;
|
||||
exports.Amount = require('./amount').Amount;
|
||||
exports.Currency = require('./currency').Currency;
|
||||
exports.Base = require('./base').Base;
|
||||
exports.UInt160 = require('./amount').UInt160;
|
||||
exports.Seed = require('./amount').Seed;
|
||||
exports.Transaction = require('./transaction').Transaction;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,47 +1,50 @@
|
||||
var EventEmitter = require('events').EventEmitter;
|
||||
var util = require('util');
|
||||
var EventEmitter = require('events').EventEmitter;
|
||||
var util = require('util');
|
||||
var WebSocket = require('ws');
|
||||
var utils = require('./utils');
|
||||
|
||||
var utils = require('./utils');
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
/**
|
||||
Constructor
|
||||
* Server
|
||||
*
|
||||
* Options must contain `url` to
|
||||
* WebSocket server
|
||||
*
|
||||
* @constructor
|
||||
* @param {Object} remote
|
||||
* @param {Object} opts
|
||||
*/
|
||||
|
||||
Keys for cfg:
|
||||
|
||||
url
|
||||
|
||||
@param remote The Remote object
|
||||
@param cfg Configuration parameters.
|
||||
*/
|
||||
|
||||
var Server = function (remote, cfg)
|
||||
{
|
||||
function Server(remote, opts) {
|
||||
EventEmitter.call(this);
|
||||
|
||||
if ("object" !== typeof cfg || "string" !== typeof cfg.url) {
|
||||
throw new Error("Invalid server configuration.");
|
||||
if (typeof opts !== 'object' || typeof opts.url !== 'string') {
|
||||
throw new Error('Invalid server configuration.');
|
||||
}
|
||||
|
||||
this._remote = remote;
|
||||
this._cfg = cfg;
|
||||
var self = this;
|
||||
|
||||
this._ws = null;
|
||||
this._connected = false;
|
||||
this._remote = remote;
|
||||
this._opts = opts;
|
||||
|
||||
this._ws = void(0);
|
||||
this._connected = false;
|
||||
this._should_connect = false;
|
||||
this._state = null;
|
||||
this._state = void(0);
|
||||
|
||||
this._id = 0;
|
||||
this._retry = 0;
|
||||
this._id = 0;
|
||||
this._retry = 0;
|
||||
|
||||
this._requests = {};
|
||||
this._requests = { };
|
||||
|
||||
this.on('message', this._handle_message.bind(this));
|
||||
this.on('response_subscribe', this._handle_response_subscribe.bind(this));
|
||||
this.on('message', function(message) {
|
||||
self._handle_message(message);
|
||||
});
|
||||
|
||||
this.on('response_subscribe', function(message) {
|
||||
self._handle_response_subscribe(message);
|
||||
});
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
util.inherits(Server, EventEmitter);
|
||||
|
||||
/**
|
||||
@@ -50,16 +53,33 @@ util.inherits(Server, EventEmitter);
|
||||
* Our requirements are that the server can process transactions and notify
|
||||
* us of changes.
|
||||
*/
|
||||
Server.online_states = [
|
||||
'syncing',
|
||||
'tracking',
|
||||
'proposing',
|
||||
'validating',
|
||||
'full'
|
||||
|
||||
Server.online_states = [
|
||||
'syncing'
|
||||
, 'tracking'
|
||||
, 'proposing'
|
||||
, 'validating'
|
||||
, 'full'
|
||||
];
|
||||
|
||||
Server.prototype.connect = function ()
|
||||
{
|
||||
/**
|
||||
* Determine if a server status qualifies
|
||||
* as 'online'
|
||||
*
|
||||
* @param {String} status
|
||||
* @return {Boolean}
|
||||
* @api private
|
||||
*/
|
||||
|
||||
Server.prototype.is_online = function(status) {
|
||||
return Server.online_states.indexOf(status) !== -1;
|
||||
};
|
||||
|
||||
/**
|
||||
* Connect to WebSocket server
|
||||
*/
|
||||
|
||||
Server.prototype.connect = function() {
|
||||
var self = this;
|
||||
|
||||
// We don't connect if we believe we're already connected. This means we have
|
||||
@@ -68,19 +88,22 @@ Server.prototype.connect = function ()
|
||||
// we will automatically reconnect.
|
||||
if (this._connected === true) return;
|
||||
|
||||
if (this._remote.trace) console.log("server: connect: %s", this._cfg.url);
|
||||
if (this._remote.trace) {
|
||||
console.log('server: connect: %s', this._opts.url);
|
||||
}
|
||||
|
||||
// Ensure any existing socket is given the command to close first.
|
||||
if (this._ws) this._ws.close();
|
||||
if (this._ws) {
|
||||
this._ws.close();
|
||||
}
|
||||
|
||||
var WebSocket = require('ws');
|
||||
var ws = this._ws = new WebSocket(this._cfg.url);
|
||||
var ws = this._ws = new WebSocket(this._opts.url);
|
||||
|
||||
this._should_connect = true;
|
||||
|
||||
self.emit('connecting');
|
||||
|
||||
ws.onopen = function () {
|
||||
ws.onopen = function() {
|
||||
// If we are no longer the active socket, simply ignore any event
|
||||
if (ws !== self._ws) return;
|
||||
|
||||
@@ -91,13 +114,15 @@ Server.prototype.connect = function ()
|
||||
self.request(request);
|
||||
};
|
||||
|
||||
ws.onerror = function (e) {
|
||||
ws.onerror = function(e) {
|
||||
// If we are no longer the active socket, simply ignore any event
|
||||
if (ws !== self._ws) return;
|
||||
|
||||
if (self._remote.trace) console.log("server: onerror: %s", e.data || e);
|
||||
if (self._remote.trace) {
|
||||
console.log('server: onerror: %s', e.data || e);
|
||||
}
|
||||
|
||||
// Most connection errors for WebSockets are conveyed as "close" events with
|
||||
// Most connection errors for WebSockets are conveyed as 'close' events with
|
||||
// code 1006. This is done for security purposes and therefore unlikely to
|
||||
// ever change.
|
||||
|
||||
@@ -115,31 +140,33 @@ Server.prototype.connect = function ()
|
||||
};
|
||||
|
||||
// Failure to open.
|
||||
ws.onclose = function () {
|
||||
ws.onclose = function() {
|
||||
// If we are no longer the active socket, simply ignore any event
|
||||
if (ws !== self._ws) return;
|
||||
|
||||
if (self._remote.trace) console.log("server: onclose: %s", ws.readyState);
|
||||
if (self._remote.trace) {
|
||||
console.log('server: onclose: %s', ws.readyState);
|
||||
}
|
||||
|
||||
handleConnectionClose();
|
||||
};
|
||||
|
||||
function handleConnectionClose()
|
||||
{
|
||||
function handleConnectionClose() {
|
||||
self.emit('socket_close');
|
||||
self._set_state('offline');
|
||||
|
||||
// Prevent additional events from this socket
|
||||
ws.onopen = ws.onerror = ws.onclose = ws.onmessage = function () {};
|
||||
ws.removeAllListeners();
|
||||
ws.on('error', function() {});
|
||||
|
||||
// Should we be connected?
|
||||
if (!self._should_connect) return;
|
||||
|
||||
// Delay and retry.
|
||||
self._retry += 1;
|
||||
self._retry_timer = setTimeout(function () {
|
||||
if (self._remote.trace) console.log("server: retry");
|
||||
self._retry += 1;
|
||||
|
||||
self._retry_timer = setTimeout(function retryTimeout() {
|
||||
if (self._remote.trace) console.log('server: retry');
|
||||
if (!self._should_connect) return;
|
||||
self.connect();
|
||||
}, self._retry < 40
|
||||
@@ -149,28 +176,48 @@ Server.prototype.connect = function ()
|
||||
: self._retry < 40+60+60
|
||||
? 10*1000 // Then, for 10 minutes: once every 10 seconds
|
||||
: 30*1000); // Then: once every 30 seconds
|
||||
}
|
||||
};
|
||||
|
||||
ws.onmessage = function (msg) {
|
||||
ws.onmessage = function(msg) {
|
||||
self.emit('message', msg.data);
|
||||
};
|
||||
};
|
||||
|
||||
Server.prototype.disconnect = function ()
|
||||
{
|
||||
/**
|
||||
* Disconnect from WebSocket server
|
||||
*
|
||||
* @api public
|
||||
*/
|
||||
|
||||
Server.prototype.disconnect = function() {
|
||||
this._should_connect = false;
|
||||
this._set_state('offline');
|
||||
if (this._ws) {
|
||||
this._ws.close();
|
||||
}
|
||||
};
|
||||
|
||||
if (this.ws) {
|
||||
this.ws.close();
|
||||
/**
|
||||
* Send stringified message to WebSocket server
|
||||
*
|
||||
* @param {Object} message
|
||||
* @api private
|
||||
*/
|
||||
|
||||
Server.prototype.send = function(message) {
|
||||
if (this._ws) {
|
||||
this._ws.send(JSON.stringify(message));
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Submit a Request object to this server.
|
||||
*
|
||||
* @param {Object} request
|
||||
* @api public
|
||||
*/
|
||||
Server.prototype.request = function (request)
|
||||
{
|
||||
|
||||
Server.prototype.request = function(request) {
|
||||
var self = this;
|
||||
|
||||
// Only bother if we are still connected.
|
||||
@@ -182,86 +229,133 @@ Server.prototype.request = function (request)
|
||||
// Advance message ID
|
||||
self._id++;
|
||||
|
||||
if (self._state === "online" ||
|
||||
(request.message.command === "subscribe" && self._ws.readyState === 1)) {
|
||||
if (self._connected || (request.message.command === 'subscribe'
|
||||
&& self._ws.readyState === 1)) {
|
||||
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(request.message);
|
||||
} else {
|
||||
// XXX There are many ways to make self smarter.
|
||||
self.once('connect', function () {
|
||||
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(request.message);
|
||||
});
|
||||
}
|
||||
} else {
|
||||
if (self._remote.trace) {
|
||||
utils.logObject("server: request: DROPPING: %s", request.message);
|
||||
utils.logObject('server: request: DROPPING: %s', request.message);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Server.prototype._set_state = function (state) {
|
||||
/**
|
||||
* Set server state
|
||||
*
|
||||
* Examples:
|
||||
*
|
||||
* set_state('online')
|
||||
* set_state('offline')
|
||||
*
|
||||
* @param {String} state
|
||||
* @api private
|
||||
*/
|
||||
|
||||
Server.prototype._set_state = function(state) {
|
||||
if (state !== this._state) {
|
||||
this._state = state;
|
||||
|
||||
this.emit('state', state);
|
||||
|
||||
if (state === 'online') {
|
||||
this._connected = true;
|
||||
this.emit('connect');
|
||||
} else if (state === 'offline') {
|
||||
this._connected = false;
|
||||
this.emit('disconnect');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Server.prototype._handle_message = function (json) {
|
||||
var self = this;
|
||||
/**
|
||||
* Handle WebSocket message
|
||||
*
|
||||
* @param {String} json
|
||||
* @api private
|
||||
*/
|
||||
|
||||
var message = JSON.parse(json);
|
||||
Server.prototype._handle_message = function(json) {
|
||||
var self = this;
|
||||
var unexpected = false;
|
||||
var message;
|
||||
|
||||
try { message = JSON.parse(json); } catch(exception) { }
|
||||
|
||||
if (message.type === 'response') {
|
||||
// A response to a request.
|
||||
var request = self._requests[message.id];
|
||||
var unexpected = typeof message !== 'object' || typeof message.type !== 'string';
|
||||
|
||||
delete self._requests[message.id];
|
||||
if (unexpected) {
|
||||
// We received a malformed response from the server
|
||||
}
|
||||
|
||||
if (!request) {
|
||||
if (self._remote.trace) utils.logObject("server: UNEXPECTED: %s", message);
|
||||
} else if ('success' === message.status) {
|
||||
if (self._remote.trace) utils.logObject("server: response: %s", message);
|
||||
if (!unexpected) {
|
||||
switch (message.type) {
|
||||
case 'response':
|
||||
// A response to a request.
|
||||
var request = self._requests[message.id];
|
||||
|
||||
request.emit('success', message.result);
|
||||
self.emit('response_'+request.message.command, message.result, request, message);
|
||||
self._remote.emit('response_'+request.message.command, message.result, request, message);
|
||||
} else if (message.error) {
|
||||
if (self._remote.trace) utils.logObject("server: error: %s", message);
|
||||
delete self._requests[message.id];
|
||||
|
||||
request.emit('error', {
|
||||
'error' : 'remoteError',
|
||||
'error_message' : 'Remote reported an error.',
|
||||
'remote' : message
|
||||
});
|
||||
if (!request) {
|
||||
if (self._remote.trace) {
|
||||
utils.logObject('server: UNEXPECTED: %s', message);
|
||||
}
|
||||
} else if (message.status === 'success') {
|
||||
if (self._remote.trace) {
|
||||
utils.logObject('server: response: %s', message);
|
||||
}
|
||||
|
||||
request.emit('success', message.result);
|
||||
|
||||
[ self, self._remote ].forEach(function(emitter) {
|
||||
emitter.emit('response_' + request.message.command, message.result, request, message);
|
||||
});
|
||||
} else if (message.error) {
|
||||
if (self._remote.trace) {
|
||||
utils.logObject('server: error: %s', message);
|
||||
}
|
||||
|
||||
request.emit('error', {
|
||||
'error' : 'remoteError',
|
||||
'error_message' : 'Remote reported an error.',
|
||||
'remote' : message
|
||||
});
|
||||
}
|
||||
break;
|
||||
|
||||
case 'serverStatus':
|
||||
// This message is only received when online.
|
||||
// As we are connected, it is the definitive final state.
|
||||
self._set_state(self.is_online(message.server_status) ? 'online' : 'offline');
|
||||
break;
|
||||
}
|
||||
} else if (message.type === 'serverStatus') {
|
||||
// This message is only received when online. As we are connected, it is the definative final state.
|
||||
self._set_state(
|
||||
Server.online_states.indexOf(message.server_status) !== -1
|
||||
? 'online'
|
||||
: 'offline');
|
||||
}
|
||||
};
|
||||
|
||||
Server.prototype._handle_response_subscribe = function (message)
|
||||
{
|
||||
/**
|
||||
* Handle subscribe response
|
||||
*
|
||||
* @param {Object} message
|
||||
* @api private
|
||||
*/
|
||||
|
||||
Server.prototype._handle_response_subscribe = function(message) {
|
||||
var self = this;
|
||||
|
||||
self._server_status = message.server_status;
|
||||
|
||||
if (Server.online_states.indexOf(message.server_status) !== -1) {
|
||||
if (self.is_online(message.server_status)) {
|
||||
self._set_state('online');
|
||||
}
|
||||
};
|
||||
|
||||
@@ -588,8 +588,9 @@ Transaction.prototype.offer_cancel = function (src, sequence) {
|
||||
|
||||
// Options:
|
||||
// .set_flags()
|
||||
// --> expiration : Date or Number
|
||||
Transaction.prototype.offer_create = function (src, taker_pays, taker_gets, expiration) {
|
||||
// --> expiration : if not undefined, Date or Number
|
||||
// --> cancel_sequence : if not undefined, Sequence
|
||||
Transaction.prototype.offer_create = function (src, taker_pays, taker_gets, expiration, cancel_sequence) {
|
||||
this._secret = this._account_secret(src);
|
||||
this.tx_json.TransactionType = 'OfferCreate';
|
||||
this.tx_json.Account = UInt160.json_rewrite(src);
|
||||
@@ -605,6 +606,9 @@ Transaction.prototype.offer_create = function (src, taker_pays, taker_gets, expi
|
||||
? expiration.getTime()
|
||||
: Number(expiration);
|
||||
|
||||
if (cancel_sequence)
|
||||
this.tx_json.OfferSequence = Number(cancel_sequence);
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
|
||||
@@ -7,7 +7,13 @@ var nbi = jsbn.nbi;
|
||||
var Amount = require("../src/js/ripple/amount").Amount;
|
||||
var UInt160 = require("../src/js/ripple/uint160").UInt160;
|
||||
|
||||
var config = require('../src/js/ripple/config').load(require('./config'));
|
||||
try {
|
||||
var conf = require('./config');
|
||||
} catch(exception) {
|
||||
var conf = require('./config-example');
|
||||
}
|
||||
|
||||
var config = require('../src/js/ripple/config').load(conf);
|
||||
|
||||
// XXX Add test cases that push XRP vs non-XRP behavior.
|
||||
|
||||
|
||||
@@ -2,7 +2,13 @@ var buster = require("buster");
|
||||
|
||||
var Seed = require("../src/js/ripple/seed").Seed;
|
||||
|
||||
var config = require('../src/js/ripple/config').load(require('./config'));
|
||||
try {
|
||||
var conf = require('./config');
|
||||
} catch(exception) {
|
||||
var conf = require('./config-example');
|
||||
}
|
||||
|
||||
var config = require('../src/js/ripple/config').load(conf);
|
||||
|
||||
buster.testCase("Base58", {
|
||||
"Seed" : {
|
||||
|
||||
@@ -2,7 +2,13 @@ var buster = require("buster");
|
||||
|
||||
var Seed = require("../src/js/ripple/seed").Seed;
|
||||
|
||||
var config = require('../src/js/ripple/config').load(require('./config'));
|
||||
try {
|
||||
var conf = require('./config');
|
||||
} catch(exception) {
|
||||
var conf = require('./config-example');
|
||||
}
|
||||
|
||||
var config = require('../src/js/ripple/config').load(conf);
|
||||
|
||||
buster.testCase("Signing", {
|
||||
"Keys" : {
|
||||
|
||||
Reference in New Issue
Block a user