mirror of
https://github.com/Xahau/xahau.js.git
synced 2026-08-22 16:10:52 +00:00
Merge branch 'develop' of https://github.com/ripple/ripple-lib into develop
This commit is contained in:
@@ -41,6 +41,7 @@ module.exports = function(grunt) {
|
||||
"src/js/sjcl-custom/sjcl-extramath.js",
|
||||
"src/js/sjcl-custom/sjcl-montgomery.js",
|
||||
"src/js/sjcl-custom/sjcl-validecc.js",
|
||||
"src/js/sjcl-custom/sjcl-ecdsa-canonical.js",
|
||||
"src/js/sjcl-custom/sjcl-ecdsa-der.js",
|
||||
"src/js/sjcl-custom/sjcl-jacobi.js"
|
||||
],
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
#The Ripple JavaScript Library
|
||||
|
||||
[](https://www.npmjs.org/package/ripple-lib)
|
||||
|
||||
`ripple-lib` connects to the Ripple network via the WebSocket protocol and runs in Node.js as well as in the browser.
|
||||
|
||||
**Use ripple-lib for**
|
||||
|
||||
@@ -4043,6 +4043,24 @@ sjcl.ecc.ecdsa.publicKey.prototype = {
|
||||
}
|
||||
};
|
||||
|
||||
sjcl.ecc.ecdsa.secretKey.prototype.canonicalizeSignature = function(rs) {
|
||||
var w = sjcl.bitArray,
|
||||
R = this._curve.r,
|
||||
l = R.bitLength();
|
||||
|
||||
var r = sjcl.bn.fromBits(w.bitSlice(rs,0,l)),
|
||||
s = sjcl.bn.fromBits(w.bitSlice(rs,l,2*l));
|
||||
|
||||
// For a canonical signature we want the lower of two possible values for s
|
||||
// 0 < s <= n/2
|
||||
if (!R.copy().halveM().greaterEquals(s)) {
|
||||
s = R.sub(s);
|
||||
}
|
||||
|
||||
return w.concat(r.toBits(l), s.toBits(l));
|
||||
};
|
||||
|
||||
|
||||
sjcl.ecc.ecdsa.secretKey.prototype.signDER = function(hash, paranoia) {
|
||||
return this.encodeDER(this.sign(hash, paranoia));
|
||||
};
|
||||
|
||||
@@ -71,8 +71,8 @@ Amount.from_json = function (j) {
|
||||
return (new Amount()).parse_json(j);
|
||||
};
|
||||
|
||||
Amount.from_quality = function (q, c, i) {
|
||||
return (new Amount()).parse_quality(q, c, i);
|
||||
Amount.from_quality = function (quality, currency, issuer, opts) {
|
||||
return (new Amount()).parse_quality(quality, currency, issuer, opts);
|
||||
};
|
||||
|
||||
Amount.from_human = function (j, opts) {
|
||||
@@ -159,6 +159,29 @@ Amount.prototype.add = function (v) {
|
||||
return result;
|
||||
};
|
||||
|
||||
/**
|
||||
* Turn this amount into its inverse.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
Amount.prototype._invert = function () {
|
||||
this._value = consts.bi_1e32.divide(this._value);
|
||||
this._offset = -32 - this._offset;
|
||||
this.canonicalize();
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Return the inverse of this amount.
|
||||
*
|
||||
* @return {Amount} New Amount object with same currency and issuer, but the
|
||||
* inverse of the value.
|
||||
*/
|
||||
Amount.prototype.invert = function () {
|
||||
return this.copy()._invert();
|
||||
};
|
||||
|
||||
Amount.prototype.canonicalize = function () {
|
||||
if (!(this._value instanceof BigInteger)) {
|
||||
// NaN.
|
||||
@@ -349,9 +372,14 @@ Amount.prototype.divide = function (d) {
|
||||
*
|
||||
* @this {Amount} The numerator (top half) of the fraction.
|
||||
* @param {Amount} denominator The denominator (bottom half) of the fraction.
|
||||
* @param opts Options for the calculation.
|
||||
* @param opts.reference_date {Date|Number} Date based on which demurrage/interest
|
||||
* should be applied. Can be given as JavaScript Date or int for Ripple epoch.
|
||||
* @return {Amount} The resulting ratio. Unit will be the same as numerator.
|
||||
*/
|
||||
Amount.prototype.ratio_human = function (denominator) {
|
||||
Amount.prototype.ratio_human = function (denominator, opts) {
|
||||
opts = opts || {};
|
||||
|
||||
if (typeof denominator === 'number' && parseInt(denominator, 10) === denominator) {
|
||||
// Special handling of integer arguments
|
||||
denominator = Amount.from_json('' + denominator + '.0');
|
||||
@@ -367,6 +395,14 @@ Amount.prototype.ratio_human = function (denominator) {
|
||||
return Amount.NaN();
|
||||
}
|
||||
|
||||
// Apply interest/demurrage
|
||||
//
|
||||
// We only need to apply it to the second factor, because the currency unit of
|
||||
// the first factor will carry over into the result.
|
||||
if (opts.reference_date) {
|
||||
denominator = denominator.applyInterest(opts.reference_date);
|
||||
}
|
||||
|
||||
// Special case: The denominator is a native (XRP) amount.
|
||||
//
|
||||
// In that case, it's going to be expressed as base units (1 XRP =
|
||||
@@ -402,9 +438,14 @@ Amount.prototype.ratio_human = function (denominator) {
|
||||
*
|
||||
* @this {Amount} The first factor of the product.
|
||||
* @param {Amount} factor The second factor of the product.
|
||||
* @param opts Options for the calculation.
|
||||
* @param opts.reference_date {Date|Number} Date based on which demurrage/interest
|
||||
* should be applied. Can be given as JavaScript Date or int for Ripple epoch.
|
||||
* @return {Amount} The product. Unit will be the same as the first factor.
|
||||
*/
|
||||
Amount.prototype.product_human = function (factor) {
|
||||
Amount.prototype.product_human = function (factor, opts) {
|
||||
opts = opts || {};
|
||||
|
||||
if (typeof factor === 'number' && parseInt(factor, 10) === factor) {
|
||||
// Special handling of integer arguments
|
||||
factor = Amount.from_json(String(factor) + '.0');
|
||||
@@ -417,6 +458,14 @@ Amount.prototype.product_human = function (factor) {
|
||||
return Amount.NaN();
|
||||
}
|
||||
|
||||
// Apply interest/demurrage
|
||||
//
|
||||
// We only need to apply it to the second factor, because the currency unit of
|
||||
// the first factor will carry over into the result.
|
||||
if (opts.reference_date) {
|
||||
factor = factor.applyInterest(opts.reference_date);
|
||||
}
|
||||
|
||||
var product = this.multiply(factor);
|
||||
|
||||
// Special case: The second factor is a native (XRP) amount expressed as base
|
||||
@@ -618,17 +667,81 @@ Amount.prototype.parse_issuer = function (issuer) {
|
||||
return this;
|
||||
};
|
||||
|
||||
// --> h: 8 hex bytes quality or 32 hex bytes directory index.
|
||||
Amount.prototype.parse_quality = function (q, c, i) {
|
||||
/**
|
||||
* Decode a price from a BookDirectory index.
|
||||
*
|
||||
* BookDirectory ledger entries each encode the offer price in their index. This
|
||||
* method can decode that information and populate an Amount object with it.
|
||||
*
|
||||
* It is possible not to provide a currency or issuer, but be aware that Amount
|
||||
* objects behave differently based on the currency, so you may get incorrect
|
||||
* results.
|
||||
*
|
||||
* Prices involving demurraging currencies are tricky, since they depend on the
|
||||
* base and counter currencies.
|
||||
*
|
||||
* @param quality {String} 8 hex bytes quality or 32 hex bytes BookDirectory
|
||||
* index.
|
||||
* @param counterCurrency {Currency|String} Currency of the resulting Amount
|
||||
* object.
|
||||
* @param counterIssuer {Issuer|String} Issuer of the resulting Amount object.
|
||||
* @param opts Additional options
|
||||
* @param opts.inverse {Boolean} If true, return the inverse of the price
|
||||
* encoded in the quality.
|
||||
* @param opts.base_currency {Currency|String} The other currency. This plays a
|
||||
* role with interest-bearing or demurrage currencies. In that case the
|
||||
* demurrage has to be applied when the quality is decoded, otherwise the
|
||||
* price will be false.
|
||||
* @param opts.reference_date {Date|Number} Date based on which demurrage/interest
|
||||
* should be applied. Can be given as JavaScript Date or int for Ripple epoch.
|
||||
* @param opts.xrp_as_drops {Boolean} Whether XRP amount should be treated as
|
||||
* drops. When the base currency is XRP, the quality is calculated in drops.
|
||||
* For human use however, we want to think of 1000000 drops as 1 XRP and
|
||||
* prices as per-XRP instead of per-drop.
|
||||
*/
|
||||
Amount.prototype.parse_quality = function (quality, counterCurrency, counterIssuer, opts)
|
||||
{
|
||||
opts = opts || {};
|
||||
|
||||
var baseCurrency = Currency.from_json(opts.base_currency);
|
||||
|
||||
this._is_negative = false;
|
||||
this._value = new BigInteger(q.substring(q.length-14), 16);
|
||||
this._offset = parseInt(q.substring(q.length-16, q.length-14), 16)-100;
|
||||
this._currency = Currency.from_json(c);
|
||||
this._issuer = UInt160.from_json(i);
|
||||
this._value = new BigInteger(quality.substring(quality.length-14), 16);
|
||||
this._offset = parseInt(quality.substring(quality.length-16, quality.length-14), 16)-100;
|
||||
this._currency = Currency.from_json(counterCurrency);
|
||||
this._issuer = UInt160.from_json(counterIssuer);
|
||||
this._is_native = this._currency.is_native();
|
||||
|
||||
// Correct offset if xrp_as_drops option is not set and base currency is XRP
|
||||
if (!opts.xrp_as_drops &&
|
||||
baseCurrency.is_valid() &&
|
||||
baseCurrency.is_native()) {
|
||||
if (opts.inverse) {
|
||||
this._offset -= 6;
|
||||
} else {
|
||||
this._offset += 6;
|
||||
}
|
||||
}
|
||||
|
||||
if (opts.inverse) {
|
||||
this._invert();
|
||||
}
|
||||
|
||||
this.canonicalize();
|
||||
|
||||
if (opts.reference_date && baseCurrency.is_valid() && baseCurrency.has_interest()) {
|
||||
var interest = baseCurrency.get_interest_at(opts.reference_date);
|
||||
|
||||
// XXX If we had better math utilities, we wouldn't need this hack.
|
||||
var interestTempAmount = Amount.from_json(""+interest+"/1/1");
|
||||
|
||||
if (interestTempAmount.is_valid()) {
|
||||
var v = this.divide(interestTempAmount);
|
||||
this._value = v._value;
|
||||
this._offset = v._offset;
|
||||
}
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -850,6 +963,39 @@ Amount.prototype.to_text = function (allow_nan) {
|
||||
return result;
|
||||
};
|
||||
|
||||
/**
|
||||
* Calculate present value based on currency and a reference date.
|
||||
*
|
||||
* This only affects demurraging and interest-bearing currencies.
|
||||
*
|
||||
* User should not store amount objects after the interest is applied. This is
|
||||
* intended by display functions such as toHuman().
|
||||
*
|
||||
* @param referenceDate {Date|Number} Date based on which demurrage/interest
|
||||
* should be applied. Can be given as JavaScript Date or int for Ripple epoch.
|
||||
* @return {Amount} The amount with interest applied.
|
||||
*/
|
||||
Amount.prototype.applyInterest = function (referenceDate) {
|
||||
if (this._currency.has_interest()) {
|
||||
var interest = this._currency.get_interest_at(referenceDate);
|
||||
|
||||
// XXX Because the Amount parsing routines don't support some of the things
|
||||
// that JavaScript can output when casting a float to a string, the
|
||||
// following call sometimes does not produce a valid Amount.
|
||||
//
|
||||
// The correct way to solve this is probably to switch to a proper
|
||||
// BigDecimal for our internal representation and then use that across
|
||||
// the board instead of instantiating these dummy Amount objects.
|
||||
var interestTempAmount = Amount.from_json(""+interest+"/1/1");
|
||||
|
||||
if (interestTempAmount.is_valid()) {
|
||||
return this.multiply(interestTempAmount);
|
||||
}
|
||||
} else {
|
||||
return this;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Format only value in a human-readable format.
|
||||
*
|
||||
@@ -885,21 +1031,8 @@ Amount.prototype.to_human = function (opts) {
|
||||
|
||||
// Apply demurrage/interest
|
||||
var ref = this;
|
||||
if (opts.reference_date && this._currency.has_interest()) {
|
||||
var interest = this._currency.get_interest_at(opts.reference_date);
|
||||
|
||||
// XXX Because the Amount parsing routines don't support some of the things
|
||||
// that JavaScript can output when casting a float to a string, the
|
||||
// following call sometimes does not produce a valid Amount.
|
||||
//
|
||||
// The correct way to solve this is probably to switch to a proper
|
||||
// BigDecimal for our internal representation and then use that across
|
||||
// the board instead of instantiating these dummy Amount objects.
|
||||
var interestTempAmount = Amount.from_json(""+interest+"/1/1");
|
||||
|
||||
if (interestTempAmount.is_valid()) {
|
||||
ref = this.multiply(interestTempAmount);
|
||||
}
|
||||
if (opts.reference_date) {
|
||||
ref = this.applyInterest(opts.reference_date);
|
||||
}
|
||||
|
||||
var order = ref._is_native ? consts.xns_precision : -ref._offset;
|
||||
|
||||
@@ -89,8 +89,10 @@ KeyPair.prototype.get_address = function () {
|
||||
};
|
||||
|
||||
KeyPair.prototype.sign = function (hash) {
|
||||
var hash = UInt256.from_json(hash);
|
||||
return this._secret.signDER(hash.to_bits(), 0);
|
||||
hash = UInt256.from_json(hash);
|
||||
var sig = this._secret.sign(hash.to_bits(), 0);
|
||||
sig = this._secret.canonicalizeSignature(sig);
|
||||
return this._secret.encodeDER(sig);
|
||||
};
|
||||
|
||||
exports.KeyPair = KeyPair;
|
||||
|
||||
@@ -225,11 +225,12 @@ OrderBook.prototype.notify = function (message) {
|
||||
break;
|
||||
|
||||
case 'CreatedNode':
|
||||
var price = Amount.from_json(an.fields.TakerPays).ratio_human(an.fields.TakerGets);
|
||||
// XXX Should use Amount#from_quality
|
||||
var price = Amount.from_json(an.fields.TakerPays).ratio_human(an.fields.TakerGets, {reference_date: new Date()});
|
||||
|
||||
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);
|
||||
var priceItem = Amount.from_json(offer.TakerPays).ratio_human(offer.TakerGets, {reference_date: new Date()});
|
||||
|
||||
if (price.compareTo(priceItem) <= 0) {
|
||||
var obj = an.fields;
|
||||
|
||||
@@ -36,8 +36,17 @@ function Server(remote, opts) {
|
||||
throw new Error('Server host is malformed, use "host" and "port" server configuration');
|
||||
}
|
||||
|
||||
if (typeof opts.port !== 'number') {
|
||||
throw new TypeError('Server configuration "port" is not a Number');
|
||||
// We want to allow integer strings as valid port numbers for backward
|
||||
// compatibility.
|
||||
if (typeof opts.port === 'string') {
|
||||
opts.port = parseFloat(opts.port);
|
||||
}
|
||||
|
||||
if (typeof opts.port !== 'number' ||
|
||||
opts.port >>> 0 !== parseFloat(opts.port) || // is integer?
|
||||
opts.port < 1 ||
|
||||
opts.port > 65535) {
|
||||
throw new TypeError('Server "port" must be an integer in range 1-65535');
|
||||
}
|
||||
|
||||
if (typeof opts.secure !== 'boolean') {
|
||||
|
||||
@@ -73,22 +73,19 @@ function Transaction(remote) {
|
||||
// Index at which transaction was submitted
|
||||
this.submitIndex = void(0);
|
||||
|
||||
this.canonical = true;
|
||||
|
||||
// We aren't clever enough to eschew preventative measures so we keep an array
|
||||
// of all submitted transactionIDs (which can change due to load_factor
|
||||
// effecting the Fee amount). This should be populated with a transactionID
|
||||
// any time it goes on the network
|
||||
this.submittedIDs = [ ]
|
||||
this.submittedIDs = [ ];
|
||||
|
||||
function finalize(message) {
|
||||
if (self.result) {
|
||||
self.result.ledger_index = message.ledger_index;
|
||||
self.result.ledger_hash = message.ledger_hash;
|
||||
} else {
|
||||
self.result = message;
|
||||
self.result.tx_json = self.tx_json;
|
||||
if (!self.finalized) {
|
||||
self.finalized = true;
|
||||
self.emit('cleanup', message);
|
||||
}
|
||||
|
||||
self.emit('cleanup', message);
|
||||
};
|
||||
|
||||
this.once('success', function(message) {
|
||||
@@ -120,6 +117,11 @@ Transaction.fee_units = {
|
||||
};
|
||||
|
||||
Transaction.flags = {
|
||||
// Universal flags can apply to any transaction type
|
||||
Universal: {
|
||||
FullyCanonicalSig: 0x80000000
|
||||
},
|
||||
|
||||
AccountSet: {
|
||||
RequireDestTag: 0x00010000,
|
||||
OptionalDestTag: 0x00020000,
|
||||
@@ -278,6 +280,15 @@ Transaction.prototype.complete = function() {
|
||||
this.tx_json.SigningPubKey = key.to_hex_pub();
|
||||
}
|
||||
|
||||
// Set canonical flag - this enables canonicalized signature checking
|
||||
if (this.canonical) {
|
||||
this.tx_json.Flags |= Transaction.flags.Universal.FullyCanonicalSig;
|
||||
|
||||
// JavaScript converts operands to 32-bit signed ints before doing bitwise
|
||||
// operations. We need to convert it back to an unsigned int.
|
||||
this.tx_json.Flags = this.tx_json.Flags >>> 0;
|
||||
}
|
||||
|
||||
return this.tx_json;
|
||||
};
|
||||
|
||||
|
||||
17
src/js/sjcl-custom/sjcl-ecdsa-canonical.js
Normal file
17
src/js/sjcl-custom/sjcl-ecdsa-canonical.js
Normal file
@@ -0,0 +1,17 @@
|
||||
sjcl.ecc.ecdsa.secretKey.prototype.canonicalizeSignature = function(rs) {
|
||||
var w = sjcl.bitArray,
|
||||
R = this._curve.r,
|
||||
l = R.bitLength();
|
||||
|
||||
var r = sjcl.bn.fromBits(w.bitSlice(rs,0,l)),
|
||||
s = sjcl.bn.fromBits(w.bitSlice(rs,l,2*l));
|
||||
|
||||
// For a canonical signature we want the lower of two possible values for s
|
||||
// 0 < s <= n/2
|
||||
if (!R.copy().halveM().greaterEquals(s)) {
|
||||
s = R.sub(s);
|
||||
}
|
||||
|
||||
return w.concat(r.toBits(l), s.toBits(l));
|
||||
};
|
||||
|
||||
@@ -431,4 +431,130 @@ describe('Amount', function() {
|
||||
assert.strictEqual(a.not_equals_why(b), 'Native mismatch.');
|
||||
});
|
||||
});
|
||||
|
||||
describe('product_human', function() {
|
||||
it('Multiply 0 XRP with 0 XRP', function () {
|
||||
assert.strictEqual('0/XRP', Amount.from_json('0').product_human(Amount.from_json('0')).to_text_full());
|
||||
});
|
||||
it('Multiply 0 USD with 0 XRP', function () {
|
||||
assert.strictEqual('0/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh', Amount.from_json('0/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh').product_human(Amount.from_json('0')).to_text_full());
|
||||
});
|
||||
it('Multiply 0 XRP with 0 USD', function () {
|
||||
assert.strictEqual('0/XRP', Amount.from_json('0').product_human(Amount.from_json('0/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh')).to_text_full());
|
||||
});
|
||||
it('Multiply 1 XRP with 0 XRP', function () {
|
||||
assert.strictEqual('0/XRP', Amount.from_json('1').product_human(Amount.from_json('0')).to_text_full());
|
||||
});
|
||||
it('Multiply 1 USD with 0 XRP', function () {
|
||||
assert.strictEqual('0/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh', Amount.from_json('1/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh').product_human(Amount.from_json('0')).to_text_full());
|
||||
});
|
||||
it('Multiply 1 XRP with 0 USD', function () {
|
||||
assert.strictEqual('0/XRP', Amount.from_json('1').product_human(Amount.from_json('0/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh')).to_text_full());
|
||||
});
|
||||
it('Multiply 0 XRP with 1 XRP', function () {
|
||||
assert.strictEqual('0/XRP', Amount.from_json('0').product_human(Amount.from_json('1')).to_text_full());
|
||||
});
|
||||
it('Multiply 0 USD with 1 XRP', function () {
|
||||
assert.strictEqual('0/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh', Amount.from_json('0/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh').product_human(Amount.from_json('1')).to_text_full());
|
||||
});
|
||||
it('Multiply 0 XRP with 1 USD', function () {
|
||||
assert.strictEqual('0/XRP', Amount.from_json('0').product_human(Amount.from_json('1/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh')).to_text_full());
|
||||
});
|
||||
it('Multiply XRP with USD', function () {
|
||||
assert.equal('0.002/XRP', Amount.from_json('200').product_human(Amount.from_json('10/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh')).to_text_full());
|
||||
});
|
||||
it('Multiply XRP with USD', function () {
|
||||
assert.strictEqual('0.2/XRP', Amount.from_json('20000').product_human(Amount.from_json('10/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh')).to_text_full());
|
||||
});
|
||||
it('Multiply XRP with USD', function () {
|
||||
assert.strictEqual('20/XRP', Amount.from_json('2000000').product_human(Amount.from_json('10/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh')).to_text_full());
|
||||
});
|
||||
it('Multiply XRP with USD, neg', function () {
|
||||
assert.strictEqual('-0.002/XRP', Amount.from_json('200').product_human(Amount.from_json('-10/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh')).to_text_full());
|
||||
});
|
||||
it('Multiply XRP with USD, neg, frac', function () {
|
||||
assert.strictEqual('-0.222/XRP', Amount.from_json('-6000').product_human(Amount.from_json('37/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh')).to_text_full());
|
||||
});
|
||||
it('Multiply USD with USD', function () {
|
||||
assert.strictEqual('20000/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh', Amount.from_json('2000/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh').product_human(Amount.from_json('10/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh')).to_text_full());
|
||||
});
|
||||
it('Multiply USD with USD', function () {
|
||||
assert.strictEqual('200000000000/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh', Amount.from_json('2000000/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh').product_human(Amount.from_json('100000/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh')).to_text_full());
|
||||
});
|
||||
it('Multiply EUR with USD, result < 1', function () {
|
||||
assert.strictEqual('100000/EUR/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh', Amount.from_json('100/EUR/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh').product_human(Amount.from_json('1000/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh')).to_text_full());
|
||||
});
|
||||
it('Multiply EUR with USD, neg', function () {
|
||||
assert.strictEqual(Amount.from_json('-24000/EUR/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh').product_human(Amount.from_json('2000/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh')).to_text_full(), '-48000000/EUR/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh');
|
||||
});
|
||||
it('Multiply EUR with USD, neg, <1', function () {
|
||||
assert.strictEqual(Amount.from_json('0.1/EUR/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh').product_human(Amount.from_json('-1000/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh')).to_text_full(), '-100/EUR/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh');
|
||||
});
|
||||
it('Multiply EUR with XRP, factor < 1', function () {
|
||||
assert.strictEqual(Amount.from_json('0.05/EUR/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh').product_human(Amount.from_json('2000')).to_text_full(), '0.0001/EUR/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh');
|
||||
});
|
||||
it('Multiply EUR with XRP, neg', function () {
|
||||
assert.strictEqual(Amount.from_json('-100/EUR/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh').product_human(Amount.from_json('5')).to_text_full(), '-0.0005/EUR/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh');
|
||||
});
|
||||
it('Multiply EUR with XRP, neg, <1', function () {
|
||||
assert.strictEqual(Amount.from_json('-0.05/EUR/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh').product_human(Amount.from_json('2000')).to_text_full(), '-0.0001/EUR/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh');
|
||||
});
|
||||
it('Multiply XRP with XRP', function () {
|
||||
assert.strictEqual(Amount.from_json('10000000').product_human(Amount.from_json('10')).to_text_full(), '0.0001/XRP');
|
||||
});
|
||||
it('Multiply USD with XAU (dem)', function () {
|
||||
assert.strictEqual(Amount.from_json('2000/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh').product_human(Amount.from_json('10/015841551A748AD2C1F76FF6ECB0CCCD00000000/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh'), {reference_date: 443845330 + 31535000}).to_text_full(), '19900.00316303882/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh');
|
||||
});
|
||||
});
|
||||
|
||||
describe('ratio_human', function() {
|
||||
it('Divide USD by XAU (dem)', function () {
|
||||
assert.strictEqual(Amount.from_json('2000/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh').ratio_human(Amount.from_json('10/015841551A748AD2C1F76FF6ECB0CCCD00000000/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh'), {reference_date: 443845330 + 31535000}).to_text_full(), '201.0049931765529/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh');
|
||||
});
|
||||
});
|
||||
|
||||
describe('_invert', function() {
|
||||
it('Invert 1', function () {
|
||||
assert.strictEqual(Amount.from_json('1/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh').invert().to_text_full(), '1/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh');
|
||||
});
|
||||
it('Invert 20', function () {
|
||||
assert.strictEqual(Amount.from_json('20/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh').invert().to_text_full(), '0.05/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh');
|
||||
});
|
||||
it('Invert 0.02', function () {
|
||||
assert.strictEqual(Amount.from_json('0.02/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh').invert().to_text_full(), '50/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh');
|
||||
});
|
||||
});
|
||||
|
||||
describe('from_quality', function() {
|
||||
it('BTC/XRP', function () {
|
||||
assert.strictEqual(Amount.from_quality('7B73A610A009249B0CC0D4311E8BA7927B5A34D86634581C5F0FF9FF678E1000', 'XRP', NaN, {base_currency: 'BTC'}).to_text_full(), '44,970/XRP');
|
||||
});
|
||||
it('BTC/XRP inverse', function () {
|
||||
assert.strictEqual(Amount.from_quality('37AAC93D336021AE94310D0430FFA090F7137C97D473488C4A0918D0DEF8624E', 'XRP', NaN, {inverse: true, base_currency: 'BTC'}).to_text_full(), '39,053.954453/XRP');
|
||||
});
|
||||
it('XRP/USD', function () {
|
||||
assert.strictEqual(Amount.from_quality('DFA3B6DDAB58C7E8E5D944E736DA4B7046C30E4F460FD9DE4D05DCAA8FE12000', 'USD', 'rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B', {base_currency: 'XRP'}).to_text_full(), '0.0165/USD/rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B');
|
||||
});
|
||||
it('XRP/USD inverse', function () {
|
||||
assert.strictEqual(Amount.from_quality('4627DFFCFF8B5A265EDBD8AE8C14A52325DBFEDAF4F5C32E5C22A840E27DCA9B', 'USD', 'rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B', {inverse: true, base_currency: 'XRP'}).to_text_full(), '0.010251/USD/rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B');
|
||||
});
|
||||
it('BTC/USD', function () {
|
||||
assert.strictEqual(Amount.from_quality('6EAB7C172DEFA430DBFAD120FDC373B5F5AF8B191649EC9858038D7EA4C68000', 'USD', 'rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B', {base_currency: 'BTC'}).to_text_full(), '1000/USD/rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B');
|
||||
});
|
||||
it('BTC/USD inverse', function () {
|
||||
assert.strictEqual(Amount.from_quality('20294C923E80A51B487EB9547B3835FD483748B170D2D0A455071AFD498D0000', 'USD', 'rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B', {inverse: true, base_currency: 'BTC'}).to_text_full(), '0.5/USD/rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B');
|
||||
});
|
||||
it('XAU(dem)/XRP', function () {
|
||||
assert.strictEqual(Amount.from_quality('587322CCBDE0ABD01704769A73A077C32FB39057D813D4165F1FF973CAF997EF', 'XRP', NaN, {base_currency: '015841551A748AD2C1F76FF6ECB0CCCD00000000', reference_date: 443845330 + 31535000}).to_text_full(), '90,452.246928/XRP');
|
||||
});
|
||||
it('XAU(dem)/XRP inverse', function () {
|
||||
assert.strictEqual(Amount.from_quality('F72C7A9EAE4A45ED1FB547AD037D07B9B965C6E662BEBAFA4A03F2A976804235', 'XRP', NaN, {inverse: true, base_currency: '015841551A748AD2C1F76FF6ECB0CCCD00000000', reference_date: 443845330 + 31535000}).to_text_full(), '90,442.196677/XRP');
|
||||
});
|
||||
it('USD/XAU(dem)', function () {
|
||||
assert.strictEqual(Amount.from_quality('4743E58E44974B325D42FD2BB683A6E36950F350EE46DD3A521B644B99782F5F', '015841551A748AD2C1F76FF6ECB0CCCD00000000', 'rUyPiNcSFFj6uMR2gEaD8jUerQ59G1qvwN', {base_currency: 'USD', reference_date: 443845330 + 31535000}).to_text_full(), '0.007710100231303007/015841551A748AD2C1F76FF6ECB0CCCD00000000/rUyPiNcSFFj6uMR2gEaD8jUerQ59G1qvwN');
|
||||
});
|
||||
it('USD/XAU(dem) inverse', function () {
|
||||
assert.strictEqual(Amount.from_quality('CDFD3AFB2F8C5DBEF75B081F7C957FF5509563266F28F36C5704A0FB0BAD8800', '015841551A748AD2C1F76FF6ECB0CCCD00000000', 'rUyPiNcSFFj6uMR2gEaD8jUerQ59G1qvwN', {inverse: true, base_currency: 'USD', reference_date: 443845330 + 31535000}).to_text_full(), '0.007675186123263489/015841551A748AD2C1F76FF6ECB0CCCD00000000/rUyPiNcSFFj6uMR2gEaD8jUerQ59G1qvwN');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
28
test/sjcl-ecdsa-canonical-test.js
Normal file
28
test/sjcl-ecdsa-canonical-test.js
Normal file
@@ -0,0 +1,28 @@
|
||||
var assert = require('assert');
|
||||
var utils = require('./testutils');
|
||||
var sjcl = require('../build/sjcl');
|
||||
var Seed = require('../src/js/ripple/seed').Seed;
|
||||
|
||||
describe('SJCL ECDSA Canonicalization', function() {
|
||||
describe('canonicalizeSignature', function() {
|
||||
it('should canonicalize non-canonical signatures', function () {
|
||||
var seed = Seed.from_json('saESc82Vun7Ta5EJRzGJbrXb5HNYk');
|
||||
var key = seed.get_key('rBZ4j6MsoctipM6GEyHSjQKzXG3yambDnZ');
|
||||
|
||||
var rs = sjcl.codec.hex.toBits("27ce1b914045ba7e8c11a2f2882cb6e07a19d4017513f12e3e363d71dc3fff0fb0a0747ecc7b4ca46e45b3b32b6b2a066aa0249c027ef11e5bce93dab756549c");
|
||||
rs = sjcl.ecc.ecdsa.secretKey.prototype.canonicalizeSignature.call(key._secret, rs);
|
||||
assert.strictEqual(sjcl.codec.hex.fromBits(rs), "27ce1b914045ba7e8c11a2f2882cb6e07a19d4017513f12e3e363d71dc3fff0f4f5f8b813384b35b91ba4c4cd494d5f8500eb84aacc9af1d6403cab218dfeca5");
|
||||
});
|
||||
|
||||
it('should not touch canonical signatures', function () {
|
||||
var seed = Seed.from_json('saESc82Vun7Ta5EJRzGJbrXb5HNYk');
|
||||
var key = seed.get_key('rBZ4j6MsoctipM6GEyHSjQKzXG3yambDnZ');
|
||||
|
||||
var rs = sjcl.codec.hex.toBits("5c32bc2b4d34e27af9fb66eeea0f47f6afb3d433658af0f649ebae7b872471ab7d23860688aaf9d8131f84cfffa6c56bf9c32fd8b315b2ef9d6bcb243f7a686c");
|
||||
rs = sjcl.ecc.ecdsa.secretKey.prototype.canonicalizeSignature.call(key._secret, rs);
|
||||
assert.strictEqual(sjcl.codec.hex.fromBits(rs), "5c32bc2b4d34e27af9fb66eeea0f47f6afb3d433658af0f649ebae7b872471ab7d23860688aaf9d8131f84cfffa6c56bf9c32fd8b315b2ef9d6bcb243f7a686c");
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// vim:sw=2:sts=2:ts=8:et
|
||||
Reference in New Issue
Block a user