mirror of
https://github.com/XRPLF/rippled.git
synced 2025-12-06 17:27:55 +00:00
Merge branch 'master' of github.com:jedmccaleb/NewCoin
This commit is contained in:
@@ -9,15 +9,121 @@
|
||||
|
||||
// var network = require("./network.js");
|
||||
|
||||
var EventEmitter = require('events').EventEmitter;
|
||||
var Amount = require('./amount.js').Amount;
|
||||
var UInt160 = require('./amount.js').UInt160;
|
||||
var EventEmitter = require('events').EventEmitter;
|
||||
var Amount = require('./amount').Amount;
|
||||
var UInt160 = require('./uint160').UInt160;
|
||||
|
||||
var Account = function (network, account) {
|
||||
this._network = network;
|
||||
this._account = UInt160.json_rewrite(account);
|
||||
var extend = require('extend');
|
||||
|
||||
return this;
|
||||
var Account = function (remote, account) {
|
||||
var self = this;
|
||||
|
||||
this._remote = remote;
|
||||
this._account = UInt160.from_json(account);
|
||||
this._account_id = this._account.to_json();
|
||||
this._subs = 0;
|
||||
|
||||
// Ledger entry object
|
||||
// Important: This must never be overwritten, only extend()-ed
|
||||
this._entry = {};
|
||||
|
||||
this.on('newListener', function (type, listener) {
|
||||
if (Account.subscribe_events.indexOf(type) !== -1) {
|
||||
if (!self._subs && 'open' === self._remote._online_state) {
|
||||
self._remote.request_subscribe()
|
||||
.accounts(self._account_id)
|
||||
.request();
|
||||
}
|
||||
self._subs += 1;
|
||||
}
|
||||
});
|
||||
|
||||
this.on('removeListener', function (type, listener) {
|
||||
if (Account.subscribe_events.indexOf(type) !== -1) {
|
||||
self._subs -= 1;
|
||||
|
||||
if (!self._subs && 'open' === self._remote._online_state) {
|
||||
self._remote.request_unsubscribe()
|
||||
.accounts(self._account_id)
|
||||
.request();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
this._remote.on('connect', function () {
|
||||
if (self._subs) {
|
||||
self._remote.request_subscribe()
|
||||
.accounts(self._account_id)
|
||||
.request();
|
||||
}
|
||||
});
|
||||
|
||||
this.on('transaction', function (msg) {
|
||||
var changed = false;
|
||||
msg.mmeta.each(function (an) {
|
||||
if (an.entryType === 'AccountRoot' &&
|
||||
an.fields.Account === self._account_id) {
|
||||
extend(self._entry, an.fieldsNew, an.fieldsFinal);
|
||||
changed = true;
|
||||
}
|
||||
});
|
||||
if (changed) {
|
||||
self.emit('entry', self._entry);
|
||||
}
|
||||
});
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
Account.prototype = new EventEmitter;
|
||||
|
||||
/**
|
||||
* List of events that require a remote subscription to the account.
|
||||
*/
|
||||
Account.subscribe_events = ['transaction', 'entry'];
|
||||
|
||||
Account.prototype.to_json = function ()
|
||||
{
|
||||
return this._account.to_json();
|
||||
};
|
||||
|
||||
/**
|
||||
* Whether the AccountId is valid.
|
||||
*
|
||||
* Note: This does not tell you whether the account exists in the ledger.
|
||||
*/
|
||||
Account.prototype.is_valid = function ()
|
||||
{
|
||||
return this._account.is_valid();
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieve the current AccountRoot entry.
|
||||
*
|
||||
* To keep up-to-date with changes to the AccountRoot entry, subscribe to the
|
||||
* "entry" event.
|
||||
*
|
||||
* @param {function (err, entry)} callback Called with the result
|
||||
*/
|
||||
Account.prototype.entry = function (callback)
|
||||
{
|
||||
var self = this;
|
||||
|
||||
self._remote.request_account_info(this._account_id)
|
||||
.on('success', function (e) {
|
||||
extend(self._entry, e.account_data);
|
||||
self.emit('entry', self._entry);
|
||||
|
||||
if ("function" === typeof callback) {
|
||||
callback(null, e);
|
||||
}
|
||||
})
|
||||
.on('error', function (e) {
|
||||
callback(e);
|
||||
})
|
||||
.request();
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
exports.Account = Account;
|
||||
|
||||
201
src/js/amount.js
201
src/js/amount.js
@@ -111,7 +111,17 @@ Amount.prototype.add = function (v) {
|
||||
result._is_negative = s.compareTo(BigInteger.ZERO) < 0;
|
||||
result._value = result._is_negative ? s.negate() : s;
|
||||
}
|
||||
else {
|
||||
else if (v.is_zero()) {
|
||||
result = this;
|
||||
}
|
||||
else if (this.is_zero()) {
|
||||
result = v.clone();
|
||||
// YYY Why are these cloned? We never modify them.
|
||||
result._currency = this._currency;
|
||||
result._issuer = this._issuer;
|
||||
}
|
||||
else
|
||||
{
|
||||
var v1 = this._is_negative ? this._value.negate() : this._value;
|
||||
var o1 = this._offset;
|
||||
var v2 = v._is_negative ? v._value.negate() : v._value;
|
||||
@@ -137,8 +147,8 @@ Amount.prototype.add = function (v) {
|
||||
result._value = result._value.negate();
|
||||
}
|
||||
|
||||
result._currency = this._currency.clone();
|
||||
result._issuer = this._issuer.clone();
|
||||
result._currency = this._currency;
|
||||
result._issuer = this._issuer;
|
||||
|
||||
result.canonicalize();
|
||||
}
|
||||
@@ -266,6 +276,9 @@ Amount.prototype.copyTo = function (d, negate) {
|
||||
this._currency.copyTo(d._currency);
|
||||
this._issuer.copyTo(d._issuer);
|
||||
|
||||
// Prevent negative zero
|
||||
if (d.is_zero()) d._is_negative = false;
|
||||
|
||||
return d;
|
||||
};
|
||||
|
||||
@@ -273,21 +286,29 @@ Amount.prototype.currency = function () {
|
||||
return this._currency;
|
||||
};
|
||||
|
||||
// Check BigInteger NaN
|
||||
// Checks currency, does not check issuer.
|
||||
Amount.prototype.equals = function (d) {
|
||||
return 'string' === typeof (d)
|
||||
? this.equals(Amount.from_json(d))
|
||||
: this === d
|
||||
|| (d instanceof Amount
|
||||
&& this._is_native === d._is_native
|
||||
&& (this._is_native
|
||||
? this._value.equals(d._value)
|
||||
: this._currency.equals(d._currency)
|
||||
? this._is_negative === d._is_negative
|
||||
? this._value.equals(d._value)
|
||||
: this._value.equals(BigInteger.ZERO) && d._value.equals(BigInteger.ZERO)
|
||||
: false));
|
||||
Amount.prototype.equals = function (d, ignore_issuer) {
|
||||
if ("string" === typeof d) {
|
||||
return this.equals(Amount.from_json(d));
|
||||
}
|
||||
|
||||
if (this === d) return true;
|
||||
|
||||
if (d instanceof Amount) {
|
||||
if (!this.is_valid() || !d.is_valid()) return false;
|
||||
if (this._is_native !== d._is_native) return false;
|
||||
|
||||
if (!this._value.equals(d._value) || this._offset !== d._offset) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this._is_negative !== d._is_negative) return false;
|
||||
|
||||
if (!this._is_native) {
|
||||
if (!this._currency.equals(d._currency)) return false;
|
||||
if (!ignore_issuer && !this._issuer.equals(d._issuer)) return false;
|
||||
}
|
||||
return true;
|
||||
} else return false;
|
||||
};
|
||||
|
||||
// Result in terms of this' currency and issuer.
|
||||
@@ -298,7 +319,7 @@ Amount.prototype.divide = function (d) {
|
||||
throw "divide by zero";
|
||||
}
|
||||
else if (this.is_zero()) {
|
||||
result = this.clone();
|
||||
result = this;
|
||||
}
|
||||
else if (!this.is_valid()) {
|
||||
throw new Error("Invalid dividend");
|
||||
@@ -307,13 +328,35 @@ Amount.prototype.divide = function (d) {
|
||||
throw new Error("Invalid divisor");
|
||||
}
|
||||
else {
|
||||
var _n = this;
|
||||
|
||||
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);
|
||||
_n._offset -= 1;
|
||||
}
|
||||
}
|
||||
|
||||
var _d = 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);
|
||||
_d._offset -= 1;
|
||||
}
|
||||
}
|
||||
|
||||
result = new Amount();
|
||||
result._offset = this._offset - d._offset - 17;
|
||||
result._value = this._value.multiply(consts.bi_1e17).divide(d._value).add(consts.bi_5);
|
||||
result._is_native = this._is_native;
|
||||
result._is_negative = this._is_negative !== d._is_negative;
|
||||
result._currency = this._currency.clone();
|
||||
result._issuer = this._issuer.clone();
|
||||
result._offset = _n._offset - _d._offset - 17;
|
||||
result._value = _n._value.multiply(consts.bi_1e17).divide(_d._value).add(consts.bi_5);
|
||||
result._is_native = _n._is_native;
|
||||
result._is_negative = _n._is_negative !== _d._is_negative;
|
||||
result._currency = _n._currency;
|
||||
result._issuer = _n._issuer;
|
||||
|
||||
result.canonicalize();
|
||||
}
|
||||
@@ -339,6 +382,13 @@ Amount.prototype.divide = function (d) {
|
||||
* @return {Amount} The resulting ratio. Unit will be the same as numerator.
|
||||
*/
|
||||
Amount.prototype.ratio_human = function (denominator) {
|
||||
if ("number" === typeof denominator && parseInt(denominator) === denominator) {
|
||||
// Special handling of integer arguments
|
||||
denominator = Amount.from_json("" + denominator + ".0");
|
||||
} else {
|
||||
denominator = Amount.from_json(denominator);
|
||||
}
|
||||
|
||||
var numerator = this;
|
||||
denominator = Amount.from_json(denominator);
|
||||
|
||||
@@ -380,7 +430,12 @@ Amount.prototype.ratio_human = function (denominator) {
|
||||
* @return {Amount} The product. Unit will be the same as the first factor.
|
||||
*/
|
||||
Amount.prototype.product_human = function (factor) {
|
||||
factor = Amount.from_json(factor);
|
||||
if ("number" === typeof factor && parseInt(factor) === factor) {
|
||||
// Special handling of integer arguments
|
||||
factor = Amount.from_json("" + factor + ".0");
|
||||
} else {
|
||||
factor = Amount.from_json(factor);
|
||||
}
|
||||
|
||||
var product = this.multiply(factor);
|
||||
|
||||
@@ -413,6 +468,10 @@ Amount.prototype.is_negative = function () {
|
||||
: false; // NaN is not negative
|
||||
};
|
||||
|
||||
Amount.prototype.is_positive = function () {
|
||||
return !this.is_zero() && !this.is_negative();
|
||||
};
|
||||
|
||||
// Only checks the value. Not the currency and issuer.
|
||||
Amount.prototype.is_valid = function () {
|
||||
return this._value instanceof BigInteger;
|
||||
@@ -433,15 +492,16 @@ Amount.prototype.issuer = function () {
|
||||
};
|
||||
|
||||
// Result in terms of this' currency and issuer.
|
||||
// XXX Diverges from cpp.
|
||||
Amount.prototype.multiply = function (v) {
|
||||
var result;
|
||||
|
||||
if (this.is_zero()) {
|
||||
result = this.clone();
|
||||
if (this.is_zero()) {
|
||||
result = this;
|
||||
}
|
||||
else if (v.is_zero()) {
|
||||
result = this.clone();
|
||||
result._value = BigInteger.ZERO.clone();
|
||||
result._value = BigInteger.ZERO;
|
||||
}
|
||||
else {
|
||||
var v1 = this._value;
|
||||
@@ -449,14 +509,18 @@ Amount.prototype.multiply = function (v) {
|
||||
var v2 = v._value;
|
||||
var o2 = v._offset;
|
||||
|
||||
while (v1.compareTo(consts.bi_man_min_value) < 0 ) {
|
||||
v1 = v1.multiply(consts.bi_10);
|
||||
o1 -= 1;
|
||||
if (this.is_native()) {
|
||||
while (v1.compareTo(consts.bi_man_min_value) < 0) {
|
||||
v1 = v1.multiply(consts.bi_10);
|
||||
o1 -= 1;
|
||||
}
|
||||
}
|
||||
|
||||
while (v2.compareTo(consts.bi_man_min_value) < 0 ) {
|
||||
v2 = v2.multiply(consts.bi_10);
|
||||
o2 -= 1;
|
||||
if (v.is_native()) {
|
||||
while (v2.compareTo(consts.bi_man_min_value) < 0) {
|
||||
v2 = v2.multiply(consts.bi_10);
|
||||
o2 -= 1;
|
||||
}
|
||||
}
|
||||
|
||||
result = new Amount();
|
||||
@@ -464,8 +528,8 @@ Amount.prototype.multiply = function (v) {
|
||||
result._value = v1.multiply(v2).divide(consts.bi_1e14).add(consts.bi_7);
|
||||
result._is_native = this._is_native;
|
||||
result._is_negative = this._is_negative !== v._is_negative;
|
||||
result._currency = this._currency.clone();
|
||||
result._issuer = this._issuer.clone();
|
||||
result._currency = this._currency;
|
||||
result._issuer = this._issuer;
|
||||
|
||||
result.canonicalize();
|
||||
}
|
||||
@@ -549,11 +613,15 @@ Amount.prototype.parse_issuer = function (issuer) {
|
||||
Amount.prototype.parse_json = function (j) {
|
||||
if ('string' === typeof j) {
|
||||
// .../.../... notation is not a wire format. But allowed for easier testing.
|
||||
var m = j.match(/^(.+)\/(...)\/(.+)$/);
|
||||
var m = j.match(/^([^/]+)\/(...)(?:\/(.+))?$/);
|
||||
|
||||
if (m) {
|
||||
this._currency = Currency.from_json(m[2]);
|
||||
this._issuer = UInt160.from_json(m[3]);
|
||||
if (m[3]) {
|
||||
this._issuer = UInt160.from_json(m[3]);
|
||||
} else {
|
||||
this._issuer = UInt160.from_json('1');
|
||||
}
|
||||
this.parse_value(m[1]);
|
||||
}
|
||||
else {
|
||||
@@ -562,6 +630,9 @@ Amount.prototype.parse_json = function (j) {
|
||||
this._issuer = new UInt160();
|
||||
}
|
||||
}
|
||||
else if ('number' === typeof j) {
|
||||
this.parse_json(""+j);
|
||||
}
|
||||
else if ('object' === typeof j && j instanceof Amount) {
|
||||
j.copyTo(this);
|
||||
}
|
||||
@@ -673,7 +744,7 @@ Amount.prototype.parse_value = function (j) {
|
||||
}
|
||||
}
|
||||
else if (j instanceof BigInteger) {
|
||||
this._value = j.clone();
|
||||
this._value = j;
|
||||
}
|
||||
else {
|
||||
this._value = NaN;
|
||||
@@ -690,6 +761,7 @@ Amount.prototype.set_currency = function (c) {
|
||||
{
|
||||
c.copyTo(this._currency);
|
||||
}
|
||||
this._is_native = this._currency.is_native();
|
||||
|
||||
return this;
|
||||
};
|
||||
@@ -879,28 +951,33 @@ Amount.prototype.to_text_full = function (opts) {
|
||||
};
|
||||
|
||||
// For debugging.
|
||||
Amount.prototype.not_equals_why = function (d) {
|
||||
return 'string' === typeof (d)
|
||||
? this.not_equals_why(Amount.from_json(d))
|
||||
: this === d
|
||||
? false
|
||||
: d instanceof Amount
|
||||
? this._is_native === d._is_native
|
||||
? this._is_native
|
||||
? this._value.equals(d._value)
|
||||
? false
|
||||
: "XRP value differs."
|
||||
: this._currency.equals(d._currency)
|
||||
? this._is_negative === d._is_negative
|
||||
? this._value.equals(d._value)
|
||||
? false
|
||||
: this._value.equals(BigInteger.ZERO) && d._value.equals(BigInteger.ZERO)
|
||||
? false
|
||||
: "Non-XRP value differs."
|
||||
: "Non-XRP sign differs."
|
||||
: "Non-XRP currency differs (" + JSON.stringify(this._currency) + "/" + JSON.stringify(d._currency) + ")"
|
||||
: "Native mismatch"
|
||||
: "Wrong constructor."
|
||||
Amount.prototype.not_equals_why = function (d, ignore_issuer) {
|
||||
if ("string" === typeof d) {
|
||||
return this.not_equals_why(Amount.from_json(d));
|
||||
}
|
||||
|
||||
if (this === d) return false;
|
||||
|
||||
if (d instanceof Amount) {
|
||||
if (!this.is_valid() || !d.is_valid()) return "Invalid amount.";
|
||||
if (this._is_native !== d._is_native) return "Native mismatch.";
|
||||
|
||||
var type = this._is_native ? "XRP" : "Non-XRP";
|
||||
|
||||
if (!this._value.equals(d._value) || this._offset !== d._offset) {
|
||||
return type+" value differs.";
|
||||
}
|
||||
|
||||
if (this._is_negative !== d._is_negative) return type+" sign differs.";
|
||||
|
||||
if (!this._is_native) {
|
||||
if (!this._currency.equals(d._currency)) return "Non-XRP currency differs.";
|
||||
if (!ignore_issuer && !this._issuer.equals(d._issuer)) {
|
||||
return "Non-XRP issuer differs.";
|
||||
}
|
||||
}
|
||||
return false;
|
||||
} else return "Wrong constructor.";
|
||||
};
|
||||
|
||||
exports.Amount = Amount;
|
||||
|
||||
@@ -130,7 +130,11 @@ Base.decode_check = function (version, input, alphabet) {
|
||||
if (computed[i] !== checksum[i])
|
||||
return NaN;
|
||||
|
||||
return new BigInteger(buffer.slice(1, -4));
|
||||
// We'll use the version byte to add a leading zero, this ensures JSBN doesn't
|
||||
// intrepret the value as a negative number
|
||||
buffer[0] = 0;
|
||||
|
||||
return new BigInteger(buffer.slice(0, -4), 256);
|
||||
}
|
||||
|
||||
exports.Base = Base;
|
||||
|
||||
67
src/js/keypair.js
Normal file
67
src/js/keypair.js
Normal file
@@ -0,0 +1,67 @@
|
||||
var sjcl = require('../../build/sjcl');
|
||||
|
||||
var UInt256 = require('./uint256').UInt256;
|
||||
|
||||
var KeyPair = function ()
|
||||
{
|
||||
this._curve = sjcl.ecc.curves['c256'];
|
||||
this._secret = null;
|
||||
this._pubkey = null;
|
||||
};
|
||||
|
||||
KeyPair.from_bn_secret = function (j)
|
||||
{
|
||||
if (j instanceof this) {
|
||||
return j.clone();
|
||||
} else {
|
||||
return (new this()).parse_bn_secret(j);
|
||||
}
|
||||
};
|
||||
|
||||
KeyPair.prototype.parse_bn_secret = function (j)
|
||||
{
|
||||
this._secret = new sjcl.ecc.ecdsa.secretKey(sjcl.ecc.curves['c256'], j);
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns public key as sjcl public key.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
KeyPair.prototype._pub = function ()
|
||||
{
|
||||
var curve = this._curve;
|
||||
|
||||
if (!this._pubkey && this._secret) {
|
||||
var exponent = this._secret._exponent;
|
||||
this._pubkey = new sjcl.ecc.ecdsa.publicKey(curve, curve.G.mult(exponent));
|
||||
}
|
||||
|
||||
return this._pubkey;
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns public key as hex.
|
||||
*
|
||||
* Key will be returned as a compressed pubkey - 33 bytes converted to hex.
|
||||
*/
|
||||
KeyPair.prototype.to_hex_pub = function ()
|
||||
{
|
||||
var pub = this._pub();
|
||||
if (!pub) return null;
|
||||
|
||||
var point = pub._point, y_even = point.y.mod(2).equals(0);
|
||||
return sjcl.codec.hex.fromBits(sjcl.bitArray.concat(
|
||||
[sjcl.bitArray.partial(8, y_even ? 0x02 : 0x03)],
|
||||
point.x.toBits(this._curve.r.bitLength())
|
||||
)).toUpperCase();
|
||||
};
|
||||
|
||||
KeyPair.prototype.sign = function (hash)
|
||||
{
|
||||
hash = UInt256.from_json(hash);
|
||||
return this._secret.signDER(hash.to_bits(), 0);
|
||||
};
|
||||
|
||||
exports.KeyPair = KeyPair;
|
||||
108
src/js/meta.js
Normal file
108
src/js/meta.js
Normal file
@@ -0,0 +1,108 @@
|
||||
var extend = require('extend');
|
||||
var UInt160 = require('./uint160').UInt160;
|
||||
var Amount = require('./amount').Amount;
|
||||
|
||||
/**
|
||||
* Meta data processing facility.
|
||||
*/
|
||||
var Meta = function (raw_data)
|
||||
{
|
||||
this.nodes = [];
|
||||
|
||||
for (var i = 0, l = raw_data.AffectedNodes.length; i < l; i++) {
|
||||
var an = raw_data.AffectedNodes[i],
|
||||
result = {};
|
||||
|
||||
["CreatedNode", "ModifiedNode", "DeletedNode"].forEach(function (x) {
|
||||
if (an[x]) result.diffType = x;
|
||||
});
|
||||
|
||||
if (!result.diffType) return null;
|
||||
|
||||
an = an[result.diffType];
|
||||
|
||||
result.entryType = an.LedgerEntryType;
|
||||
result.ledgerIndex = an.LedgerIndex;
|
||||
|
||||
result.fields = extend({}, an.PreviousFields, an.NewFields, an.FinalFields);
|
||||
result.fieldsPrev = an.PreviousFields || {};
|
||||
result.fieldsNew = an.NewFields || {};
|
||||
result.fieldsFinal = an.FinalFields || {};
|
||||
|
||||
this.nodes.push(result);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Execute a function on each affected node.
|
||||
*
|
||||
* The callback is passed two parameters. The first is a node object which looks
|
||||
* like this:
|
||||
*
|
||||
* {
|
||||
* // Type of diff, e.g. CreatedNode, ModifiedNode
|
||||
* diffType: 'CreatedNode'
|
||||
*
|
||||
* // Type of node affected, e.g. RippleState, AccountRoot
|
||||
* entryType: 'RippleState',
|
||||
*
|
||||
* // Index of the ledger this change occurred in
|
||||
* ledgerIndex: '01AB01AB...',
|
||||
*
|
||||
* // Contains all fields with later versions taking precedence
|
||||
* //
|
||||
* // This is a shorthand for doing things like checking which account
|
||||
* // this affected without having to check the diffType.
|
||||
* fields: {...},
|
||||
*
|
||||
* // Old fields (before the change)
|
||||
* fieldsPrev: {...},
|
||||
*
|
||||
* // New fields (that have been added)
|
||||
* fieldsNew: {...},
|
||||
*
|
||||
* // Changed fields
|
||||
* fieldsFinal: {...}
|
||||
* }
|
||||
*
|
||||
* The second parameter to the callback is the index of the node in the metadata
|
||||
* (first entry is index 0).
|
||||
*/
|
||||
Meta.prototype.each = function (fn)
|
||||
{
|
||||
for (var i = 0, l = this.nodes.length; i < l; i++) {
|
||||
fn(this.nodes[i], i);
|
||||
}
|
||||
};
|
||||
|
||||
var amountFieldsAffectingIssuer = [
|
||||
"LowLimit", "HighLimit", "TakerPays", "TakerGets"
|
||||
];
|
||||
Meta.prototype.getAffectedAccounts = function ()
|
||||
{
|
||||
var accounts = [];
|
||||
|
||||
// This code should match the behavior of the C++ method:
|
||||
// TransactionMetaSet::getAffectedAccounts
|
||||
this.each(function (an) {
|
||||
var fields = (an.diffType === "CreatedNode") ? an.fieldsNew : an.fieldsFinal;
|
||||
|
||||
for (var i in fields) {
|
||||
var field = fields[i];
|
||||
|
||||
if ("string" === typeof field && UInt160.is_valid(field)) {
|
||||
accounts.push(field);
|
||||
} else if (amountFieldsAffectingIssuer.indexOf(i) !== -1) {
|
||||
var amount = Amount.from_json(field);
|
||||
var issuer = amount.issuer();
|
||||
if (issuer.is_valid() && !issuer.is_zero()) {
|
||||
accounts.push(issuer.to_json());
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return accounts;
|
||||
};
|
||||
|
||||
exports.Meta = Meta;
|
||||
104
src/js/orderbook.js
Normal file
104
src/js/orderbook.js
Normal file
@@ -0,0 +1,104 @@
|
||||
// Routines for working with an orderbook.
|
||||
//
|
||||
// Events:
|
||||
|
||||
// var network = require("./network.js");
|
||||
|
||||
var EventEmitter = require('events').EventEmitter;
|
||||
var Amount = require('./amount').Amount;
|
||||
var UInt160 = require('./uint160').UInt160;
|
||||
var Currency = require('./currency').Currency;
|
||||
|
||||
var extend = require('extend');
|
||||
|
||||
var OrderBook = function (remote,
|
||||
currency_out, issuer_out,
|
||||
currency_in, issuer_in) {
|
||||
var self = this;
|
||||
|
||||
this._remote = remote;
|
||||
this._currency_out = currency_out;
|
||||
this._issuer_out = issuer_out;
|
||||
this._currency_in = currency_in;
|
||||
this._issuer_in = issuer_in;
|
||||
|
||||
this._subs = 0;
|
||||
|
||||
// Ledger entry object
|
||||
// Important: This must never be overwritten, only extend()-ed
|
||||
this._entry = {};
|
||||
|
||||
this.on('newListener', function (type, listener) {
|
||||
if (OrderBook.subscribe_events.indexOf(type) !== -1) {
|
||||
if (!self._subs && 'open' === self._remote._online_state) {
|
||||
self._remote.request_subscribe()
|
||||
.books([self.to_json()])
|
||||
.request();
|
||||
}
|
||||
self._subs += 1;
|
||||
}
|
||||
});
|
||||
|
||||
this.on('removeListener', function (type, listener) {
|
||||
if (OrderBook.subscribe_events.indexOf(type) !== -1) {
|
||||
self._subs -= 1;
|
||||
|
||||
if (!self._subs && 'open' === self._remote._online_state) {
|
||||
self._remote.request_unsubscribe()
|
||||
.books([self.to_json()])
|
||||
.request();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
this._remote.on('connect', function () {
|
||||
if (self._subs) {
|
||||
self._remote.request_subscribe()
|
||||
.books([self.to_json()])
|
||||
.request();
|
||||
}
|
||||
});
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
OrderBook.prototype = new EventEmitter;
|
||||
|
||||
/**
|
||||
* List of events that require a remote subscription to the orderbook.
|
||||
*/
|
||||
OrderBook.subscribe_events = ['transaction'];
|
||||
|
||||
OrderBook.prototype.to_json = function ()
|
||||
{
|
||||
var json = {
|
||||
"CurrencyOut": this._currency_out,
|
||||
"CurrencyIn": this._currency_in
|
||||
};
|
||||
|
||||
if (json["CurrencyOut"] !== "XRP") json["IssuerOut"] = this._issuer_out;
|
||||
if (json["CurrencyIn"] !== "XRP") json["IssuerIn"] = this._issuer_in;
|
||||
|
||||
return json;
|
||||
};
|
||||
|
||||
/**
|
||||
* Whether the OrderBook is valid.
|
||||
*
|
||||
* Note: This only checks whether the parameters (currencies and issuer) are
|
||||
* syntactically valid. It does not check anything against the ledger.
|
||||
*/
|
||||
OrderBook.prototype.is_valid = function ()
|
||||
{
|
||||
return (
|
||||
Currency.is_valid(this._currency_in) &&
|
||||
(this._currency_in !== "XRP" && UInt160.is_valid(this._issuer_in)) &&
|
||||
Currency.is_valid(this._currency_out) &&
|
||||
(this._currency_out !== "XRP" && UInt160.is_valid(this._issuer_out)) &&
|
||||
!(this._currency_in === "XRP" && this._currency_out === "XRP")
|
||||
);
|
||||
};
|
||||
|
||||
exports.OrderBook = OrderBook;
|
||||
|
||||
// vim:sw=2:sts=2:ts=8:et
|
||||
207
src/js/remote.js
207
src/js/remote.js
@@ -20,6 +20,9 @@ var Amount = require('./amount').Amount;
|
||||
var Currency = require('./amount').Currency;
|
||||
var UInt160 = require('./amount').UInt160;
|
||||
var Transaction = require('./transaction').Transaction;
|
||||
var Account = require('./account').Account;
|
||||
var Meta = require('./meta').Meta;
|
||||
var OrderBook = require('./orderbook').OrderBook;
|
||||
|
||||
var utils = require('./utils');
|
||||
var config = require('./config');
|
||||
@@ -88,6 +91,26 @@ Request.prototype.ledger_index = function (ledger_index) {
|
||||
return this;
|
||||
};
|
||||
|
||||
Request.prototype.ledger_select = function (ledger_spec) {
|
||||
if (ledger_spec === 'closed') {
|
||||
this.message.ledger_index = -1;
|
||||
|
||||
} else if (ledger_spec === 'current') {
|
||||
this.message.ledger_index = -2;
|
||||
|
||||
} else if (ledger_spec === 'verified') {
|
||||
this.message.ledger_index = -3;
|
||||
|
||||
} else if (String(ledger_spec).length > 12) { // XXX Better test needed
|
||||
this.message.ledger_hash = ledger_spec;
|
||||
|
||||
} else {
|
||||
this.message.ledger_index = ledger_spec;
|
||||
}
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
Request.prototype.account_root = function (account) {
|
||||
this.message.account_root = UInt160.json_rewrite(account);
|
||||
|
||||
@@ -138,6 +161,12 @@ Request.prototype.tx_json = function (j) {
|
||||
return this;
|
||||
};
|
||||
|
||||
Request.prototype.tx_blob = function (j) {
|
||||
this.message.tx_blob = j;
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
Request.prototype.ripple_state = function (account, issuer, currency) {
|
||||
this.message.ripple_state = {
|
||||
'accounts' : [
|
||||
@@ -173,6 +202,31 @@ Request.prototype.rt_accounts = function (accounts) {
|
||||
return this.accounts(accounts, true);
|
||||
};
|
||||
|
||||
Request.prototype.books = function (books) {
|
||||
var procBooks = [];
|
||||
|
||||
for (var i = 0, l = books.length; i < l; i++) {
|
||||
var book = books[i];
|
||||
|
||||
var json = {
|
||||
"CurrencyOut": Currency.json_rewrite(book["CurrencyOut"]),
|
||||
"CurrencyIn": Currency.json_rewrite(book["CurrencyIn"])
|
||||
};
|
||||
|
||||
if (json["CurrencyOut"] !== "XRP") {
|
||||
json["IssuerOut"] = UInt160.json_rewrite(book["IssuerOut"]);
|
||||
}
|
||||
if (json["CurrencyIn"] !== "XRP") {
|
||||
json["IssuerIn"] = UInt160.json_rewrite(book["IssuerIn"]);
|
||||
}
|
||||
|
||||
procBooks.push(json);
|
||||
}
|
||||
this.message.books = procBooks;
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
//
|
||||
// Remote - access to a remote Ripple server via websocket.
|
||||
//
|
||||
@@ -196,6 +250,7 @@ var Remote = function (opts, trace) {
|
||||
this.websocket_ssl = opts.websocket_ssl;
|
||||
this.local_sequence = opts.local_sequence; // Locally track sequence numbers
|
||||
this.local_fee = opts.local_fee; // Locally set fees
|
||||
this.local_signing = opts.local_signing;
|
||||
this.id = 0;
|
||||
this.trace = opts.trace || trace;
|
||||
this._server_fatal = false; // True, if we know server exited.
|
||||
@@ -212,14 +267,21 @@ var Remote = function (opts, trace) {
|
||||
this.retry = undefined;
|
||||
|
||||
this._load_base = 256;
|
||||
this._load_fee = 256;
|
||||
this._load_factor = 1.0;
|
||||
this._fee_ref = undefined;
|
||||
this._fee_base = undefined;
|
||||
this._reserve_base = undefined;
|
||||
this._reserve_inc = undefined;
|
||||
this._server_status = undefined;
|
||||
|
||||
// Local signing implies local fees and sequences
|
||||
if (this.local_signing) {
|
||||
this.local_sequence = true;
|
||||
this.local_fee = true;
|
||||
}
|
||||
|
||||
// Cache information for accounts.
|
||||
// DEPRECATED, will be removed
|
||||
this.accounts = {
|
||||
// Consider sequence numbers stable if you know you're not generating bad transactions.
|
||||
// Otherwise, clear it to have it automatically refreshed from the network.
|
||||
@@ -228,6 +290,9 @@ var Remote = function (opts, trace) {
|
||||
|
||||
};
|
||||
|
||||
// Hash map of Account objects by AccountId.
|
||||
this._accounts = {};
|
||||
|
||||
// List of secrets that we know about.
|
||||
this.secrets = {
|
||||
// Secrets can be set by calling set_secret(account, secret).
|
||||
@@ -329,11 +394,13 @@ Remote.prototype._set_state = function (state) {
|
||||
switch (state) {
|
||||
case 'online':
|
||||
this._online_state = 'open';
|
||||
this.emit('connect');
|
||||
this.emit('connected');
|
||||
break;
|
||||
|
||||
case 'offline':
|
||||
this._online_state = 'closed';
|
||||
this.emit('disconnect');
|
||||
this.emit('disconnected');
|
||||
break;
|
||||
}
|
||||
@@ -374,11 +441,13 @@ Remote.prototype.ledger_hash = function () {
|
||||
|
||||
// Stop from open state.
|
||||
Remote.prototype._connect_stop = function () {
|
||||
delete this.ws.onerror;
|
||||
delete this.ws.onclose;
|
||||
if (this.ws) {
|
||||
delete this.ws.onerror;
|
||||
delete this.ws.onclose;
|
||||
|
||||
this.ws.terminate();
|
||||
delete this.ws;
|
||||
this.ws.close();
|
||||
delete this.ws;
|
||||
}
|
||||
|
||||
this._set_state('offline');
|
||||
};
|
||||
@@ -437,6 +506,12 @@ Remote.prototype._connect_start = function () {
|
||||
|
||||
if (this.trace) console.log("remote: connect: %s", url);
|
||||
|
||||
// There should not be an active connection at this point, but if there is
|
||||
// we will shut it down so we don't end up with a duplicate.
|
||||
if (this.ws) {
|
||||
this._connect_stop();
|
||||
}
|
||||
|
||||
var WebSocket = require('ws');
|
||||
var ws = this.ws = new WebSocket(url);
|
||||
|
||||
@@ -494,6 +569,7 @@ Remote.prototype._connect_start = function () {
|
||||
|
||||
// It is possible for messages to be dispatched after the connection is closed.
|
||||
Remote.prototype._connect_message = function (ws, json) {
|
||||
var self = this;
|
||||
var message = JSON.parse(json);
|
||||
var unexpected = false;
|
||||
var request;
|
||||
@@ -543,6 +619,23 @@ Remote.prototype._connect_message = function (ws, json) {
|
||||
|
||||
case 'account':
|
||||
// XXX If not trusted, need proof.
|
||||
if (this.trace) utils.logObject("remote: account: %s", message);
|
||||
|
||||
// Process metadata
|
||||
message.mmeta = new Meta(message.meta);
|
||||
|
||||
// Pass the event on to any related Account objects
|
||||
var affected = message.mmeta.getAffectedAccounts();
|
||||
for (var i = 0, l = affected.length; i < l; i++) {
|
||||
var account = self._accounts[affected[i]];
|
||||
|
||||
// Only trigger the event if the account object is actually
|
||||
// subscribed - this prevents some weird phantom events from
|
||||
// occurring.
|
||||
if (account && account._subs) {
|
||||
account.emit('transaction', message);
|
||||
}
|
||||
}
|
||||
|
||||
this.emit('account', message);
|
||||
break;
|
||||
@@ -561,6 +654,16 @@ Remote.prototype._connect_message = function (ws, json) {
|
||||
Remote.online_states.indexOf(message.server_status) !== -1
|
||||
? 'online'
|
||||
: 'offline');
|
||||
|
||||
if ('load_base' in message
|
||||
&& 'load_factor' in message
|
||||
&& (message.load_base !== this._load_base || message.load_factor != this._load_factor))
|
||||
{
|
||||
this._load_base = message.load_base;
|
||||
this._load_factor = message.load_factor;
|
||||
|
||||
this.emit('load', { 'load_base' : this._load_base, 'load_factor' : this.load_factor });
|
||||
}
|
||||
break;
|
||||
|
||||
// All other messages
|
||||
@@ -763,15 +866,25 @@ Remote.prototype.request_unsubscribe = function (streams) {
|
||||
return request;
|
||||
};
|
||||
|
||||
// --> current: true, for the current ledger.
|
||||
Remote.prototype.request_transaction_entry = function (hash, current) {
|
||||
// .ledger_choose()
|
||||
// .ledger_hash()
|
||||
// .ledger_index()
|
||||
Remote.prototype.request_transaction_entry = function (hash) {
|
||||
//utils.assert(this.trusted); // If not trusted, need to check proof, maybe talk packet protocol.
|
||||
|
||||
return (new Request(this, 'transaction_entry'))
|
||||
.ledger_choose(current)
|
||||
.tx_hash(hash);
|
||||
};
|
||||
|
||||
// DEPRECATED: use request_transaction_entry
|
||||
Remote.prototype.request_tx = function (hash) {
|
||||
var request = new Request(this, 'tx');
|
||||
|
||||
request.message.transaction = hash;
|
||||
|
||||
return request;
|
||||
};
|
||||
|
||||
Remote.prototype.request_account_info = function (accountID) {
|
||||
var request = new Request(this, 'account_info');
|
||||
|
||||
@@ -863,58 +976,12 @@ Remote.prototype.request_sign = function (secret, tx_json) {
|
||||
};
|
||||
|
||||
// Submit a transaction.
|
||||
Remote.prototype.submit = function (transaction) {
|
||||
Remote.prototype.request_submit = function () {
|
||||
var self = this;
|
||||
|
||||
if (transaction._secret && !this.trusted)
|
||||
{
|
||||
transaction.emit('error', {
|
||||
'result' : 'tejServerUntrusted',
|
||||
'result_message' : "Attempt to give a secret to an untrusted server."
|
||||
});
|
||||
}
|
||||
else {
|
||||
if (self.local_sequence && !transaction.tx_json.Sequence) {
|
||||
transaction.tx_json.Sequence = this.account_seq(transaction.tx_json.Account, 'ADVANCE');
|
||||
// console.log("Sequence: %s", transaction.tx_json.Sequence);
|
||||
}
|
||||
var request = new Request(this, 'submit');
|
||||
|
||||
if (self.local_sequence && !transaction.tx_json.Sequence) {
|
||||
// Look in the last closed ledger.
|
||||
this.account_seq_cache(transaction.tx_json.Account, false)
|
||||
.on('success_account_seq_cache', function () {
|
||||
// Try again.
|
||||
self.submit(transaction);
|
||||
})
|
||||
.on('error_account_seq_cache', function (message) {
|
||||
// XXX Maybe be smarter about this. Don't want to trust an untrusted server for this seq number.
|
||||
|
||||
// Look in the current ledger.
|
||||
self.account_seq_cache(transaction.tx_json.Account, 'CURRENT')
|
||||
.on('success_account_seq_cache', function () {
|
||||
// Try again.
|
||||
self.submit(transaction);
|
||||
})
|
||||
.on('error_account_seq_cache', function (message) {
|
||||
// Forward errors.
|
||||
transaction.emit('error', message);
|
||||
})
|
||||
.request();
|
||||
})
|
||||
.request();
|
||||
}
|
||||
else {
|
||||
// Convert the transaction into a request and submit it.
|
||||
|
||||
(new Request(this, 'submit'))
|
||||
.build_path(transaction._build_path)
|
||||
.tx_json(transaction.tx_json)
|
||||
.secret(transaction._secret)
|
||||
.on('success', function (message) { transaction.emit('success', message); }) // Forward successes and errors.
|
||||
.on('error', function (message) { transaction.emit('error', message); })
|
||||
.request();
|
||||
}
|
||||
}
|
||||
return request;
|
||||
};
|
||||
|
||||
//
|
||||
@@ -928,7 +995,7 @@ Remote.prototype._server_subscribe = function () {
|
||||
var self = this;
|
||||
|
||||
var feeds = [ 'ledger', 'server' ];
|
||||
|
||||
|
||||
if (this._transaction_subs)
|
||||
feeds.push('transactions');
|
||||
|
||||
@@ -950,10 +1017,10 @@ Remote.prototype._server_subscribe = function () {
|
||||
|
||||
// FIXME Use this to estimate fee.
|
||||
self._load_base = message.load_base || 256;
|
||||
self._load_fee = message.load_fee || 256;
|
||||
self._load_factor = message.load_factor || 1.0;
|
||||
self._fee_ref = message.fee_ref;
|
||||
self._fee_base = message.fee_base;
|
||||
self._reserve_base = message.reverse_base;
|
||||
self._reserve_base = message.reserve_base;
|
||||
self._reserve_inc = message.reserve_inc;
|
||||
self._server_status = message.server_status;
|
||||
|
||||
@@ -1017,6 +1084,23 @@ Remote.prototype.request_owner_count = function (account, current) {
|
||||
});
|
||||
};
|
||||
|
||||
Remote.prototype.account = function (accountId) {
|
||||
var account = new Account(this, accountId);
|
||||
|
||||
if (!account.is_valid()) return account;
|
||||
|
||||
return this._accounts[account.to_json()] = account;
|
||||
};
|
||||
|
||||
Remote.prototype.book = function (currency_out, issuer_out,
|
||||
currency_in, issuer_in) {
|
||||
var book = new OrderBook(this,
|
||||
currency_out, issuer_out,
|
||||
currency_in, issuer_in);
|
||||
|
||||
return book;
|
||||
}
|
||||
|
||||
// Return the next account sequence if possible.
|
||||
// <-- undefined or Sequence
|
||||
Remote.prototype.account_seq = function (account, advance) {
|
||||
@@ -1028,7 +1112,8 @@ Remote.prototype.account_seq = function (account, advance) {
|
||||
{
|
||||
seq = account_info.seq;
|
||||
|
||||
if (advance) account_info.seq += 1;
|
||||
if (advance === "ADVANCE") account_info.seq += 1;
|
||||
if (advance === "REWIND") account_info.seq -= 1;
|
||||
|
||||
// console.log("cached: %s current=%d next=%d", account, seq, account_info.seq);
|
||||
}
|
||||
|
||||
119
src/js/seed.js
119
src/js/seed.js
@@ -5,85 +5,65 @@
|
||||
var sjcl = require('../../build/sjcl');
|
||||
var utils = require('./utils');
|
||||
var jsbn = require('./jsbn');
|
||||
var extend = require('extend');
|
||||
|
||||
var BigInteger = jsbn.BigInteger;
|
||||
|
||||
var Base = require('./base').Base,
|
||||
UInt256 = require('./uint256').UInt256;
|
||||
UInt = require('./uint').UInt,
|
||||
UInt256 = require('./uint256').UInt256,
|
||||
KeyPair = require('./keypair').KeyPair;
|
||||
|
||||
var Seed = function () {
|
||||
var Seed = extend(function () {
|
||||
// Internal form: NaN or BigInteger
|
||||
this._value = NaN;
|
||||
};
|
||||
this._curve = sjcl.ecc.curves['c256'];
|
||||
this._value = NaN;
|
||||
}, UInt);
|
||||
|
||||
Seed.json_rewrite = function (j) {
|
||||
return Seed.from_json(j).to_json();
|
||||
};
|
||||
|
||||
// Return a new Seed from j.
|
||||
Seed.from_json = function (j) {
|
||||
return (j instanceof Seed)
|
||||
? j.clone()
|
||||
: (new Seed()).parse_json(j);
|
||||
};
|
||||
|
||||
Seed.is_valid = function (j) {
|
||||
return Seed.from_json(j).is_valid();
|
||||
};
|
||||
|
||||
Seed.prototype.clone = function () {
|
||||
return this.copyTo(new Seed());
|
||||
};
|
||||
|
||||
// Returns copy.
|
||||
Seed.prototype.copyTo = function (d) {
|
||||
d._value = this._value;
|
||||
|
||||
return d;
|
||||
};
|
||||
|
||||
Seed.prototype.equals = function (d) {
|
||||
return this._value instanceof BigInteger && d._value instanceof BigInteger && this._value.equals(d._value);
|
||||
};
|
||||
|
||||
Seed.prototype.is_valid = function () {
|
||||
return this._value instanceof BigInteger;
|
||||
};
|
||||
Seed.width = 16;
|
||||
Seed.prototype = extend({}, UInt.prototype);
|
||||
Seed.prototype.constructor = Seed;
|
||||
|
||||
// value = NaN on error.
|
||||
// One day this will support rfc1751 too.
|
||||
Seed.prototype.parse_json = function (j) {
|
||||
if ('string' !== typeof j) {
|
||||
this._value = NaN;
|
||||
}
|
||||
else if (j[0] === "s") {
|
||||
this._value = Base.decode_check(Base.VER_FAMILY_SEED, j);
|
||||
}
|
||||
else if (16 === j.length) {
|
||||
this._value = new BigInteger(utils.stringToArray(j), 128);
|
||||
}
|
||||
else {
|
||||
this._value = NaN;
|
||||
if ('string' === typeof j) {
|
||||
if (!j.length) {
|
||||
this._value = NaN;
|
||||
// XXX Should actually always try and continue if it failed.
|
||||
} else if (j[0] === "s") {
|
||||
this._value = Base.decode_check(Base.VER_FAMILY_SEED, j);
|
||||
} else if (j.length === 32) {
|
||||
this._value = this.parse_hex(j);
|
||||
// XXX Should also try 1751
|
||||
} else {
|
||||
this.parse_passphrase(j);
|
||||
}
|
||||
} else {
|
||||
this._value = NaN;
|
||||
}
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
// Convert from internal form.
|
||||
Seed.prototype.parse_passphrase = function (j) {
|
||||
if ("string" !== typeof j) {
|
||||
throw new Error("Passphrase must be a string");
|
||||
}
|
||||
|
||||
var hash = sjcl.hash.sha512.hash(sjcl.codec.utf8String.toBits(j));
|
||||
var bits = sjcl.bitArray.bitSlice(hash, 0, 128);
|
||||
|
||||
this.parse_bits(bits);
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
Seed.prototype.to_json = function () {
|
||||
if (!(this._value instanceof BigInteger))
|
||||
return NaN;
|
||||
|
||||
var bytes = this._value.toByteArray().map(function (b) { return b ? b < 0 ? 256+b : b : 0; });
|
||||
var target = 20;
|
||||
|
||||
// XXX Make sure only trim off leading zeros.
|
||||
var array = bytes.length < target
|
||||
? bytes.length
|
||||
? [].concat(utils.arraySet(target - bytes.length, 0), bytes)
|
||||
: utils.arraySet(target, 0)
|
||||
: bytes.slice(target - bytes.length);
|
||||
var output = Base.encode_check(Base.VER_FAMILY_SEED, array);
|
||||
var output = Base.encode_check(Base.VER_FAMILY_SEED, this.to_bytes());
|
||||
|
||||
return output;
|
||||
};
|
||||
@@ -103,27 +83,34 @@ function SHA256_RIPEMD160(bits) {
|
||||
return sjcl.hash.ripemd160.hash(sjcl.hash.sha256.hash(bits));
|
||||
}
|
||||
|
||||
Seed.prototype.generate_private = function (account_id) {
|
||||
// XXX If account_id is given, should loop over keys until we find the right one
|
||||
Seed.prototype.get_key = function (account_id) {
|
||||
if (!this.is_valid()) {
|
||||
throw new Error("Cannot generate keys from invalid seed!");
|
||||
}
|
||||
// XXX Should loop over keys until we find the right one
|
||||
|
||||
var curve = this._curve;
|
||||
|
||||
var seq = 0;
|
||||
|
||||
var private_gen, public_gen, i = 0;
|
||||
do {
|
||||
private_gen = sjcl.bn.fromBits(firstHalfOfSHA512(append_int(this.seed, i)));
|
||||
private_gen = sjcl.bn.fromBits(firstHalfOfSHA512(append_int(this.to_bytes(), i)));
|
||||
i++;
|
||||
} while (!sjcl.ecc.curves.c256.r.greaterEquals(private_gen));
|
||||
} while (!curve.r.greaterEquals(private_gen));
|
||||
|
||||
public_gen = sjcl.ecc.curves.c256.G.mult(private_gen);
|
||||
public_gen = curve.G.mult(private_gen);
|
||||
|
||||
var sec;
|
||||
i = 0;
|
||||
do {
|
||||
sec = sjcl.bn.fromBits(firstHalfOfSHA512(append_int(append_int(public_gen.toBytesCompressed(), seq), i)));
|
||||
i++;
|
||||
} while (!sjcl.ecc.curves.c256.r.greaterEquals(sec));
|
||||
} while (!curve.r.greaterEquals(sec));
|
||||
|
||||
return UInt256.from_bn(sec);
|
||||
sec = sec.add(private_gen).mod(curve.r);
|
||||
|
||||
return KeyPair.from_bn_secret(sec);
|
||||
};
|
||||
|
||||
exports.Seed = Seed;
|
||||
|
||||
@@ -92,7 +92,7 @@ SerializedObject.prototype.serialize_field = function (spec, obj)
|
||||
Type = spec.shift();
|
||||
|
||||
if ("undefined" !== typeof obj[name]) {
|
||||
console.log(name, Type.id, field_id);
|
||||
//console.log(name, Type.id, field_id);
|
||||
this.append(SerializedObject.get_field_header(Type.id, field_id));
|
||||
|
||||
try {
|
||||
|
||||
@@ -12,7 +12,8 @@ var extend = require('extend'),
|
||||
|
||||
var amount = require('./amount'),
|
||||
UInt160 = amount.UInt160,
|
||||
Amount = amount.Amount;
|
||||
Amount = amount.Amount,
|
||||
Currency= amount.Currency;
|
||||
|
||||
// Shortcuts
|
||||
var hex = sjcl.codec.hex,
|
||||
@@ -47,7 +48,7 @@ SerializedType.prototype.serialize_varint = function (so, val) {
|
||||
} else throw new Error("Variable integer overflow.");
|
||||
};
|
||||
|
||||
exports.Int8 = new SerializedType({
|
||||
var STInt8 = exports.Int8 = new SerializedType({
|
||||
serialize: function (so, val) {
|
||||
so.append([val & 0xff]);
|
||||
},
|
||||
@@ -56,7 +57,7 @@ exports.Int8 = new SerializedType({
|
||||
}
|
||||
});
|
||||
|
||||
exports.Int16 = new SerializedType({
|
||||
var STInt16 = exports.Int16 = new SerializedType({
|
||||
serialize: function (so, val) {
|
||||
so.append([
|
||||
val >>> 8 & 0xff,
|
||||
@@ -65,10 +66,11 @@ exports.Int16 = new SerializedType({
|
||||
},
|
||||
parse: function (so) {
|
||||
// XXX
|
||||
throw new Error("Parsing Int16 not implemented");
|
||||
}
|
||||
});
|
||||
|
||||
exports.Int32 = new SerializedType({
|
||||
var STInt32 = exports.Int32 = new SerializedType({
|
||||
serialize: function (so, val) {
|
||||
so.append([
|
||||
val >>> 24 & 0xff,
|
||||
@@ -79,46 +81,82 @@ exports.Int32 = new SerializedType({
|
||||
},
|
||||
parse: function (so) {
|
||||
// XXX
|
||||
throw new Error("Parsing Int32 not implemented");
|
||||
}
|
||||
});
|
||||
|
||||
exports.Int64 = new SerializedType({
|
||||
var STInt64 = exports.Int64 = new SerializedType({
|
||||
serialize: function (so, val) {
|
||||
// XXX
|
||||
throw new Error("Serializing Int64 not implemented");
|
||||
},
|
||||
parse: function (so) {
|
||||
// XXX
|
||||
throw new Error("Parsing Int64 not implemented");
|
||||
}
|
||||
});
|
||||
|
||||
exports.Hash128 = new SerializedType({
|
||||
var STHash128 = exports.Hash128 = new SerializedType({
|
||||
serialize: function (so, val) {
|
||||
// XXX
|
||||
throw new Error("Serializing Hash128 not implemented");
|
||||
},
|
||||
parse: function (so) {
|
||||
// XXX
|
||||
throw new Error("Parsing Hash128 not implemented");
|
||||
}
|
||||
});
|
||||
|
||||
exports.Hash256 = new SerializedType({
|
||||
var STHash256 = exports.Hash256 = new SerializedType({
|
||||
serialize: function (so, val) {
|
||||
// XXX
|
||||
throw new Error("Serializing Hash256 not implemented");
|
||||
},
|
||||
parse: function (so) {
|
||||
// XXX
|
||||
throw new Error("Parsing Hash256 not implemented");
|
||||
}
|
||||
});
|
||||
|
||||
exports.Hash160 = new SerializedType({
|
||||
var STHash160 = exports.Hash160 = new SerializedType({
|
||||
serialize: function (so, val) {
|
||||
// XXX
|
||||
throw new Error("Serializing Hash160 not implemented");
|
||||
},
|
||||
parse: function (so) {
|
||||
// XXX
|
||||
throw new Error("Parsing Hash160 not implemented");
|
||||
}
|
||||
});
|
||||
|
||||
exports.Amount = new SerializedType({
|
||||
// Internal
|
||||
var STCurrency = new SerializedType({
|
||||
serialize: function (so, val) {
|
||||
var currency = val.to_json();
|
||||
if ("string" === typeof currency && currency.length === 3) {
|
||||
var currencyCode = currency.toUpperCase(),
|
||||
currencyData = utils.arraySet(20, 0);
|
||||
|
||||
if (!/^[A-Z]{3}$/.test(currencyCode)) {
|
||||
throw new Error('Invalid currency code');
|
||||
}
|
||||
|
||||
currencyData[12] = currencyCode.charCodeAt(0) & 0xff;
|
||||
currencyData[13] = currencyCode.charCodeAt(1) & 0xff;
|
||||
currencyData[14] = currencyCode.charCodeAt(2) & 0xff;
|
||||
|
||||
so.append(currencyData);
|
||||
} else {
|
||||
throw new Error('Tried to serialize invalid/unimplemented currency type.');
|
||||
}
|
||||
},
|
||||
parse: function (so) {
|
||||
// XXX
|
||||
throw new Error("Parsing Currency not implemented");
|
||||
}
|
||||
});
|
||||
|
||||
var STAmount = exports.Amount = new SerializedType({
|
||||
serialize: function (so, val) {
|
||||
var amount = Amount.from_json(val);
|
||||
if (!amount.is_valid()) {
|
||||
@@ -126,6 +164,7 @@ exports.Amount = new SerializedType({
|
||||
}
|
||||
|
||||
// Amount (64-bit integer)
|
||||
var valueBytes = utils.arraySet(8, 0);
|
||||
if (amount.is_native()) {
|
||||
var valueHex = amount._value.toString(16);
|
||||
|
||||
@@ -137,103 +176,138 @@ exports.Amount = new SerializedType({
|
||||
valueHex = "0" + valueHex;
|
||||
}
|
||||
|
||||
var valueBytes = bytes.fromBits(hex.toBits(valueHex));
|
||||
valueBytes = bytes.fromBits(hex.toBits(valueHex));
|
||||
// Clear most significant two bits - these bits should already be 0 if
|
||||
// Amount enforces the range correctly, but we'll clear them anyway just
|
||||
// so this code can make certain guarantees about the encoded value.
|
||||
valueBytes[0] &= 0x3f;
|
||||
if (!amount.is_negative()) valueBytes[0] |= 0x40;
|
||||
|
||||
so.append(valueBytes);
|
||||
} else {
|
||||
// XXX
|
||||
throw new Error("Non-native amounts not implemented!");
|
||||
var hi = 0, lo = 0;
|
||||
|
||||
// First bit: non-native
|
||||
hi |= 1 << 31;
|
||||
|
||||
if (!amount.is_zero()) {
|
||||
// Second bit: non-negative?
|
||||
if (!amount.is_negative()) hi |= 1 << 30;
|
||||
|
||||
// Next eight bits: offset/exponent
|
||||
hi |= ((97 + amount._offset) & 0xff) << 22;
|
||||
|
||||
// Remaining 52 bits: mantissa
|
||||
hi |= amount._value.shiftRight(32).intValue() & 0x3fffff;
|
||||
lo = amount._value.intValue() & 0xffffffff;
|
||||
}
|
||||
|
||||
valueBytes = sjcl.codec.bytes.fromBits([hi, lo]);
|
||||
}
|
||||
|
||||
so.append(valueBytes);
|
||||
|
||||
if (!amount.is_native()) {
|
||||
// Currency (160-bit hash)
|
||||
var currency = amount.currency().to_json();
|
||||
if ("string" === typeof currency && currency.length === 3) {
|
||||
var currencyCode = currency.toUpperCase(),
|
||||
currencyData = utils.arraySet(20, 0);
|
||||
|
||||
if (!/^[A-Z]{3}$/.test(currencyCode)) {
|
||||
throw new Error('Invalid currency code');
|
||||
}
|
||||
|
||||
currencyData[12] = currencyCode.charCodeAt(0) & 0xff;
|
||||
currencyData[13] = currencyCode.charCodeAt(1) & 0xff;
|
||||
currencyData[14] = currencyCode.charCodeAt(2) & 0xff;
|
||||
|
||||
var currencyBits = bytes.toBits(currencyData),
|
||||
currencyHash = sjcl.hash.ripemd160.hash(currencyBits);
|
||||
|
||||
so.append(bytes.fromBits(currencyHash));
|
||||
} else {
|
||||
throw new Error('Tried to serialize invalid/unimplemented currency type.');
|
||||
}
|
||||
var currency = amount.currency();
|
||||
STCurrency.serialize(so, currency);
|
||||
|
||||
// Issuer (160-bit hash)
|
||||
// XXX
|
||||
so.append(amount.issuer().to_bytes());
|
||||
}
|
||||
},
|
||||
parse: function (so) {
|
||||
// XXX
|
||||
throw new Error("Parsing Amount not implemented");
|
||||
}
|
||||
});
|
||||
|
||||
exports.VariableLength = new SerializedType({
|
||||
var STVL = exports.VariableLength = new SerializedType({
|
||||
serialize: function (so, val) {
|
||||
if ("string" === typeof val) this.serialize_hex(so, val);
|
||||
else throw new Error("Unknown datatype.");
|
||||
},
|
||||
parse: function (so) {
|
||||
// XXX
|
||||
throw new Error("Parsing VL not implemented");
|
||||
}
|
||||
});
|
||||
|
||||
exports.Account = new SerializedType({
|
||||
var STAccount = exports.Account = new SerializedType({
|
||||
serialize: function (so, val) {
|
||||
var account = UInt160.from_json(val);
|
||||
this.serialize_hex(so, account.to_hex());
|
||||
},
|
||||
parse: function (so) {
|
||||
// XXX
|
||||
throw new Error("Parsing Account not implemented");
|
||||
}
|
||||
});
|
||||
|
||||
exports.PathSet = new SerializedType({
|
||||
var STPathSet = exports.PathSet = new SerializedType({
|
||||
serialize: function (so, val) {
|
||||
// XXX
|
||||
for (var i = 0, l = val.length; i < l; i++) {
|
||||
for (var j = 0, l2 = val[i].length; j < l2; j++) {
|
||||
var entry = val[i][j];
|
||||
|
||||
var type = 0;
|
||||
|
||||
if (entry.account) type |= 0x01;
|
||||
if (entry.currency) type |= 0x10;
|
||||
if (entry.issuer) type |= 0x20;
|
||||
|
||||
STInt8.serialize(so, type);
|
||||
|
||||
if (entry.account) {
|
||||
so.append(UInt160.from_json(entry.account).to_bytes());
|
||||
}
|
||||
if (entry.currency) {
|
||||
var currency = Currency.from_json(entry.currency);
|
||||
STCurrency.serialize(so, currency);
|
||||
}
|
||||
if (entry.issuer) {
|
||||
so.append(UInt160.from_json(entry.issuer).to_bytes());
|
||||
}
|
||||
}
|
||||
|
||||
if (j < l2) STInt8.serialize(so, 0xff);
|
||||
}
|
||||
STInt8.serialize(so, 0x00);
|
||||
},
|
||||
parse: function (so) {
|
||||
// XXX
|
||||
throw new Error("Parsing PathSet not implemented");
|
||||
}
|
||||
});
|
||||
|
||||
exports.Vector256 = new SerializedType({
|
||||
var STVector256 = exports.Vector256 = new SerializedType({
|
||||
serialize: function (so, val) {
|
||||
// XXX
|
||||
throw new Error("Serializing Vector256 not implemented");
|
||||
},
|
||||
parse: function (so) {
|
||||
// XXX
|
||||
throw new Error("Parsing Vector256 not implemented");
|
||||
}
|
||||
});
|
||||
|
||||
exports.Object = new SerializedType({
|
||||
var STObject = exports.Object = new SerializedType({
|
||||
serialize: function (so, val) {
|
||||
// XXX
|
||||
throw new Error("Serializing Object not implemented");
|
||||
},
|
||||
parse: function (so) {
|
||||
// XXX
|
||||
throw new Error("Parsing Object not implemented");
|
||||
}
|
||||
});
|
||||
|
||||
exports.Array = new SerializedType({
|
||||
var STArray = exports.Array = new SerializedType({
|
||||
serialize: function (so, val) {
|
||||
// XXX
|
||||
throw new Error("Serializing Array not implemented");
|
||||
},
|
||||
parse: function (so) {
|
||||
// XXX
|
||||
throw new Error("Parsing Array not implemented");
|
||||
}
|
||||
});
|
||||
|
||||
@@ -5,12 +5,18 @@ sjcl.ecc.ecdsa.secretKey.prototype.signDER = function(hash, paranoia) {
|
||||
sjcl.ecc.ecdsa.secretKey.prototype.encodeDER = function(rs) {
|
||||
var w = sjcl.bitArray,
|
||||
R = this._curve.r,
|
||||
l = R.bitLength(),
|
||||
r = sjcl.bn.fromBits(w.bitSlice(rs,0,l)).toBits(),
|
||||
s = sjcl.bn.fromBits(w.bitSlice(rs,l,2*l)).toBits();
|
||||
l = R.bitLength();
|
||||
|
||||
var rb = sjcl.codec.bytes.fromBits(r),
|
||||
sb = sjcl.codec.bytes.fromBits(s);
|
||||
var rb = sjcl.codec.bytes.fromBits(w.bitSlice(rs,0,l)),
|
||||
sb = sjcl.codec.bytes.fromBits(w.bitSlice(rs,l,2*l));
|
||||
|
||||
// Drop empty leading bytes
|
||||
while (!rb[0] && rb.length) rb.shift();
|
||||
while (!sb[0] && sb.length) sb.shift();
|
||||
|
||||
// If high bit is set, prepend an extra zero byte (DER signed integer)
|
||||
if (rb[0] & 0x80) rb.unshift(0);
|
||||
if (sb[0] & 0x80) sb.unshift(0);
|
||||
|
||||
var buffer = [].concat(
|
||||
0x30,
|
||||
|
||||
@@ -9,7 +9,7 @@ sjcl.bn.prototype.divRem = function (that) {
|
||||
this.initWith(0);
|
||||
return this;
|
||||
} else if (thisa.equals(thata)) {
|
||||
this.initWith(sign);
|
||||
this.initWith(1);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
30
src/js/sjcl-custom/sjcl-validecc.js
Normal file
30
src/js/sjcl-custom/sjcl-validecc.js
Normal file
@@ -0,0 +1,30 @@
|
||||
sjcl.ecc.ecdsa.secretKey.prototype = {
|
||||
sign: function(hash, paranoia) {
|
||||
var R = this._curve.r,
|
||||
l = R.bitLength(),
|
||||
k = sjcl.bn.random(R.sub(1), paranoia).add(1),
|
||||
r = this._curve.G.mult(k).x.mod(R),
|
||||
s = sjcl.bn.fromBits(hash).add(r.mul(this._exponent)).mul(k.inverseMod(R)).mod(R);
|
||||
|
||||
return sjcl.bitArray.concat(r.toBits(l), s.toBits(l));
|
||||
}
|
||||
};
|
||||
|
||||
sjcl.ecc.ecdsa.publicKey.prototype = {
|
||||
verify: function(hash, rs) {
|
||||
var w = sjcl.bitArray,
|
||||
R = this._curve.r,
|
||||
l = R.bitLength(),
|
||||
r = sjcl.bn.fromBits(w.bitSlice(rs,0,l)),
|
||||
s = sjcl.bn.fromBits(w.bitSlice(rs,l,2*l)),
|
||||
sInv = s.modInverse(R),
|
||||
hG = sjcl.bn.fromBits(hash).mul(sInv).mod(R),
|
||||
hA = r.mul(sInv).mod(R),
|
||||
r2 = this._curve.G.mult2(hG, hA, this._point).x;
|
||||
|
||||
if (r.equals(0) || s.equals(0) || r.greaterEquals(R) || s.greaterEquals(R) || !r2.equals(r)) {
|
||||
throw (new sjcl.exception.corrupt("signature didn't check out"));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
@@ -74,6 +74,7 @@ var Transaction = function (remote) {
|
||||
this.hash = undefined;
|
||||
this.submit_index = undefined; // ledger_current_index was this when transaction was submited.
|
||||
this.state = undefined; // Under construction.
|
||||
this.finalized = false;
|
||||
|
||||
this.on('success', function (message) {
|
||||
if (message.engine_result) {
|
||||
@@ -167,6 +168,26 @@ Transaction.prototype.set_state = function (state) {
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Attempts to complete the transaction for submission.
|
||||
*
|
||||
* This function seeks to fill out certain fields, such as Fee and
|
||||
* SigningPubKey, which can be determined by the library based on network
|
||||
* information and other fields.
|
||||
*/
|
||||
Transaction.prototype.complete = function () {
|
||||
var tx_json = this.tx_json;
|
||||
|
||||
if (this.remote.local_fee && undefined === tx_json.Fee) {
|
||||
tx_json.Fee = Transaction.fees['default'].to_json();
|
||||
}
|
||||
if (this.remote.local_signing && undefined === tx_json.SigningPubKey) {
|
||||
var seed = Seed.from_json(this._secret);
|
||||
var key = seed.get_key(this.tx_json.Account);
|
||||
tx_json.SigningPubKey = key.to_hex_pub();
|
||||
}
|
||||
};
|
||||
|
||||
Transaction.prototype.serialize = function () {
|
||||
return SerializedObject.from_json(this.tx_json);
|
||||
};
|
||||
@@ -181,11 +202,10 @@ Transaction.prototype.signing_hash = function () {
|
||||
|
||||
Transaction.prototype.sign = function () {
|
||||
var seed = Seed.from_json(this._secret),
|
||||
priv = seed.generate_private(this.tx_json.Account),
|
||||
hash = this.signing_hash();
|
||||
|
||||
var key = new sjcl.ecc.ecdsa.secretKey(sjcl.ecc.curves['c256'], priv.to_bn()),
|
||||
sig = key.signDER(hash.to_bits(), 0),
|
||||
var key = seed.get_key(this.tx_json.Account),
|
||||
sig = key.sign(hash, 0),
|
||||
hex = sjcl.codec.hex.fromBits(sig).toUpperCase();
|
||||
|
||||
this.tx_json.TxnSignature = hex;
|
||||
@@ -199,6 +219,7 @@ Transaction.prototype.sign = function () {
|
||||
// // status is final status. Only works under a ledger_accepting conditions.
|
||||
// switch status:
|
||||
// case 'tesSUCCESS': all is well.
|
||||
// case 'tejSecretUnknown': unable to sign transaction - secret unknown
|
||||
// case 'tejServerUntrusted': sending secret to untrusted server.
|
||||
// case 'tejInvalidAccount': locally detected error.
|
||||
// case 'tejLost': locally gave up looking
|
||||
@@ -216,14 +237,12 @@ Transaction.prototype.submit = function (callback) {
|
||||
'error' : 'tejInvalidAccount',
|
||||
'error_message' : 'Bad account.'
|
||||
});
|
||||
return;
|
||||
return this;
|
||||
}
|
||||
|
||||
// YYY Might check paths for invalid accounts.
|
||||
|
||||
if (this.remote.local_fee && undefined === tx_json.Fee) {
|
||||
tx_json.Fee = Transaction.fees['default'].to_json();
|
||||
}
|
||||
this.complete();
|
||||
|
||||
if (this.callback || this.listeners('final').length || this.listeners('lost').length || this.listeners('pending').length) {
|
||||
// There are listeners for callback, 'final', 'lost', or 'pending' arrange to emit them.
|
||||
@@ -240,15 +259,19 @@ Transaction.prototype.submit = function (callback) {
|
||||
self.remote.request_transaction_entry(self.hash)
|
||||
.ledger_hash(ledger_hash)
|
||||
.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;
|
||||
|
||||
if (self.callback)
|
||||
self.callback(message.metadata.TransactionResult, message);
|
||||
|
||||
stop = true;
|
||||
})
|
||||
.on('error', function (message) {
|
||||
if (self.finalized) return;
|
||||
|
||||
if ('remoteError' === message.error
|
||||
&& 'transactionNotFound' === message.remote.error) {
|
||||
if (self.submit_index + SUBMIT_LOST < ledger_index) {
|
||||
@@ -258,7 +281,9 @@ Transaction.prototype.submit = function (callback) {
|
||||
if (self.callback)
|
||||
self.callback('tejLost', message);
|
||||
|
||||
stop = true;
|
||||
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.
|
||||
@@ -271,11 +296,6 @@ Transaction.prototype.submit = function (callback) {
|
||||
// XXX Could log other unexpectedness.
|
||||
})
|
||||
.request();
|
||||
|
||||
if (stop) {
|
||||
self.remote.removeListener('ledger_closed', on_ledger_closed);
|
||||
self.emit('final', message);
|
||||
}
|
||||
};
|
||||
|
||||
this.remote.on('ledger_closed', on_ledger_closed);
|
||||
@@ -289,7 +309,100 @@ Transaction.prototype.submit = function (callback) {
|
||||
|
||||
this.set_state('client_submitted');
|
||||
|
||||
this.remote.submit(this);
|
||||
if (self.remote.local_sequence && !self.tx_json.Sequence) {
|
||||
self.tx_json.Sequence = this.remote.account_seq(self.tx_json.Account, 'ADVANCE');
|
||||
// console.log("Sequence: %s", self.tx_json.Sequence);
|
||||
|
||||
if (!self.tx_json.Sequence) {
|
||||
// Look in the last closed ledger.
|
||||
this.remote.account_seq_cache(self.tx_json.Account, false)
|
||||
.on('success_account_seq_cache', function () {
|
||||
// Try again.
|
||||
self.submit();
|
||||
})
|
||||
.on('error_account_seq_cache', function (message) {
|
||||
// XXX Maybe be smarter about this. Don't want to trust an untrusted server for this seq number.
|
||||
|
||||
// Look in the current ledger.
|
||||
self.remote.account_seq_cache(self.tx_json.Account, 'CURRENT')
|
||||
.on('success_account_seq_cache', function () {
|
||||
// Try again.
|
||||
self.submit();
|
||||
})
|
||||
.on('error_account_seq_cache', function (message) {
|
||||
// Forward errors.
|
||||
self.emit('error', message);
|
||||
})
|
||||
.request();
|
||||
})
|
||||
.request();
|
||||
return this;
|
||||
}
|
||||
|
||||
// If the transaction fails we want to either undo incrementing the sequence
|
||||
// or submit a noop transaction to consume the sequence remotely.
|
||||
this.on('success', function (res) {
|
||||
if (!res || "string" !== typeof res.engine_result) return;
|
||||
|
||||
switch (res.engine_result.slice(0, 3)) {
|
||||
// Synchronous local error
|
||||
case 'tej':
|
||||
self.remote.account_seq(self.tx_json.Account, 'REWIND');
|
||||
break;
|
||||
// XXX: What do we do in case of ter?
|
||||
case 'tel':
|
||||
case 'tem':
|
||||
case 'tef':
|
||||
// XXX Once we have a transaction submission manager class, we can
|
||||
// check if there are any other transactions pending. If there are,
|
||||
// we should submit a dummy transaction to ensure those
|
||||
// transactions are still valid.
|
||||
//var noop = self.remote.transaction().account_set(self.tx_json.Account);
|
||||
//noop.submit();
|
||||
|
||||
// XXX Hotfix. This only works if no other transactions are pending.
|
||||
self.remote.account_seq(self.tx_json.Account, 'REWIND');
|
||||
break;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Prepare request
|
||||
|
||||
var request = this.remote.request_submit();
|
||||
|
||||
// Forward successes and errors.
|
||||
request.on('success', function (message) {
|
||||
self.emit('success', message);
|
||||
});
|
||||
request.on('error', function (message) {
|
||||
self.emit('error', message);
|
||||
});
|
||||
|
||||
if (!this._secret && !this.tx_json.Signature) {
|
||||
this.emit('error', {
|
||||
'result' : 'tejSecretUnknown',
|
||||
'result_message' : "Could not sign transactions because we."
|
||||
});
|
||||
return this;
|
||||
} else if (this.remote.local_signing) {
|
||||
this.sign();
|
||||
request.tx_blob(this.serialize().to_hex());
|
||||
} else {
|
||||
if (!this.remote.trusted) {
|
||||
this.emit('error', {
|
||||
'result' : 'tejServerUntrusted',
|
||||
'result_message' : "Attempt to give a secret to an untrusted server."
|
||||
});
|
||||
return this;
|
||||
}
|
||||
|
||||
request.secret(this._secret);
|
||||
request.build_path(this._build_path);
|
||||
request.tx_json(this.tx_json);
|
||||
}
|
||||
|
||||
request.request();
|
||||
|
||||
return this;
|
||||
}
|
||||
@@ -319,8 +432,8 @@ Transaction.prototype.destination_tag = function (tag) {
|
||||
Transaction._path_rewrite = function (path) {
|
||||
var path_new = [];
|
||||
|
||||
for (var index in path) {
|
||||
var node = path[index];
|
||||
for (var i = 0, l = path.length; i < l; i++) {
|
||||
var node = path[i];
|
||||
var node_new = {};
|
||||
|
||||
if ('account' in node)
|
||||
@@ -339,7 +452,7 @@ Transaction._path_rewrite = function (path) {
|
||||
}
|
||||
|
||||
Transaction.prototype.path_add = function (path) {
|
||||
this.tx_json.Paths = this.tx_json.Paths || []
|
||||
this.tx_json.Paths = this.tx_json.Paths || [];
|
||||
this.tx_json.Paths.push(Transaction._path_rewrite(path));
|
||||
|
||||
return this;
|
||||
@@ -348,8 +461,8 @@ Transaction.prototype.path_add = function (path) {
|
||||
// --> paths: undefined or array of path
|
||||
// A path is an array of objects containing some combination of: account, currency, issuer
|
||||
Transaction.prototype.paths = function (paths) {
|
||||
for (var index in paths) {
|
||||
this.path_add(paths[index]);
|
||||
for (var i = 0, l = paths.length; i < l; i++) {
|
||||
this.path_add(paths[i]);
|
||||
}
|
||||
|
||||
return this;
|
||||
|
||||
@@ -20,8 +20,8 @@ var UInt = function () {
|
||||
this._value = NaN;
|
||||
};
|
||||
|
||||
UInt.json_rewrite = function (j) {
|
||||
return this.from_json(j).to_json();
|
||||
UInt.json_rewrite = function (j, opts) {
|
||||
return this.from_json(j).to_json(opts);
|
||||
};
|
||||
|
||||
// Return a new UInt from j.
|
||||
@@ -92,6 +92,10 @@ UInt.prototype.is_valid = function () {
|
||||
return this._value instanceof BigInteger;
|
||||
};
|
||||
|
||||
UInt.prototype.is_zero = function () {
|
||||
return this._value.equals(BigInteger.ZERO);
|
||||
};
|
||||
|
||||
// value = NaN on error.
|
||||
UInt.prototype.parse_generic = function (j) {
|
||||
// Canonicalize and validate
|
||||
@@ -102,14 +106,14 @@ UInt.prototype.parse_generic = function (j) {
|
||||
case undefined:
|
||||
case "0":
|
||||
case this.constructor.STR_ZERO:
|
||||
case this.constructor.ADDRESS_ZERO:
|
||||
case this.constructor.ACCOUNT_ZERO:
|
||||
case this.constructor.HEX_ZERO:
|
||||
this._value = nbi();
|
||||
break;
|
||||
|
||||
case "1":
|
||||
case this.constructor.STR_ONE:
|
||||
case this.constructor.ADDRESS_ONE:
|
||||
case this.constructor.ACCOUNT_ONE:
|
||||
case this.constructor.HEX_ONE:
|
||||
this._value = new BigInteger([1]);
|
||||
|
||||
|
||||
@@ -24,8 +24,8 @@ UInt160.width = 20;
|
||||
UInt160.prototype = extend({}, UInt.prototype);
|
||||
UInt160.prototype.constructor = UInt160;
|
||||
|
||||
var ADDRESS_ZERO = UInt160.ADDRESS_ZERO = "rrrrrrrrrrrrrrrrrrrrrhoLvTp";
|
||||
var ADDRESS_ONE = UInt160.ADDRESS_ONE = "rrrrrrrrrrrrrrrrrrrrBZbvji";
|
||||
var ACCOUNT_ZERO = UInt160.ACCOUNT_ZERO = "rrrrrrrrrrrrrrrrrrrrrhoLvTp";
|
||||
var ACCOUNT_ONE = UInt160.ACCOUNT_ONE = "rrrrrrrrrrrrrrrrrrrrBZbvji";
|
||||
var HEX_ZERO = UInt160.HEX_ZERO = "0000000000000000000000000000000000000000";
|
||||
var HEX_ONE = UInt160.HEX_ONE = "0000000000000000000000000000000000000001";
|
||||
var STR_ZERO = UInt160.STR_ZERO = utils.hexToString(HEX_ZERO);
|
||||
@@ -59,8 +59,8 @@ UInt160.prototype.to_json = function (opts) {
|
||||
|
||||
var output = Base.encode_check(Base.VER_ACCOUNT_ID, this.to_bytes());
|
||||
|
||||
if (config.gateways && output in config.gateways && !opts.no_gateway)
|
||||
output = config.gateways[output];
|
||||
if (opts.gateways && output in opts.gateways)
|
||||
output = opts.gateways[output];
|
||||
|
||||
return output;
|
||||
};
|
||||
|
||||
@@ -24,9 +24,6 @@ UInt256.width = 32;
|
||||
UInt256.prototype = extend({}, UInt.prototype);
|
||||
UInt256.prototype.constructor = UInt256;
|
||||
|
||||
// XXX Generate these constants (or remove them)
|
||||
var ADDRESS_ZERO = UInt256.ADDRESS_ZERO = "XXX";
|
||||
var ADDRESS_ONE = UInt256.ADDRESS_ONE = "XXX";
|
||||
var HEX_ZERO = UInt256.HEX_ZERO = "00000000000000000000000000000000" +
|
||||
"00000000000000000000000000000000";
|
||||
var HEX_ONE = UInt256.HEX_ONE = "00000000000000000000000000000000" +
|
||||
|
||||
Reference in New Issue
Block a user