Merge branch 'master' of github.com:jedmccaleb/NewCoin

This commit is contained in:
jed
2012-10-11 22:42:29 -07:00
22 changed files with 1220 additions and 865 deletions

View File

@@ -1,7 +1,10 @@
// Represent Newcoin amounts and currencies.
// - Numbers in hex are big-endian.
var utils = require('./utils.js');
var jsbn = require('./jsbn.js');
var utils = require("./utils.js");
var BigInteger = jsbn.BigInteger;
var UInt160 = function () {
// Internal form:
@@ -9,7 +12,11 @@ var UInt160 = function () {
// XXX Should standardize on 'i' format or 20 format.
};
// Returns NaN on error.
UInt160.from_json = function (j) {
return (new UInt160()).parse_json(j);
};
// value === NaN on error.
UInt160.prototype.parse_json = function (j) {
// Canonicalize and validate
@@ -49,7 +56,7 @@ UInt160.prototype.parse_json = function (j) {
}
}
return this.value;
return this;
};
// Convert from internal form.
@@ -62,7 +69,7 @@ UInt160.prototype.to_json = function () {
{
return exports.consts.hex_one;
}
else if (20 === this.value.length) {
else if ('string' === typeof this.value && 20 === this.value.length) {
return utils.stringToHex(this.value);
}
else
@@ -81,7 +88,11 @@ var Currency = function () {
// XXX Should support hex, C++ doesn't currently allow it.
}
// Returns NaN on error.
Currency.from_json = function (j) {
return (new Currency()).parse_json(j);
};
// this.value === NaN on error.
Currency.prototype.parse_json = function (j) {
if ("" === j || "0" === j || "XNS" === j) {
this.value = 0;
@@ -93,15 +104,15 @@ Currency.prototype.parse_json = function (j) {
this.value = j;
}
return this.value;
return this;
};
Currency.prototype.to_json = function () {
return this.value ? this.value : 'XNS';
return this.value ? this.value : "XNS";
};
Currency.prototype.to_human = function() {
return this.value ? this.value : 'XNS';
return this.value ? this.value : "XNS";
};
var Amount = function () {
@@ -109,19 +120,86 @@ var Amount = function () {
// integer : XNS
// { 'value' : ..., 'currency' : ..., 'issuer' : ...}
this.value = 0;
this.offset = 0;
this.is_native = false;
this.is_negative = false;
this.value = new BigInteger(); // NaN for bad value. Always positive for non-XNS.
this.offset = undefined; // For non-XNS.
this.is_native = true; // Default to XNS. Only valid if value is not NaN.
this.is_negative = undefined; // Undefined for XNS.
this.currency = new Currency();
this.issuer = new UInt160();
};
// Convert only value to JSON text.
Amount.prototype.to_text = function() {
// XXX Needs to work for native and non-native.
return this.is_negative ? -this.value : this.value; // XXX Use biginteger.
Amount.from_json = function (j) {
return (new Amount()).parse_json(j);
};
// YYY Might also check range.
Amount.prototype.is_valid = function() {
return NaN !== this.value;
}
// Convert only value to JSON wire format.
Amount.prototype.to_text = function(allow_nan) {
if (NaN === this.value) {
// Never should happen.
return allow_nan ? NaN : "0";
}
else if (this.is_native) {
if (this.value.compareTo(exports.consts.xns_max) > 0 || this.value.compareTo(exports.consts.xns_min) < 0)
{
// Never should happen.
return allow_nan ? NaN : "0";
}
else
{
return this.value.toString();
}
}
else if (this.value.equals(BigInteger.ZERO))
{
return "0";
}
else if (this.offset < -25 || this.offset > -5)
{
// Use e notation.
// XXX Clamp output.
return (this.is_negative ? "-" : "") + this.value.toString() + "e" + this.offset;
}
else
{
var val = "000000000000000000000000000" + this.value.toString() + "00000000000000000000000";
var pre = val.substring(0, this.offset + 43);
var post = val.substring(this.offset + 43);
var s_pre = pre.match(/[1-9].*$/); // Everything but leading zeros.
var s_post = post.match(/[1-9]0*$/); // Last non-zero plus trailing zeros.
return (this.is_negative ? "-" : "")
+ (s_pre ? s_pre[0] : "0")
+ (s_post ? "." + post.substring(0, 1+post.length-s_post[0].length) : "");
}
};
Amount.prototype.canonicalize = function() {
if (NaN === this.value || !this.currency) {
// nothing
}
else if (this.value.equals(BigInteger.ZERO)) {
this.offset = -100;
this.is_negative = false;
}
else
{
while (this.value.compareTo(exports.consts.bi_man_min_value) < 0) {
this.value = this.value.multiply(exports.consts.bi_10);
this.offset -= 1;
}
while (this.value.compareTo(exports.consts.bi_man_max_value) > 0) {
this.value = this.value.divide(exports.consts.bi_10);
this.offset += 1;
}
}
};
Amount.prototype.to_json = function() {
@@ -138,58 +216,132 @@ Amount.prototype.to_json = function() {
}
};
// Parse a native value.
Amount.prototype.to_text_full = function() {
return this.value === NaN
? NaN
: this.is_native
? this.to_text() + "/XNS"
: this.to_text() + "/" + this.currency.to_json() + "/" + this.issuer.to_json();
};
// Parse a XNS value from untrusted input.
// - integer = raw units
// - float = with precision 6
// XXX Improvements: disallow leading zeros.
Amount.prototype.parse_native = function(j) {
if ('integer' === typeof j) {
// XNS
this.value = j >= 0 ? j : -j; // XXX Use biginteger.
this.offset = 0;
var m;
if ('string' === typeof j)
m = j.match(/^(\d+)(\.\d{1,6})?$/);
if (null !== m) {
if (undefined === m[2]) {
// Integer notation
this.value = new BigInteger(m[1]);
}
else {
// Decimal notation
var int_part = (new BigInteger(m[1])).multiply(exports.consts.xns_unit);
var fraction_part = (new BigInteger(m[2])).multiply(new BigInteger(String(Math.pow(10, 1+exports.consts.xns_precision-m[2].length))));
this.value = int_part.add(fraction_part);
}
this.is_native = true;
this.is_negative = j < 0;
this.offset = undefined;
this.is_negative = undefined;
if (this.value.compareTo(exports.consts.xns_max) > 0 || this.value.compareTo(exports.consts.xns_min) < 0)
{
this.value = NaN;
}
}
else if ('string' === typeof j) {
this.value = j >= 0 ? j : -j; // XXX Use biginteger.
this.offset = 0;
this.is_native = true;
this.is_negative = j < 0;
}
else {
this.value = NaN;
}
return this;
};
// Parse a non-native value.
Amount.prototype.parse_value = function(j) {
if ('integer' === typeof j) {
this.value = j >= 0 ? j : -j; // XXX Use biginteger.
if ('number' === typeof j) {
this.value = new BigInteger(j);
this.offset = 0;
this.is_native = false;
this.is_negative = j < 0;
this.canonicalize();
}
else if ('string' === typeof j) {
this.value = j >= 0 ? j : -j; // XXX Use biginteger.
this.offset = 0;
var e = j.match(/^(-?\d+)e(\d+)/);
var d = j.match(/^(-?\d+)\.(\d+)/);
if (e) {
// e notation
this.value = new BigInteger(e[1]);
this.offset = parseInt(e[2]);
}
else if (d) {
// float notation
var integer = new BigInteger(d[1]);
var fraction = new BigInteger(d[2]);
this.value = integer.multiply(exports.consts.bi_10.clone().pow(d[2].length)).add(fraction);
this.offset = -d[2].length;
}
else
{
// integer notation
this.value = new BigInteger(j);
this.offset = 0;
}
this.is_native = false;
this.is_negative = j < 0;
this.is_negative = undefined;
this.canonicalize();
}
else {
this.value = NaN;
}
return this;
};
// <-> j
Amount.prototype.parse_json = function(j) {
if ('object' === typeof j && j.currency) {
if ('string' === typeof j) {
// .../.../... notation is not a wire format. But allowed for easier testing.
var m = j.match(/^(.+)\/(...)\/(.+)$/);
this.parse_value(j);
if (m) {
this.parse_value(m[1]);
this.currency = Currency.from_json(m[2]);
this.issuer = UInt160.from_json(m[3]);
}
else {
this.parse_native(j);
this.currency = new Currency();
this.issuer = new UInt160();
}
}
else if ('object' === typeof j && j.currency) {
// Never XNS.
this.parse_value(j.value);
this.currency.parse_json(j.currency);
this.issuer.parse_json(j.issuer);
}
else {
this.parse_native(j);
this.currency = 0;
this.issuer = 0;
this.value = NaN;
}
return this;
};
exports.Amount = Amount;
@@ -201,10 +353,17 @@ exports.consts = {
'address_one' : "iiiiiiiiiiiiiiiiiiiiBZbvjr",
'currency_xns' : 0,
'currency_one' : 1,
'uint160_xns' : hexToString("0000000000000000000000000000000000000000"),
'uint160_one' : hexToString("0000000000000000000000000000000000000001"),
'uint160_xns' : utils.hexToString("0000000000000000000000000000000000000000"),
'uint160_one' : utils.hexToString("0000000000000000000000000000000000000001"),
'hex_xns' : "0000000000000000000000000000000000000000",
'hex_one' : "0000000000000000000000000000000000000001",
'xns_max' : new BigInteger("9000000000000000000"), // Json wire limit.
'xns_min' : new BigInteger("-9000000000000000000"), // Json wire limit.
'xns_unit' : new BigInteger('1000000'),
'xns_precision' : 6,
'bi_man_min_value' : new BigInteger('1000000000000000'),
'bi_man_max_value' : new BigInteger('9999999999999999'),
'bi_10' : new BigInteger('10'),
};
// vim:sw=2:sts=2:ts=8

View File

@@ -1,12 +1,541 @@
// Derived from Tom Wu's jsbn code.
//
// Changes made for clean up and to package as a node.js module.
// Copyright (c) 2005-2009 Tom Wu
// All Rights Reserved.
// See "LICENSE" for details.
// Basic JavaScript BN library - subset useful for RSA encryption.
// Extended JavaScript BN functions, required for RSA private ops.
// Version 1.1: new BigInteger("0", 10) returns "proper" zero
// Version 1.2: square() API, isProbablePrime fix
// Bits per digit
var dbits;
// JavaScript engine analysis
var canary = 0xdeadbeefcafe;
var j_lm = ((canary&0xffffff)==0xefcafe);
// (public) Constructor
var BigInteger = function BigInteger(a,b,c) {
if(a != null)
if("number" == typeof a) this.fromNumber(a,b,c);
else if(b == null && "string" != typeof a) this.fromString(a,256);
else this.fromString(a,b);
};
// return new, unset BigInteger
var nbi = function nbi() { return new BigInteger(null); };
// am: Compute w_j += (x*this_i), propagate carries,
// c is initial carry, returns final carry.
// c < 3*dvalue, x < 2*dvalue, this_i < dvalue
// We need to select the fastest one that works in this environment.
// am1: use a single mult and divide to get the high bits,
// max digit bits should be 26 because
// max internal value = 2*dvalue^2-2*dvalue (< 2^53)
function am1(i,x,w,j,c,n) {
while(--n >= 0) {
var v = x*this[i++]+w[j]+c;
c = Math.floor(v/0x4000000);
w[j++] = v&0x3ffffff;
}
return c;
}
// am2 avoids a big mult-and-extract completely.
// Max digit bits should be <= 30 because we do bitwise ops
// on values up to 2*hdvalue^2-hdvalue-1 (< 2^31)
function am2(i,x,w,j,c,n) {
var xl = x&0x7fff, xh = x>>15;
while(--n >= 0) {
var l = this[i]&0x7fff;
var h = this[i++]>>15;
var m = xh*l+h*xl;
l = xl*l+((m&0x7fff)<<15)+w[j]+(c&0x3fffffff);
c = (l>>>30)+(m>>>15)+xh*h+(c>>>30);
w[j++] = l&0x3fffffff;
}
return c;
}
// Alternately, set max digit bits to 28 since some
// browsers slow down when dealing with 32-bit numbers.
function am3(i,x,w,j,c,n) {
var xl = x&0x3fff, xh = x>>14;
while(--n >= 0) {
var l = this[i]&0x3fff;
var h = this[i++]>>14;
var m = xh*l+h*xl;
l = xl*l+((m&0x3fff)<<14)+w[j]+c;
c = (l>>28)+(m>>14)+xh*h;
w[j++] = l&0xfffffff;
}
return c;
}
if(j_lm && 'undefined' !== typeof navigator && (navigator.appName == "Microsoft Internet Explorer")) {
BigInteger.prototype.am = am2;
dbits = 30;
}
else if(j_lm && 'undefined' !== typeof navigator && (navigator.appName != "Netscape")) {
BigInteger.prototype.am = am1;
dbits = 26;
}
else { // Mozilla/Netscape seems to prefer am3
BigInteger.prototype.am = am3;
dbits = 28;
}
BigInteger.prototype.DB = dbits;
BigInteger.prototype.DM = ((1<<dbits)-1);
BigInteger.prototype.DV = (1<<dbits);
var BI_FP = 52;
BigInteger.prototype.FV = Math.pow(2,BI_FP);
BigInteger.prototype.F1 = BI_FP-dbits;
BigInteger.prototype.F2 = 2*dbits-BI_FP;
// Digit conversions
var BI_RM = "0123456789abcdefghijklmnopqrstuvwxyz";
var BI_RC = new Array();
var rr,vv;
rr = "0".charCodeAt(0);
for(vv = 0; vv <= 9; ++vv) BI_RC[rr++] = vv;
rr = "a".charCodeAt(0);
for(vv = 10; vv < 36; ++vv) BI_RC[rr++] = vv;
rr = "A".charCodeAt(0);
for(vv = 10; vv < 36; ++vv) BI_RC[rr++] = vv;
function int2char(n) { return BI_RM.charAt(n); }
function intAt(s,i) {
var c = BI_RC[s.charCodeAt(i)];
return (c==null)?-1:c;
}
// (protected) copy this to r
function bnpCopyTo(r) {
for(var i = this.t-1; i >= 0; --i) r[i] = this[i];
r.t = this.t;
r.s = this.s;
}
// (protected) set from integer value x, -DV <= x < DV
function bnpFromInt(x) {
this.t = 1;
this.s = (x<0)?-1:0;
if(x > 0) this[0] = x;
else if(x < -1) this[0] = x+DV;
else this.t = 0;
}
// return bigint initialized to value
function nbv(i) { var r = nbi(); r.fromInt(i); return r; }
// (protected) set from string and radix
function bnpFromString(s,b) {
var k;
if(b == 16) k = 4;
else if(b == 8) k = 3;
else if(b == 256) k = 8; // byte array
else if(b == 2) k = 1;
else if(b == 32) k = 5;
else if(b == 4) k = 2;
else { this.fromRadix(s,b); return; }
this.t = 0;
this.s = 0;
var i = s.length, mi = false, sh = 0;
while(--i >= 0) {
var x = (k==8)?s[i]&0xff:intAt(s,i);
if(x < 0) {
if(s.charAt(i) == "-") mi = true;
continue;
}
mi = false;
if(sh == 0)
this[this.t++] = x;
else if(sh+k > this.DB) {
this[this.t-1] |= (x&((1<<(this.DB-sh))-1))<<sh;
this[this.t++] = (x>>(this.DB-sh));
}
else
this[this.t-1] |= x<<sh;
sh += k;
if(sh >= this.DB) sh -= this.DB;
}
if(k == 8 && (s[0]&0x80) != 0) {
this.s = -1;
if(sh > 0) this[this.t-1] |= ((1<<(this.DB-sh))-1)<<sh;
}
this.clamp();
if(mi) BigInteger.ZERO.subTo(this,this);
}
// (protected) clamp off excess high words
function bnpClamp() {
var c = this.s&this.DM;
while(this.t > 0 && this[this.t-1] == c) --this.t;
}
// (public) return string representation in given radix
function bnToString(b) {
if(this.s < 0) return "-"+this.negate().toString(b);
var k;
if(b == 16) k = 4;
else if(b == 8) k = 3;
else if(b == 2) k = 1;
else if(b == 32) k = 5;
else if(b == 4) k = 2;
else return this.toRadix(b);
var km = (1<<k)-1, d, m = false, r = "", i = this.t;
var p = this.DB-(i*this.DB)%k;
if(i-- > 0) {
if(p < this.DB && (d = this[i]>>p) > 0) { m = true; r = int2char(d); }
while(i >= 0) {
if(p < k) {
d = (this[i]&((1<<p)-1))<<(k-p);
d |= this[--i]>>(p+=this.DB-k);
}
else {
d = (this[i]>>(p-=k))&km;
if(p <= 0) { p += this.DB; --i; }
}
if(d > 0) m = true;
if(m) r += int2char(d);
}
}
return m?r:"0";
}
// (public) -this
function bnNegate() { var r = nbi(); BigInteger.ZERO.subTo(this,r); return r; }
// (public) |this|
function bnAbs() { return (this.s<0)?this.negate():this; }
// (public) return + if this > a, - if this < a, 0 if equal
function bnCompareTo(a) {
var r = this.s-a.s;
if(r != 0) return r;
var i = this.t;
r = i-a.t;
if(r != 0) return (this.s<0)?-r:r;
while(--i >= 0) if((r=this[i]-a[i]) != 0) return r;
return 0;
}
// returns bit length of the integer x
function nbits(x) {
var r = 1, t;
if((t=x>>>16) != 0) { x = t; r += 16; }
if((t=x>>8) != 0) { x = t; r += 8; }
if((t=x>>4) != 0) { x = t; r += 4; }
if((t=x>>2) != 0) { x = t; r += 2; }
if((t=x>>1) != 0) { x = t; r += 1; }
return r;
}
// (public) return the number of bits in "this"
function bnBitLength() {
if(this.t <= 0) return 0;
return this.DB*(this.t-1)+nbits(this[this.t-1]^(this.s&this.DM));
}
// (protected) r = this << n*DB
function bnpDLShiftTo(n,r) {
var i;
for(i = this.t-1; i >= 0; --i) r[i+n] = this[i];
for(i = n-1; i >= 0; --i) r[i] = 0;
r.t = this.t+n;
r.s = this.s;
}
// (protected) r = this >> n*DB
function bnpDRShiftTo(n,r) {
for(var i = n; i < this.t; ++i) r[i-n] = this[i];
r.t = Math.max(this.t-n,0);
r.s = this.s;
}
// (protected) r = this << n
function bnpLShiftTo(n,r) {
var bs = n%this.DB;
var cbs = this.DB-bs;
var bm = (1<<cbs)-1;
var ds = Math.floor(n/this.DB), c = (this.s<<bs)&this.DM, i;
for(i = this.t-1; i >= 0; --i) {
r[i+ds+1] = (this[i]>>cbs)|c;
c = (this[i]&bm)<<bs;
}
for(i = ds-1; i >= 0; --i) r[i] = 0;
r[ds] = c;
r.t = this.t+ds+1;
r.s = this.s;
r.clamp();
}
// (protected) r = this >> n
function bnpRShiftTo(n,r) {
r.s = this.s;
var ds = Math.floor(n/this.DB);
if(ds >= this.t) { r.t = 0; return; }
var bs = n%this.DB;
var cbs = this.DB-bs;
var bm = (1<<bs)-1;
r[0] = this[ds]>>bs;
for(var i = ds+1; i < this.t; ++i) {
r[i-ds-1] |= (this[i]&bm)<<cbs;
r[i-ds] = this[i]>>bs;
}
if(bs > 0) r[this.t-ds-1] |= (this.s&bm)<<cbs;
r.t = this.t-ds;
r.clamp();
}
// (protected) r = this - a
function bnpSubTo(a,r) {
var i = 0, c = 0, m = Math.min(a.t,this.t);
while(i < m) {
c += this[i]-a[i];
r[i++] = c&this.DM;
c >>= this.DB;
}
if(a.t < this.t) {
c -= a.s;
while(i < this.t) {
c += this[i];
r[i++] = c&this.DM;
c >>= this.DB;
}
c += this.s;
}
else {
c += this.s;
while(i < a.t) {
c -= a[i];
r[i++] = c&this.DM;
c >>= this.DB;
}
c -= a.s;
}
r.s = (c<0)?-1:0;
if(c < -1) r[i++] = this.DV+c;
else if(c > 0) r[i++] = c;
r.t = i;
r.clamp();
}
// (protected) r = this * a, r != this,a (HAC 14.12)
// "this" should be the larger one if appropriate.
function bnpMultiplyTo(a,r) {
var x = this.abs(), y = a.abs();
var i = x.t;
r.t = i+y.t;
while(--i >= 0) r[i] = 0;
for(i = 0; i < y.t; ++i) r[i+x.t] = x.am(0,y[i],r,i,0,x.t);
r.s = 0;
r.clamp();
if(this.s != a.s) BigInteger.ZERO.subTo(r,r);
}
// (protected) r = this^2, r != this (HAC 14.16)
function bnpSquareTo(r) {
var x = this.abs();
var i = r.t = 2*x.t;
while(--i >= 0) r[i] = 0;
for(i = 0; i < x.t-1; ++i) {
var c = x.am(i,x[i],r,2*i,0,1);
if((r[i+x.t]+=x.am(i+1,2*x[i],r,2*i+1,c,x.t-i-1)) >= x.DV) {
r[i+x.t] -= x.DV;
r[i+x.t+1] = 1;
}
}
if(r.t > 0) r[r.t-1] += x.am(i,x[i],r,2*i,0,1);
r.s = 0;
r.clamp();
}
// (protected) divide this by m, quotient and remainder to q, r (HAC 14.20)
// r != q, this != m. q or r may be null.
function bnpDivRemTo(m,q,r) {
var pm = m.abs();
if(pm.t <= 0) return;
var pt = this.abs();
if(pt.t < pm.t) {
if(q != null) q.fromInt(0);
if(r != null) this.copyTo(r);
return;
}
if(r == null) r = nbi();
var y = nbi(), ts = this.s, ms = m.s;
var nsh = this.DB-nbits(pm[pm.t-1]); // normalize modulus
if(nsh > 0) { pm.lShiftTo(nsh,y); pt.lShiftTo(nsh,r); }
else { pm.copyTo(y); pt.copyTo(r); }
var ys = y.t;
var y0 = y[ys-1];
if(y0 == 0) return;
var yt = y0*(1<<this.F1)+((ys>1)?y[ys-2]>>this.F2:0);
var d1 = this.FV/yt, d2 = (1<<this.F1)/yt, e = 1<<this.F2;
var i = r.t, j = i-ys, t = (q==null)?nbi():q;
y.dlShiftTo(j,t);
if(r.compareTo(t) >= 0) {
r[r.t++] = 1;
r.subTo(t,r);
}
BigInteger.ONE.dlShiftTo(ys,t);
t.subTo(y,y); // "negative" y so we can replace sub with am later
while(y.t < ys) y[y.t++] = 0;
while(--j >= 0) {
// Estimate quotient digit
var qd = (r[--i]==y0)?this.DM:Math.floor(r[i]*d1+(r[i-1]+e)*d2);
if((r[i]+=y.am(0,qd,r,j,0,ys)) < qd) { // Try it out
y.dlShiftTo(j,t);
r.subTo(t,r);
while(r[i] < --qd) r.subTo(t,r);
}
}
if(q != null) {
r.drShiftTo(ys,q);
if(ts != ms) BigInteger.ZERO.subTo(q,q);
}
r.t = ys;
r.clamp();
if(nsh > 0) r.rShiftTo(nsh,r); // Denormalize remainder
if(ts < 0) BigInteger.ZERO.subTo(r,r);
}
// (public) this mod a
function bnMod(a) {
var r = nbi();
this.abs().divRemTo(a,null,r);
if(this.s < 0 && r.compareTo(BigInteger.ZERO) > 0) a.subTo(r,r);
return r;
}
// Modular reduction using "classic" algorithm
function Classic(m) { this.m = m; }
function cConvert(x) {
if(x.s < 0 || x.compareTo(this.m) >= 0) return x.mod(this.m);
else return x;
}
function cRevert(x) { return x; }
function cReduce(x) { x.divRemTo(this.m,null,x); }
function cMulTo(x,y,r) { x.multiplyTo(y,r); this.reduce(r); }
function cSqrTo(x,r) { x.squareTo(r); this.reduce(r); }
Classic.prototype.convert = cConvert;
Classic.prototype.revert = cRevert;
Classic.prototype.reduce = cReduce;
Classic.prototype.mulTo = cMulTo;
Classic.prototype.sqrTo = cSqrTo;
// (protected) return "-1/this % 2^DB"; useful for Mont. reduction
// justification:
// xy == 1 (mod m)
// xy = 1+km
// xy(2-xy) = (1+km)(1-km)
// x[y(2-xy)] = 1-k^2m^2
// x[y(2-xy)] == 1 (mod m^2)
// if y is 1/x mod m, then y(2-xy) is 1/x mod m^2
// should reduce x and y(2-xy) by m^2 at each step to keep size bounded.
// JS multiply "overflows" differently from C/C++, so care is needed here.
function bnpInvDigit() {
if(this.t < 1) return 0;
var x = this[0];
if((x&1) == 0) return 0;
var y = x&3; // y == 1/x mod 2^2
y = (y*(2-(x&0xf)*y))&0xf; // y == 1/x mod 2^4
y = (y*(2-(x&0xff)*y))&0xff; // y == 1/x mod 2^8
y = (y*(2-(((x&0xffff)*y)&0xffff)))&0xffff; // y == 1/x mod 2^16
// last step - calculate inverse mod DV directly;
// assumes 16 < DB <= 32 and assumes ability to handle 48-bit ints
y = (y*(2-x*y%this.DV))%this.DV; // y == 1/x mod 2^dbits
// we really want the negative inverse, and -DV < y < DV
return (y>0)?this.DV-y:-y;
}
// Montgomery reduction
function Montgomery(m) {
this.m = m;
this.mp = m.invDigit();
this.mpl = this.mp&0x7fff;
this.mph = this.mp>>15;
this.um = (1<<(m.DB-15))-1;
this.mt2 = 2*m.t;
}
// xR mod m
function montConvert(x) {
var r = nbi();
x.abs().dlShiftTo(this.m.t,r);
r.divRemTo(this.m,null,r);
if(x.s < 0 && r.compareTo(BigInteger.ZERO) > 0) this.m.subTo(r,r);
return r;
}
// x/R mod m
function montRevert(x) {
var r = nbi();
x.copyTo(r);
this.reduce(r);
return r;
}
// x = x/R mod m (HAC 14.32)
function montReduce(x) {
while(x.t <= this.mt2) // pad x so am has enough room later
x[x.t++] = 0;
for(var i = 0; i < this.m.t; ++i) {
// faster way of calculating u0 = x[i]*mp mod DV
var j = x[i]&0x7fff;
var u0 = (j*this.mpl+(((j*this.mph+(x[i]>>15)*this.mpl)&this.um)<<15))&x.DM;
// use am to combine the multiply-shift-add into one call
j = i+this.m.t;
x[j] += this.m.am(0,u0,x,i,0,this.m.t);
// propagate carry
while(x[j] >= x.DV) { x[j] -= x.DV; x[++j]++; }
}
x.clamp();
x.drShiftTo(this.m.t,x);
if(x.compareTo(this.m) >= 0) x.subTo(this.m,x);
}
// r = "x^2/R mod m"; x != r
function montSqrTo(x,r) { x.squareTo(r); this.reduce(r); }
// r = "xy/R mod m"; x,y != r
function montMulTo(x,y,r) { x.multiplyTo(y,r); this.reduce(r); }
Montgomery.prototype.convert = montConvert;
Montgomery.prototype.revert = montRevert;
Montgomery.prototype.reduce = montReduce;
Montgomery.prototype.mulTo = montMulTo;
Montgomery.prototype.sqrTo = montSqrTo;
// (protected) true iff this is even
function bnpIsEven() { return ((this.t>0)?(this[0]&1):this.s) == 0; }
// (protected) this^e, e < 2^32, doing sqr and mul with "r" (HAC 14.79)
function bnpExp(e,z) {
if(e > 0xffffffff || e < 1) return BigInteger.ONE;
var r = nbi(), r2 = nbi(), g = z.convert(this), i = nbits(e)-1;
g.copyTo(r);
while(--i >= 0) {
z.sqrTo(r,r2);
if((e&(1<<i)) > 0) z.mulTo(r2,g,r);
else { var t = r; r = r2; r2 = t; }
}
return z.revert(r);
}
// (public) this^e % m, 0 <= e < 2^32
function bnModPowInt(e,m) {
var z;
if(e < 256 || m.isEven()) z = new Classic(m); else z = new Montgomery(m);
return this.exp(e,z);
}
// (public)
function bnClone() { var r = nbi(); this.copyTo(r); return r; }
@@ -654,3 +1183,37 @@ BigInteger.prototype.square = bnSquare;
// int hashCode()
// long longValue()
// static BigInteger valueOf(long val)
// protected
BigInteger.prototype.copyTo = bnpCopyTo;
BigInteger.prototype.fromInt = bnpFromInt;
BigInteger.prototype.fromString = bnpFromString;
BigInteger.prototype.clamp = bnpClamp;
BigInteger.prototype.dlShiftTo = bnpDLShiftTo;
BigInteger.prototype.drShiftTo = bnpDRShiftTo;
BigInteger.prototype.lShiftTo = bnpLShiftTo;
BigInteger.prototype.rShiftTo = bnpRShiftTo;
BigInteger.prototype.subTo = bnpSubTo;
BigInteger.prototype.multiplyTo = bnpMultiplyTo;
BigInteger.prototype.squareTo = bnpSquareTo;
BigInteger.prototype.divRemTo = bnpDivRemTo;
BigInteger.prototype.invDigit = bnpInvDigit;
BigInteger.prototype.isEven = bnpIsEven;
BigInteger.prototype.exp = bnpExp;
// public
BigInteger.prototype.toString = bnToString;
BigInteger.prototype.negate = bnNegate;
BigInteger.prototype.abs = bnAbs;
BigInteger.prototype.compareTo = bnCompareTo;
BigInteger.prototype.bitLength = bnBitLength;
BigInteger.prototype.mod = bnMod;
BigInteger.prototype.modPowInt = bnModPowInt;
// "constants"
BigInteger.ZERO = nbv(0);
BigInteger.ONE = nbv(1);
exports.nbi = nbi;
exports.BigInteger = BigInteger;
// vim:sw=2:sts=2:ts=8

View File

@@ -1,559 +0,0 @@
// Copyright (c) 2005 Tom Wu
// All Rights Reserved.
// See "LICENSE" for details.
// Basic JavaScript BN library - subset useful for RSA encryption.
// Bits per digit
var dbits;
// JavaScript engine analysis
var canary = 0xdeadbeefcafe;
var j_lm = ((canary&0xffffff)==0xefcafe);
// (public) Constructor
function BigInteger(a,b,c) {
if(a != null)
if("number" == typeof a) this.fromNumber(a,b,c);
else if(b == null && "string" != typeof a) this.fromString(a,256);
else this.fromString(a,b);
}
// return new, unset BigInteger
function nbi() { return new BigInteger(null); }
// am: Compute w_j += (x*this_i), propagate carries,
// c is initial carry, returns final carry.
// c < 3*dvalue, x < 2*dvalue, this_i < dvalue
// We need to select the fastest one that works in this environment.
// am1: use a single mult and divide to get the high bits,
// max digit bits should be 26 because
// max internal value = 2*dvalue^2-2*dvalue (< 2^53)
function am1(i,x,w,j,c,n) {
while(--n >= 0) {
var v = x*this[i++]+w[j]+c;
c = Math.floor(v/0x4000000);
w[j++] = v&0x3ffffff;
}
return c;
}
// am2 avoids a big mult-and-extract completely.
// Max digit bits should be <= 30 because we do bitwise ops
// on values up to 2*hdvalue^2-hdvalue-1 (< 2^31)
function am2(i,x,w,j,c,n) {
var xl = x&0x7fff, xh = x>>15;
while(--n >= 0) {
var l = this[i]&0x7fff;
var h = this[i++]>>15;
var m = xh*l+h*xl;
l = xl*l+((m&0x7fff)<<15)+w[j]+(c&0x3fffffff);
c = (l>>>30)+(m>>>15)+xh*h+(c>>>30);
w[j++] = l&0x3fffffff;
}
return c;
}
// Alternately, set max digit bits to 28 since some
// browsers slow down when dealing with 32-bit numbers.
function am3(i,x,w,j,c,n) {
var xl = x&0x3fff, xh = x>>14;
while(--n >= 0) {
var l = this[i]&0x3fff;
var h = this[i++]>>14;
var m = xh*l+h*xl;
l = xl*l+((m&0x3fff)<<14)+w[j]+c;
c = (l>>28)+(m>>14)+xh*h;
w[j++] = l&0xfffffff;
}
return c;
}
if(j_lm && (navigator.appName == "Microsoft Internet Explorer")) {
BigInteger.prototype.am = am2;
dbits = 30;
}
else if(j_lm && (navigator.appName != "Netscape")) {
BigInteger.prototype.am = am1;
dbits = 26;
}
else { // Mozilla/Netscape seems to prefer am3
BigInteger.prototype.am = am3;
dbits = 28;
}
BigInteger.prototype.DB = dbits;
BigInteger.prototype.DM = ((1<<dbits)-1);
BigInteger.prototype.DV = (1<<dbits);
var BI_FP = 52;
BigInteger.prototype.FV = Math.pow(2,BI_FP);
BigInteger.prototype.F1 = BI_FP-dbits;
BigInteger.prototype.F2 = 2*dbits-BI_FP;
// Digit conversions
var BI_RM = "0123456789abcdefghijklmnopqrstuvwxyz";
var BI_RC = new Array();
var rr,vv;
rr = "0".charCodeAt(0);
for(vv = 0; vv <= 9; ++vv) BI_RC[rr++] = vv;
rr = "a".charCodeAt(0);
for(vv = 10; vv < 36; ++vv) BI_RC[rr++] = vv;
rr = "A".charCodeAt(0);
for(vv = 10; vv < 36; ++vv) BI_RC[rr++] = vv;
function int2char(n) { return BI_RM.charAt(n); }
function intAt(s,i) {
var c = BI_RC[s.charCodeAt(i)];
return (c==null)?-1:c;
}
// (protected) copy this to r
function bnpCopyTo(r) {
for(var i = this.t-1; i >= 0; --i) r[i] = this[i];
r.t = this.t;
r.s = this.s;
}
// (protected) set from integer value x, -DV <= x < DV
function bnpFromInt(x) {
this.t = 1;
this.s = (x<0)?-1:0;
if(x > 0) this[0] = x;
else if(x < -1) this[0] = x+DV;
else this.t = 0;
}
// return bigint initialized to value
function nbv(i) { var r = nbi(); r.fromInt(i); return r; }
// (protected) set from string and radix
function bnpFromString(s,b) {
var k;
if(b == 16) k = 4;
else if(b == 8) k = 3;
else if(b == 256) k = 8; // byte array
else if(b == 2) k = 1;
else if(b == 32) k = 5;
else if(b == 4) k = 2;
else { this.fromRadix(s,b); return; }
this.t = 0;
this.s = 0;
var i = s.length, mi = false, sh = 0;
while(--i >= 0) {
var x = (k==8)?s[i]&0xff:intAt(s,i);
if(x < 0) {
if(s.charAt(i) == "-") mi = true;
continue;
}
mi = false;
if(sh == 0)
this[this.t++] = x;
else if(sh+k > this.DB) {
this[this.t-1] |= (x&((1<<(this.DB-sh))-1))<<sh;
this[this.t++] = (x>>(this.DB-sh));
}
else
this[this.t-1] |= x<<sh;
sh += k;
if(sh >= this.DB) sh -= this.DB;
}
if(k == 8 && (s[0]&0x80) != 0) {
this.s = -1;
if(sh > 0) this[this.t-1] |= ((1<<(this.DB-sh))-1)<<sh;
}
this.clamp();
if(mi) BigInteger.ZERO.subTo(this,this);
}
// (protected) clamp off excess high words
function bnpClamp() {
var c = this.s&this.DM;
while(this.t > 0 && this[this.t-1] == c) --this.t;
}
// (public) return string representation in given radix
function bnToString(b) {
if(this.s < 0) return "-"+this.negate().toString(b);
var k;
if(b == 16) k = 4;
else if(b == 8) k = 3;
else if(b == 2) k = 1;
else if(b == 32) k = 5;
else if(b == 4) k = 2;
else return this.toRadix(b);
var km = (1<<k)-1, d, m = false, r = "", i = this.t;
var p = this.DB-(i*this.DB)%k;
if(i-- > 0) {
if(p < this.DB && (d = this[i]>>p) > 0) { m = true; r = int2char(d); }
while(i >= 0) {
if(p < k) {
d = (this[i]&((1<<p)-1))<<(k-p);
d |= this[--i]>>(p+=this.DB-k);
}
else {
d = (this[i]>>(p-=k))&km;
if(p <= 0) { p += this.DB; --i; }
}
if(d > 0) m = true;
if(m) r += int2char(d);
}
}
return m?r:"0";
}
// (public) -this
function bnNegate() { var r = nbi(); BigInteger.ZERO.subTo(this,r); return r; }
// (public) |this|
function bnAbs() { return (this.s<0)?this.negate():this; }
// (public) return + if this > a, - if this < a, 0 if equal
function bnCompareTo(a) {
var r = this.s-a.s;
if(r != 0) return r;
var i = this.t;
r = i-a.t;
if(r != 0) return (this.s<0)?-r:r;
while(--i >= 0) if((r=this[i]-a[i]) != 0) return r;
return 0;
}
// returns bit length of the integer x
function nbits(x) {
var r = 1, t;
if((t=x>>>16) != 0) { x = t; r += 16; }
if((t=x>>8) != 0) { x = t; r += 8; }
if((t=x>>4) != 0) { x = t; r += 4; }
if((t=x>>2) != 0) { x = t; r += 2; }
if((t=x>>1) != 0) { x = t; r += 1; }
return r;
}
// (public) return the number of bits in "this"
function bnBitLength() {
if(this.t <= 0) return 0;
return this.DB*(this.t-1)+nbits(this[this.t-1]^(this.s&this.DM));
}
// (protected) r = this << n*DB
function bnpDLShiftTo(n,r) {
var i;
for(i = this.t-1; i >= 0; --i) r[i+n] = this[i];
for(i = n-1; i >= 0; --i) r[i] = 0;
r.t = this.t+n;
r.s = this.s;
}
// (protected) r = this >> n*DB
function bnpDRShiftTo(n,r) {
for(var i = n; i < this.t; ++i) r[i-n] = this[i];
r.t = Math.max(this.t-n,0);
r.s = this.s;
}
// (protected) r = this << n
function bnpLShiftTo(n,r) {
var bs = n%this.DB;
var cbs = this.DB-bs;
var bm = (1<<cbs)-1;
var ds = Math.floor(n/this.DB), c = (this.s<<bs)&this.DM, i;
for(i = this.t-1; i >= 0; --i) {
r[i+ds+1] = (this[i]>>cbs)|c;
c = (this[i]&bm)<<bs;
}
for(i = ds-1; i >= 0; --i) r[i] = 0;
r[ds] = c;
r.t = this.t+ds+1;
r.s = this.s;
r.clamp();
}
// (protected) r = this >> n
function bnpRShiftTo(n,r) {
r.s = this.s;
var ds = Math.floor(n/this.DB);
if(ds >= this.t) { r.t = 0; return; }
var bs = n%this.DB;
var cbs = this.DB-bs;
var bm = (1<<bs)-1;
r[0] = this[ds]>>bs;
for(var i = ds+1; i < this.t; ++i) {
r[i-ds-1] |= (this[i]&bm)<<cbs;
r[i-ds] = this[i]>>bs;
}
if(bs > 0) r[this.t-ds-1] |= (this.s&bm)<<cbs;
r.t = this.t-ds;
r.clamp();
}
// (protected) r = this - a
function bnpSubTo(a,r) {
var i = 0, c = 0, m = Math.min(a.t,this.t);
while(i < m) {
c += this[i]-a[i];
r[i++] = c&this.DM;
c >>= this.DB;
}
if(a.t < this.t) {
c -= a.s;
while(i < this.t) {
c += this[i];
r[i++] = c&this.DM;
c >>= this.DB;
}
c += this.s;
}
else {
c += this.s;
while(i < a.t) {
c -= a[i];
r[i++] = c&this.DM;
c >>= this.DB;
}
c -= a.s;
}
r.s = (c<0)?-1:0;
if(c < -1) r[i++] = this.DV+c;
else if(c > 0) r[i++] = c;
r.t = i;
r.clamp();
}
// (protected) r = this * a, r != this,a (HAC 14.12)
// "this" should be the larger one if appropriate.
function bnpMultiplyTo(a,r) {
var x = this.abs(), y = a.abs();
var i = x.t;
r.t = i+y.t;
while(--i >= 0) r[i] = 0;
for(i = 0; i < y.t; ++i) r[i+x.t] = x.am(0,y[i],r,i,0,x.t);
r.s = 0;
r.clamp();
if(this.s != a.s) BigInteger.ZERO.subTo(r,r);
}
// (protected) r = this^2, r != this (HAC 14.16)
function bnpSquareTo(r) {
var x = this.abs();
var i = r.t = 2*x.t;
while(--i >= 0) r[i] = 0;
for(i = 0; i < x.t-1; ++i) {
var c = x.am(i,x[i],r,2*i,0,1);
if((r[i+x.t]+=x.am(i+1,2*x[i],r,2*i+1,c,x.t-i-1)) >= x.DV) {
r[i+x.t] -= x.DV;
r[i+x.t+1] = 1;
}
}
if(r.t > 0) r[r.t-1] += x.am(i,x[i],r,2*i,0,1);
r.s = 0;
r.clamp();
}
// (protected) divide this by m, quotient and remainder to q, r (HAC 14.20)
// r != q, this != m. q or r may be null.
function bnpDivRemTo(m,q,r) {
var pm = m.abs();
if(pm.t <= 0) return;
var pt = this.abs();
if(pt.t < pm.t) {
if(q != null) q.fromInt(0);
if(r != null) this.copyTo(r);
return;
}
if(r == null) r = nbi();
var y = nbi(), ts = this.s, ms = m.s;
var nsh = this.DB-nbits(pm[pm.t-1]); // normalize modulus
if(nsh > 0) { pm.lShiftTo(nsh,y); pt.lShiftTo(nsh,r); }
else { pm.copyTo(y); pt.copyTo(r); }
var ys = y.t;
var y0 = y[ys-1];
if(y0 == 0) return;
var yt = y0*(1<<this.F1)+((ys>1)?y[ys-2]>>this.F2:0);
var d1 = this.FV/yt, d2 = (1<<this.F1)/yt, e = 1<<this.F2;
var i = r.t, j = i-ys, t = (q==null)?nbi():q;
y.dlShiftTo(j,t);
if(r.compareTo(t) >= 0) {
r[r.t++] = 1;
r.subTo(t,r);
}
BigInteger.ONE.dlShiftTo(ys,t);
t.subTo(y,y); // "negative" y so we can replace sub with am later
while(y.t < ys) y[y.t++] = 0;
while(--j >= 0) {
// Estimate quotient digit
var qd = (r[--i]==y0)?this.DM:Math.floor(r[i]*d1+(r[i-1]+e)*d2);
if((r[i]+=y.am(0,qd,r,j,0,ys)) < qd) { // Try it out
y.dlShiftTo(j,t);
r.subTo(t,r);
while(r[i] < --qd) r.subTo(t,r);
}
}
if(q != null) {
r.drShiftTo(ys,q);
if(ts != ms) BigInteger.ZERO.subTo(q,q);
}
r.t = ys;
r.clamp();
if(nsh > 0) r.rShiftTo(nsh,r); // Denormalize remainder
if(ts < 0) BigInteger.ZERO.subTo(r,r);
}
// (public) this mod a
function bnMod(a) {
var r = nbi();
this.abs().divRemTo(a,null,r);
if(this.s < 0 && r.compareTo(BigInteger.ZERO) > 0) a.subTo(r,r);
return r;
}
// Modular reduction using "classic" algorithm
function Classic(m) { this.m = m; }
function cConvert(x) {
if(x.s < 0 || x.compareTo(this.m) >= 0) return x.mod(this.m);
else return x;
}
function cRevert(x) { return x; }
function cReduce(x) { x.divRemTo(this.m,null,x); }
function cMulTo(x,y,r) { x.multiplyTo(y,r); this.reduce(r); }
function cSqrTo(x,r) { x.squareTo(r); this.reduce(r); }
Classic.prototype.convert = cConvert;
Classic.prototype.revert = cRevert;
Classic.prototype.reduce = cReduce;
Classic.prototype.mulTo = cMulTo;
Classic.prototype.sqrTo = cSqrTo;
// (protected) return "-1/this % 2^DB"; useful for Mont. reduction
// justification:
// xy == 1 (mod m)
// xy = 1+km
// xy(2-xy) = (1+km)(1-km)
// x[y(2-xy)] = 1-k^2m^2
// x[y(2-xy)] == 1 (mod m^2)
// if y is 1/x mod m, then y(2-xy) is 1/x mod m^2
// should reduce x and y(2-xy) by m^2 at each step to keep size bounded.
// JS multiply "overflows" differently from C/C++, so care is needed here.
function bnpInvDigit() {
if(this.t < 1) return 0;
var x = this[0];
if((x&1) == 0) return 0;
var y = x&3; // y == 1/x mod 2^2
y = (y*(2-(x&0xf)*y))&0xf; // y == 1/x mod 2^4
y = (y*(2-(x&0xff)*y))&0xff; // y == 1/x mod 2^8
y = (y*(2-(((x&0xffff)*y)&0xffff)))&0xffff; // y == 1/x mod 2^16
// last step - calculate inverse mod DV directly;
// assumes 16 < DB <= 32 and assumes ability to handle 48-bit ints
y = (y*(2-x*y%this.DV))%this.DV; // y == 1/x mod 2^dbits
// we really want the negative inverse, and -DV < y < DV
return (y>0)?this.DV-y:-y;
}
// Montgomery reduction
function Montgomery(m) {
this.m = m;
this.mp = m.invDigit();
this.mpl = this.mp&0x7fff;
this.mph = this.mp>>15;
this.um = (1<<(m.DB-15))-1;
this.mt2 = 2*m.t;
}
// xR mod m
function montConvert(x) {
var r = nbi();
x.abs().dlShiftTo(this.m.t,r);
r.divRemTo(this.m,null,r);
if(x.s < 0 && r.compareTo(BigInteger.ZERO) > 0) this.m.subTo(r,r);
return r;
}
// x/R mod m
function montRevert(x) {
var r = nbi();
x.copyTo(r);
this.reduce(r);
return r;
}
// x = x/R mod m (HAC 14.32)
function montReduce(x) {
while(x.t <= this.mt2) // pad x so am has enough room later
x[x.t++] = 0;
for(var i = 0; i < this.m.t; ++i) {
// faster way of calculating u0 = x[i]*mp mod DV
var j = x[i]&0x7fff;
var u0 = (j*this.mpl+(((j*this.mph+(x[i]>>15)*this.mpl)&this.um)<<15))&x.DM;
// use am to combine the multiply-shift-add into one call
j = i+this.m.t;
x[j] += this.m.am(0,u0,x,i,0,this.m.t);
// propagate carry
while(x[j] >= x.DV) { x[j] -= x.DV; x[++j]++; }
}
x.clamp();
x.drShiftTo(this.m.t,x);
if(x.compareTo(this.m) >= 0) x.subTo(this.m,x);
}
// r = "x^2/R mod m"; x != r
function montSqrTo(x,r) { x.squareTo(r); this.reduce(r); }
// r = "xy/R mod m"; x,y != r
function montMulTo(x,y,r) { x.multiplyTo(y,r); this.reduce(r); }
Montgomery.prototype.convert = montConvert;
Montgomery.prototype.revert = montRevert;
Montgomery.prototype.reduce = montReduce;
Montgomery.prototype.mulTo = montMulTo;
Montgomery.prototype.sqrTo = montSqrTo;
// (protected) true iff this is even
function bnpIsEven() { return ((this.t>0)?(this[0]&1):this.s) == 0; }
// (protected) this^e, e < 2^32, doing sqr and mul with "r" (HAC 14.79)
function bnpExp(e,z) {
if(e > 0xffffffff || e < 1) return BigInteger.ONE;
var r = nbi(), r2 = nbi(), g = z.convert(this), i = nbits(e)-1;
g.copyTo(r);
while(--i >= 0) {
z.sqrTo(r,r2);
if((e&(1<<i)) > 0) z.mulTo(r2,g,r);
else { var t = r; r = r2; r2 = t; }
}
return z.revert(r);
}
// (public) this^e % m, 0 <= e < 2^32
function bnModPowInt(e,m) {
var z;
if(e < 256 || m.isEven()) z = new Classic(m); else z = new Montgomery(m);
return this.exp(e,z);
}
// protected
BigInteger.prototype.copyTo = bnpCopyTo;
BigInteger.prototype.fromInt = bnpFromInt;
BigInteger.prototype.fromString = bnpFromString;
BigInteger.prototype.clamp = bnpClamp;
BigInteger.prototype.dlShiftTo = bnpDLShiftTo;
BigInteger.prototype.drShiftTo = bnpDRShiftTo;
BigInteger.prototype.lShiftTo = bnpLShiftTo;
BigInteger.prototype.rShiftTo = bnpRShiftTo;
BigInteger.prototype.subTo = bnpSubTo;
BigInteger.prototype.multiplyTo = bnpMultiplyTo;
BigInteger.prototype.squareTo = bnpSquareTo;
BigInteger.prototype.divRemTo = bnpDivRemTo;
BigInteger.prototype.invDigit = bnpInvDigit;
BigInteger.prototype.isEven = bnpIsEven;
BigInteger.prototype.exp = bnpExp;
// public
BigInteger.prototype.toString = bnToString;
BigInteger.prototype.negate = bnNegate;
BigInteger.prototype.abs = bnAbs;
BigInteger.prototype.compareTo = bnCompareTo;
BigInteger.prototype.bitLength = bnBitLength;
BigInteger.prototype.mod = bnMod;
BigInteger.prototype.modPowInt = bnModPowInt;
// "constants"
BigInteger.ZERO = nbv(0);
BigInteger.ONE = nbv(1);

88
js/nodeutils.js Normal file
View File

@@ -0,0 +1,88 @@
var fs = require("fs");
var path = require("path");
var utils = require("./utils.js");
// Empty a directory.
var emptyPath = function(dirPath, done) {
fs.readdir(dirPath, function(err, files) {
if (err) {
done(err);
}
else {
utils.mapOr(rmPath, files.map(function(f) { return path.join(dirPath, f); }), done);
}
});
};
// Make a directory and sub-directories.
var mkPath = function(dirPath, mode, done) {
fs.mkdir(dirPath, typeof mode === "string" ? parseInt(mode, 8) : mode, function(e) {
if (!e || e.code === "EEXIST") {
// Created or already exists, done.
done();
}
else if (e.code === "ENOENT") {
// Missing sub dir.
mkPath(path.dirname(dirPath), mode, function(e) {
if (e) {
throw e;
}
else {
mkPath(dirPath, mode, done);
}
});
}
else {
throw e;
}
});
};
// Create directory if needed and empty if needed.
var resetPath = function(dirPath, mode, done) {
mkPath(dirPath, mode, function(e) {
if (e) {
done(e);
}
else {
emptyPath(dirPath, done);
}
});
};
// Remove path recursively.
var rmPath = function(dirPath, done) {
// console.log("rmPath: %s", dirPath);
fs.lstat(dirPath, function(err, stats) {
if (err && err.code == "ENOENT") {
done();
}
if (err) {
done(err);
}
else if (stats.isDirectory()) {
emptyPath(dirPath, function(e) {
if (e) {
done(e);
}
else {
// console.log("rmdir: %s", dirPath); done();
fs.rmdir(dirPath, done);
}
});
}
else {
// console.log("unlink: %s", dirPath); done();
fs.unlink(dirPath, done);
}
});
};
exports.mkPath = mkPath;
exports.resetPath = resetPath;
exports.rmPath = rmPath;
// vim:sw=2:sts=2:ts=8

View File

@@ -7,10 +7,15 @@
// YYY A better model might be to allow requesting a target state: keep connected or not.
//
var util = require('util');
// Node
var util = require('util');
// npm
var WebSocket = require('ws');
var amount = require('./amount.js');
var Amount = amount.Amount;
// --> trusted: truthy, if remote is trusted
var Remote = function (trusted, websocket_ip, websocket_port, config, trace) {
this.trusted = trusted;
@@ -57,10 +62,10 @@ var flags = {
// XXX This needs to be determined from the network.
var fees = {
'default' : 100,
'account_create' : 1000,
'nickname_create' : 1000,
'offer' : 100,
'default' : Amount.from_json("100"),
'account_create' : Amount.from_json("1000"),
'nickname_create' : Amount.from_json("1000"),
'offer' : Amount.from_json("100"),
};
Remote.prototype.connect_helper = function () {
@@ -383,40 +388,79 @@ Remote.prototype.dirty_account_root = function (account) {
// Transactions
//
Remote.prototype.ripple_line_set = function (secret, src, dst, amount, onDone) {
Remote.prototype.offer_create = function (secret, src, taker_pays, taker_gets, expiration, onDone) {
var secret = this.config.accounts[src] ? this.config.accounts[src].secret : secret;
var src_account = this.config.accounts[src] ? this.config.accounts[src].account : src;
var dst_account = this.config.accounts[dst] ? this.config.accounts[dst].account : dst;
var transaction = {
'TransactionType' : 'OfferCreate',
'Account' : src_account,
'Fee' : fees.offer.to_json(),
'TakerPays' : taker_pays.to_json(),
'TakerGets' : taker_gets.to_json(),
};
if (expiration)
transaction.Expiration = expiration;
this.submit_seq(
{
'transaction' : {
'TransactionType' : 'CreditSet',
'Account' : src_account,
'Destination' : dst_account,
'Fee' : create ? fees.account_create : fees['default'],
'Amount' : amount,
},
'transaction' : transaction,
'secret' : secret,
}, function () {
}, onDone);
};
Remote.prototype.send_xns = function (secret, src, dst, amount, create, onDone) {
Remote.prototype.ripple_line_set = function (secret, src, limit, quaility_in, quality_out, onDone) {
var secret = this.config.accounts[src] ? this.config.accounts[src].secret : secret;
var src_account = this.config.accounts[src] ? this.config.accounts[src].account : src;
var transaction = {
'TransactionType' : 'CreditSet',
'Account' : src_account,
'Fee' : fees['default'].to_json(),
};
if (limit)
transaction.LimitAmount = limit.to_json();
if (quaility_in)
transaction.QualityIn = quaility_in;
if (quaility_out)
transaction.QualityOut = quaility_out;
this.submit_seq(
{
'transaction' : transaction,
'secret' : secret,
}, function () {
}, onDone);
};
// --> create: is only valid if destination gets XNS.
Remote.prototype.send = function (secret, src, dst, deliver_amount, send_max, create, onDone) {
var secret = this.config.accounts[src] ? this.config.accounts[src].secret : secret;
var src_account = this.config.accounts[src] ? this.config.accounts[src].account : src;
var dst_account = this.config.accounts[dst] ? this.config.accounts[dst].account : dst;
var transaction = {
'TransactionType' : 'Payment',
'Account' : src_account,
'Fee' : (create ? fees.account_create : fees['default']).to_json(),
'Destination' : dst_account,
'Amount' : deliver_amount.to_json(),
};
if (create)
transaction.Flags = flags.tfCreateAccount;
if (send_max)
transaction.SendMax = send_max.to_json();
this.submit_seq(
{
'transaction' : {
'TransactionType' : 'Payment',
'Account' : src_account,
'Destination' : dst_account,
'Fee' : create ? fees.account_create : fees['default'],
'Flags' : create ? flags.tfCreateAccount : 0,
'Amount' : amount,
},
'transaction' : transaction,
'secret' : secret,
}, function () {
}, onDone);

View File

@@ -1,8 +1,3 @@
// YYY Should probably have two versions: node vs browser
var fs = require("fs");
var path = require("path");
Function.prototype.method = function(name,func) {
this.prototype[name] = func;
@@ -42,84 +37,6 @@ var mapOr = function(func, array, done) {
}
};
// Make a directory and sub-directories.
var mkPath = function(dirPath, mode, done) {
fs.mkdir(dirPath, typeof mode === "string" ? parseInt(mode, 8) : mode, function(e) {
if (!e || e.code === "EEXIST") {
// Created or already exists, done.
done();
}
else if (e.code === "ENOENT") {
// Missing sub dir.
mkPath(path.dirname(dirPath), mode, function(e) {
if (e) {
throw e;
}
else {
mkPath(dirPath, mode, done);
}
});
}
else {
throw e;
}
});
};
// Empty a directory.
var emptyPath = function(dirPath, done) {
fs.readdir(dirPath, function(err, files) {
if (err) {
done(err);
}
else {
mapOr(rmPath, files.map(function(f) { return path.join(dirPath, f); }), done);
}
});
};
// Remove path recursively.
var rmPath = function(dirPath, done) {
// console.log("rmPath: %s", dirPath);
fs.lstat(dirPath, function(err, stats) {
if (err && err.code == "ENOENT") {
done();
}
if (err) {
done(err);
}
else if (stats.isDirectory()) {
emptyPath(dirPath, function(e) {
if (e) {
done(e);
}
else {
// console.log("rmdir: %s", dirPath); done();
fs.rmdir(dirPath, done);
}
});
}
else {
// console.log("unlink: %s", dirPath); done();
fs.unlink(dirPath, done);
}
});
};
// Create directory if needed and empty if needed.
var resetPath = function(dirPath, mode, done) {
mkPath(dirPath, mode, function(e) {
if (e) {
done(e);
}
else {
emptyPath(dirPath, done);
}
});
};
var trace = function(comment, func) {
return function() {
console.log("%s: %s", trace, arguments.toString);
@@ -151,11 +68,7 @@ var stringToHex = function (s) {
}).join("");
};
exports.emptyPath = emptyPath;
exports.mapOr = mapOr;
exports.mkPath = mkPath;
exports.resetPath = resetPath;
exports.rmPath = rmPath;
exports.trace = trace;
exports.hexToString = hexToString;
exports.stringToHex = stringToHex;

View File

@@ -1131,15 +1131,13 @@ Json::Value STAmount::getJson(int) const
{
Json::Value elem(Json::objectValue);
// This is a hack, many places don't specify a currency. STAmount is used just as a value.
if (!mIsNative)
{
// It is an error for currency or issuer not to be specified for valid json.
elem["value"] = getText();
elem["currency"] = getHumanCurrency();
if (!mIssuer.isZero())
elem["issuer"] = NewcoinAddress::createHumanAccountID(mIssuer);
elem["issuer"] = NewcoinAddress::createHumanAccountID(mIssuer);
}
else
{

View File

@@ -208,17 +208,25 @@ void Application::startNewLedger()
void Application::loadOldLedger()
{
Ledger::pointer lastLedger = Ledger::getSQL("SELECT * from Ledgers order by LedgerSeq desc limit 1;");
if (!lastLedger)
try
{
std::cout << "No Ledger found?" << std::endl;
Ledger::pointer lastLedger = Ledger::getSQL("SELECT * from Ledgers order by LedgerSeq desc limit 1;");
if (!lastLedger)
{
std::cout << "No Ledger found?" << std::endl;
exit(-1);
}
lastLedger->setClosed();
Ledger::pointer openLedger = boost::make_shared<Ledger>(false, boost::ref(*lastLedger));
mMasterLedger.switchLedgers(lastLedger, openLedger);
mNetOps.setLastCloseTime(lastLedger->getCloseTimeNC());
}
catch (SHAMapMissingNode& mn)
{
Log(lsFATAL) << "Cannot load ledger. " << mn;
exit(-1);
}
lastLedger->setClosed();
Ledger::pointer openLedger = boost::make_shared<Ledger>(false, boost::ref(*lastLedger));
mMasterLedger.switchLedgers(lastLedger, openLedger);
mNetOps.setLastCloseTime(lastLedger->getCloseTimeNC());
}
// vim:ts=4

View File

@@ -24,7 +24,8 @@ SETUP_LOG();
Ledger::Ledger(const NewcoinAddress& masterID, uint64 startAmount) : mTotCoins(startAmount), mLedgerSeq(1),
mCloseTime(0), mParentCloseTime(0), mCloseResolution(LEDGER_TIME_ACCURACY), mCloseFlags(0),
mClosed(false), mValidHash(false), mAccepted(false), mImmutable(false),
mTransactionMap(new SHAMap(smtTRANSACTION)), mAccountStateMap(new SHAMap(smtFREE))
mTransactionMap(boost::make_shared<SHAMap>(smtTRANSACTION)),
mAccountStateMap(boost::make_shared<SHAMap>(smtSTATE))
{
// special case: put coins in root account
AccountState::pointer startAccount = boost::make_shared<AccountState>(masterID);
@@ -38,13 +39,21 @@ Ledger::Ledger(const NewcoinAddress& masterID, uint64 startAmount) : mTotCoins(s
}
Ledger::Ledger(const uint256 &parentHash, const uint256 &transHash, const uint256 &accountHash,
uint64 totCoins, uint32 closeTime, uint32 parentCloseTime, int closeFlags, int closeResolution, uint32 ledgerSeq,bool isMutable)
uint64 totCoins, uint32 closeTime, uint32 parentCloseTime, int closeFlags, int closeResolution, uint32 ledgerSeq)
: mParentHash(parentHash), mTransHash(transHash), mAccountHash(accountHash), mTotCoins(totCoins),
mLedgerSeq(ledgerSeq), mCloseTime(closeTime), mParentCloseTime(parentCloseTime),
mCloseResolution(closeResolution), mCloseFlags(closeFlags),
mClosed(false), mValidHash(false), mAccepted(false), mImmutable(isMutable)
{
mClosed(false), mValidHash(false), mAccepted(false), mImmutable(true),
mTransactionMap(boost::make_shared<SHAMap>(smtTRANSACTION, transHash)),
mAccountStateMap(boost::make_shared<SHAMap>(smtSTATE, accountHash))
{ // This will throw if the root nodes are not available locally
updateHash();
if (mTransHash.isNonZero())
mTransactionMap->fetchRoot(mTransHash);
if (mAccountHash.isNonZero())
mAccountStateMap->fetchRoot(mAccountHash);
mTransactionMap->setImmutable();
mAccountStateMap->setImmutable();
}
Ledger::Ledger(Ledger& ledger, bool isMutable) : mTotCoins(ledger.mTotCoins), mLedgerSeq(ledger.mLedgerSeq),
@@ -62,7 +71,8 @@ Ledger::Ledger(bool /* dummy */, Ledger& prevLedger) :
mTotCoins(prevLedger.mTotCoins), mLedgerSeq(prevLedger.mLedgerSeq + 1),
mParentCloseTime(prevLedger.mCloseTime), mCloseResolution(prevLedger.mCloseResolution),
mCloseFlags(0), mClosed(false), mValidHash(false), mAccepted(false), mImmutable(false),
mTransactionMap(new SHAMap(smtTRANSACTION)), mAccountStateMap(prevLedger.mAccountStateMap->snapShot(true))
mTransactionMap(boost::make_shared<SHAMap>(smtTRANSACTION)),
mAccountStateMap(prevLedger.mAccountStateMap->snapShot(true))
{ // Create a new ledger that follows this one
prevLedger.updateHash();
mParentHash = prevLedger.getHash();
@@ -328,10 +338,11 @@ void Ledger::saveAcceptedLedger(Ledger::ref ledger)
ledger->mAccountHash.GetHex() % ledger->mTransHash.GetHex()));
// write out dirty nodes
while(ledger->mTransactionMap->flushDirty(256, hotTRANSACTION_NODE, ledger->mLedgerSeq))
{ ; }
while(ledger->mAccountStateMap->flushDirty(256, hotACCOUNT_NODE, ledger->mLedgerSeq))
{ ; }
int fc;
while ((fc = ledger->mTransactionMap->flushDirty(256, hotTRANSACTION_NODE, ledger->mLedgerSeq)) > 0)
{ cLog(lsINFO) << "Flushed " << fc << " dirty transaction nodes"; }
while ((fc = ledger->mAccountStateMap->flushDirty(256, hotACCOUNT_NODE, ledger->mLedgerSeq)) > 0)
{ cLog(lsINFO) << "Flushed " << fc << " dirty state nodes"; }
ledger->disarmDirty();
SHAMap& txSet = *ledger->peekTransactionMap();
@@ -425,7 +436,7 @@ Ledger::pointer Ledger::getSQL(const std::string& sql)
}
Ledger::pointer ret = Ledger::pointer(new Ledger(prevHash, transHash, accountHash, totCoins,
closingTime, prevClosingTime, closeFlags, closeResolution, ledgerSeq, true));
closingTime, prevClosingTime, closeFlags, closeResolution, ledgerSeq));
if (ret->getHash() != ledgerHash)
{
if (sLog(lsERROR))

View File

@@ -93,7 +93,7 @@ public:
Ledger(const uint256 &parentHash, const uint256 &transHash, const uint256 &accountHash,
uint64 totCoins, uint32 closeTime, uint32 parentCloseTime, int closeFlags, int closeResolution,
uint32 ledgerSeq, bool immutable); // used for database ledgers
uint32 ledgerSeq); // used for database ledgers
Ledger(const std::vector<unsigned char>& rawLedger);

View File

@@ -96,6 +96,45 @@ LedgerAcquire::LedgerAcquire(const uint256& hash) : PeerSet(hash, LEDGER_ACQUIRE
#endif
}
bool LedgerAcquire::tryLocal()
{ // return value: true = no more work to do
HashedObject::pointer node = theApp->getHashedObjectStore().retrieve(mHash);
if (!node)
return false;
mLedger = boost::make_shared<Ledger>(strCopy(node->getData()));
assert(mLedger->getHash() == mHash);
mHaveBase = true;
if (!mLedger->getTransHash())
mHaveTransactions = true;
else
{
try
{
mLedger->peekTransactionMap()->fetchRoot(mLedger->getTransHash());
}
catch (SHAMapMissingNode&)
{
}
}
if (!mLedger->getAccountHash())
mHaveState = true;
else
{
try
{
mLedger->peekAccountStateMap()->fetchRoot(mLedger->getAccountHash());
}
catch (SHAMapMissingNode&)
{
}
}
return mHaveTransactions && mHaveState;
}
void LedgerAcquire::onTimer()
{
if (getTimeouts() > 6)
@@ -124,7 +163,7 @@ void LedgerAcquire::done()
setComplete();
mLock.lock();
triggers = mOnComplete;
mOnComplete.empty();
mOnComplete.clear();
mLock.unlock();
if (mLedger)

View File

@@ -94,6 +94,7 @@ public:
bool takeAsNode(const std::list<SHAMapNode>& IDs, const std::list<std::vector<unsigned char> >& data);
bool takeAsRootNode(const std::vector<unsigned char>& data);
void trigger(Peer::ref, bool timer);
bool tryLocal();
};
class LedgerAcquireMaster

View File

@@ -585,7 +585,6 @@ bool NetworkOPs::checkLastClosedLedger(const std::vector<Peer::pointer>& peerLis
if (!mAcquiringLedger->isComplete())
{ // add more peers
int count = 0;
std::vector<Peer::pointer> peers=theApp->getConnectionPool().getPeerVector();
BOOST_FOREACH(Peer::ref it, peerList)
{
if (it->getClosedLedgerHash() == closedLedger)

View File

@@ -69,6 +69,6 @@ CKey::pointer PubKeyCache::store(const NewcoinAddress& id, const CKey::pointer&
void PubKeyCache::clear()
{
boost::mutex::scoped_lock sl(mLock);
mCache.empty();
mCache.clear();
}
// vim:ts=4

View File

@@ -1816,21 +1816,25 @@ void RippleCalc::pathNext(const PathState::pointer& pspCur, const int iPaths, co
lesCurrent.bumpSeq(); // Begin ledger varance.
pspCur->terStatus = calcNodeFwd(0, pspCur, bMultiQuality);
}
tLog(tesSUCCESS == pspCur->terStatus, lsDEBUG)
if (tesSUCCESS == pspCur->terStatus)
{
tLog(!pspCur->saInPass || !pspCur->saOutPass, lsDEBUG)
<< boost::str(boost::format("saOutPass=%s saInPass=%s")
% pspCur->saOutPass.getFullText()
% pspCur->saInPass.getFullText());
// Make sure we have a quality.
assert(tesSUCCESS != pspCur->terStatus || (!!pspCur->saOutPass && !!pspCur->saInPass));
assert(!!pspCur->saOutPass && !!pspCur->saInPass);
pspCur->uQuality = tesSUCCESS == pspCur->terStatus
? STAmount::getRate(pspCur->saOutPass, pspCur->saInPass) // Calculate relative quality.
: 0; // Mark path as inactive.
pspCur->uQuality = STAmount::getRate(pspCur->saOutPass, pspCur->saInPass); // Calculate relative quality.
cLog(lsINFO) << "Path after forward: " << pspCur->getJson();
}
else
{
pspCur->uQuality = 0;
}
}
// XXX Stand alone calculation not implemented, does not calculate required input.
@@ -1968,12 +1972,27 @@ TER RippleCalc::rippleCalc(
nothing();
}
else if ((!bLimitQuality || pspCur->uQuality <= uQualityLimit) // Quality is not limted or increment has allowed quality.
|| !pspBest // Best is not yet set.
|| PathState::lessPriority(pspBest, pspCur)) // Current is better than set.
{
lesActive.swapWith(pspCur->lesEntries); // For the path, save ledger state.
pspBest = pspCur;
else {
tLog(!pspCur->saInPass || !pspCur->saOutPass, lsDEBUG)
<< boost::str(boost::format("calcOfferFirst: better: uQuality=%016lX saInPass=%s saOutPass=%s")
% pspCur->uQuality
% pspCur->saInPass.getFullText()
% pspCur->saOutPass.getFullText());
assert(!!pspCur->saInPass && !!pspCur->saOutPass);
if ((!bLimitQuality || pspCur->uQuality <= uQualityLimit) // Quality is not limted or increment has allowed quality.
|| !pspBest // Best is not yet set.
|| PathState::lessPriority(pspBest, pspCur)) // Current is better than set.
{
cLog(lsDEBUG) << boost::str(boost::format("calcOfferFirst: better: uQuality=%016lX saInPass=%s saOutPass=%s")
% pspCur->uQuality
% pspCur->saInPass.getFullText()
% pspCur->saOutPass.getFullText());
lesActive.swapWith(pspCur->lesEntries); // For the path, save ledger state.
pspBest = pspCur;
}
}
}
}
@@ -1982,6 +2001,11 @@ TER RippleCalc::rippleCalc(
{
// Apply best path.
cLog(lsDEBUG) << boost::str(boost::format("calcOfferFirst: best: uQuality=%016lX saInPass=%s saOutPass=%s")
% pspBest->uQuality
% pspBest->saInPass.getFullText()
% pspBest->saOutPass.getFullText());
// Record best pass' offers that became unfunded for deletion on success.
vuUnfundedBecame.insert(vuUnfundedBecame.end(), pspBest->vUnfundedBecame.begin(), pspBest->vUnfundedBecame.end());
@@ -1991,7 +2015,7 @@ TER RippleCalc::rippleCalc(
saInAct += pspBest->saInPass;
saOutAct += pspBest->saOutPass;
if (temUNCERTAIN == terResult && saOutAct == saDstAmountReq)
if (saOutAct == saDstAmountReq)
{
// Done. Delivered requested amount.

View File

@@ -690,7 +690,10 @@ SHAMapTreeNode::pointer SHAMap::fetchNodeExternal(const SHAMapNode& id, const ui
HashedObject::pointer obj(theApp->getHashedObjectStore().retrieve(hash));
if (!obj)
{
Log(lsTRACE) << "fetchNodeExternal: missing " << hash;
throw SHAMapMissingNode(mType, id, hash);
}
assert(Serializer::getSHA512Half(obj->getData()) == hash);
try
@@ -708,6 +711,13 @@ SHAMapTreeNode::pointer SHAMap::fetchNodeExternal(const SHAMapNode& id, const ui
}
}
void SHAMap::fetchRoot(const uint256& hash)
{
root = fetchNodeExternal(SHAMapNode(), hash);
root->makeInner();
mTNByID[*root] = root;
}
void SHAMap::armDirty()
{ // begin saving dirty nodes
++mSeq;
@@ -725,6 +735,8 @@ int SHAMap::flushDirty(int maxNodes, HashedObjectType t, uint32 seq)
boost::unordered_map<SHAMapNode, SHAMapTreeNode::pointer>::iterator it = dirtyNodes.begin();
while (it != dirtyNodes.end())
{
tLog(mType == smtTRANSACTION, lsDEBUG) << "TX node write " << it->first;
tLog(mType == smtSTATE, lsDEBUG) << "STATE node write " << it->first;
s.erase();
it->second->addRaw(s, snfPREFIX);
theApp->getHashedObjectStore().store(t, seq, s.peekData(), s.getSHA512Half());

View File

@@ -273,6 +273,8 @@ public:
bool hasTargetIndex() const { return !mTargetIndex.isZero(); }
};
extern std::ostream& operator<<(std::ostream&, const SHAMapMissingNode&);
class SHAMap
{
public:
@@ -330,6 +332,7 @@ public:
ScopedLock Lock() const { return ScopedLock(mLock); }
bool hasNode(const SHAMapNode& id);
void fetchRoot(const uint256& hash);
// normal hash access functions
bool hasItem(const uint256& id);

View File

@@ -18,6 +18,9 @@
std::string SHAMapNode::getString() const
{
if ((mDepth == 0) && (mNodeID.isZero()))
return "NodeID(root)";
return str(boost::format("NodeID(%s,%s)")
% boost::lexical_cast<std::string>(mDepth)
% mNodeID.GetHex());
@@ -507,4 +510,17 @@ bool SHAMapTreeNode::setChildHash(int m, const uint256 &hash)
return updateHash();
}
std::ostream& operator<<(std::ostream& out, const SHAMapMissingNode& mn)
{
if (mn.getMapType() == smtTRANSACTION)
out << "Missing/TXN(" << mn.getNodeID() << ")";
else if (mn.getMapType() == smtSTATE)
out << "Missing/STA(" << mn.getNodeID() << ")";
else
out << "Missing/" << mn.getNodeID();
return out;
}
// vim:ts=4

View File

@@ -120,8 +120,8 @@ std::auto_ptr<SerializedType> STObject::makeDeserializedObject(SerializedTypeID
void STObject::set(const std::vector<SOElement::ptr>& type)
{
mData.empty();
mType.empty();
mData.clear();
mType.clear();
BOOST_FOREACH(const SOElement::ptr& elem, type)
{
@@ -138,7 +138,7 @@ bool STObject::setType(const std::vector<SOElement::ptr> &type)
boost::ptr_vector<SerializedType> newData;
bool valid = true;
mType.empty();
mType.clear();
BOOST_FOREACH(const SOElement::ptr& elem, type)
{
bool match = false;
@@ -204,7 +204,7 @@ bool STObject::isFieldAllowed(SField::ref field)
bool STObject::set(SerializerIterator& sit, int depth)
{ // return true = terminated with end-of-object
mData.empty();
mData.clear();
while (!sit.empty())
{
int type, field;

34
test/amount-test.js Normal file
View File

@@ -0,0 +1,34 @@
var buster = require("buster");
var amount = require("../js/amount.js");
buster.testCase("Amount", {
"UInt160" : {
"Parse 0" : function () {
buster.assert.equals(0, amount.UInt160.from_json("0").value);
},
"Parse 0 export" : function () {
buster.assert.equals(amount.consts.hex_xns, amount.UInt160.from_json("0").to_json());
},
"Parse native 123" : function () {
buster.assert.equals("123/XNS", amount.Amount.from_json("123").to_text_full());
},
"Parse native 12.3" : function () {
buster.assert.equals("12300000/XNS", amount.Amount.from_json("12.3").to_text_full());
},
"Parse 123./USD/iHb9CJAWyB4ij91VRWn96DkukG4bwdtyTh" : function () {
buster.assert.equals("123/USD/iHb9CJAWyB4ij91VRWn96DkukG4bwdtyTh", amount.Amount.from_json("123./USD/iHb9CJAWyB4ij91VRWn96DkukG4bwdtyTh").to_text_full());
},
"Parse 12300/USD/iHb9CJAWyB4ij91VRWn96DkukG4bwdtyTh" : function () {
buster.assert.equals("12300/USD/iHb9CJAWyB4ij91VRWn96DkukG4bwdtyTh", amount.Amount.from_json("12300/USD/iHb9CJAWyB4ij91VRWn96DkukG4bwdtyTh").to_text_full());
},
"Parse 12.3/USD/iHb9CJAWyB4ij91VRWn96DkukG4bwdtyTh" : function () {
buster.assert.equals("12.3/USD/iHb9CJAWyB4ij91VRWn96DkukG4bwdtyTh", amount.Amount.from_json("12.3/USD/iHb9CJAWyB4ij91VRWn96DkukG4bwdtyTh").to_text_full());
},
"Parse 1.2300/USD/iHb9CJAWyB4ij91VRWn96DkukG4bwdtyTh" : function () {
buster.assert.equals("1.23/USD/iHb9CJAWyB4ij91VRWn96DkukG4bwdtyTh", amount.Amount.from_json("1.2300/USD/iHb9CJAWyB4ij91VRWn96DkukG4bwdtyTh").to_text_full());
},
}
});
// vim:sw=2:sts=2:ts=8

View File

@@ -8,148 +8,148 @@
// Servers are created in tmp/server/$server
//
var config = require("./config.js");
var utils = require("../js/utils.js");
var config = require("./config.js");
var nodeutils = require("../js/nodeutils.js");
var fs = require("fs");
var path = require("path");
var util = require("util");
var child = require("child_process");
var fs = require("fs");
var path = require("path");
var util = require("util");
var child = require("child_process");
var servers = {};
// Create a server object
var Server = function(name) {
this.name = name;
this.name = name;
};
// Return a server's newcoind.cfg as string.
Server.method('configContent', function() {
var cfg = config.servers[this.name];
Server.prototype.configContent = function() {
var cfg = config.servers[this.name];
return Object.keys(cfg).map(function(o) {
return util.format("[%s]\n%s\n", o, cfg[o]);
}).join("");
});
return Object.keys(cfg).map(function(o) {
return util.format("[%s]\n%s\n", o, cfg[o]);
}).join("");
};
Server.method('serverPath', function() {
Server.prototype.serverPath = function() {
return "tmp/server/" + this.name;
});
};
Server.method('configPath', function() {
return path.join(this.serverPath(), "newcoind.cfg");
});
Server.prototype.configPath = function() {
return path.join(this.serverPath(), "newcoind.cfg");
};
// Write a server's newcoind.cfg.
Server.method('writeConfig', function(done) {
fs.writeFile(this.configPath(), this.configContent(), 'utf8', done);
});
Server.prototype.writeConfig = function(done) {
fs.writeFile(this.configPath(), this.configContent(), 'utf8', done);
};
// Spawn the server.
Server.method('serverSpawnSync', function() {
// Spawn in standalone mode for now.
this.child = child.spawn(
config.newcoind,
[
"-a",
"-v",
"--conf=newcoind.cfg"
],
{
cwd: this.serverPath(),
env: process.env,
stdio: 'inherit'
});
Server.prototype.serverSpawnSync = function() {
// Spawn in standalone mode for now.
this.child = child.spawn(
config.newcoind,
[
"-a",
"-v",
"--conf=newcoind.cfg"
],
{
cwd: this.serverPath(),
env: process.env,
stdio: 'inherit'
});
console.log("server: start %s: %s -a --conf=%s", this.child.pid, config.newcoind, this.configPath());
console.log("server: start %s: %s -a --conf=%s", this.child.pid, config.newcoind, this.configPath());
// By default, just log exits.
this.child.on('exit', function(code, signal) {
// If could not exec: code=127, signal=null
// If regular exit: code=0, signal=null
console.log("server: spawn: server exited code=%s: signal=%s", code, signal);
});
// By default, just log exits.
this.child.on('exit', function(code, signal) {
// If could not exec: code=127, signal=null
// If regular exit: code=0, signal=null
console.log("server: spawn: server exited code=%s: signal=%s", code, signal);
});
});
};
// Prepare server's working directory.
Server.method('makeBase', function(done) {
Server.prototype.makeBase = function(done) {
var path = this.serverPath();
var self = this;
var self = this;
// Reset the server directory, build it if needed.
utils.resetPath(path, '0777', function(e) {
if (e) {
throw e;
}
else {
self.writeConfig(done);
}
});
});
nodeutils.resetPath(path, '0777', function(e) {
if (e) {
throw e;
}
else {
self.writeConfig(done);
}
});
};
// Create a standalone server.
// Prepare the working directory and spawn the server.
Server.method('start', function(done) {
var self = this;
Server.prototype.start = function(done) {
var self = this;
this.makeBase(function(e) {
if (e) {
throw e;
}
else {
self.serverSpawnSync();
done();
}
});
});
if (e) {
throw e;
}
else {
self.serverSpawnSync();
done();
}
});
};
// Stop a standalone server.
Server.method('stop', function(done) {
if (this.child) {
// Update the on exit to invoke done.
this.child.on('exit', function(code, signal) {
console.log("server: stop: server exited");
done();
});
this.child.kill();
}
else
{
console.log("server: stop: no such server");
done();
}
});
Server.prototype.stop = function(done) {
if (this.child) {
// Update the on exit to invoke done.
this.child.on('exit', function(code, signal) {
console.log("server: stop: server exited");
done();
});
this.child.kill();
}
else
{
console.log("server: stop: no such server");
done('noSuchServer');
}
};
// Start the named server.
exports.start = function(name, done) {
if (servers[name])
{
console.log("server: start: server already started.");
}
else
{
var server = new Server(name);
if (servers[name])
{
console.log("server: start: server already started.");
}
else
{
var server = new Server(name);
servers[name] = server;
servers[name] = server;
console.log("server: start: %s", JSON.stringify(server));
console.log("server: start: %s", JSON.stringify(server));
server.start(done);
}
server.start(done);
}
};
// Delete the named server.
exports.stop = function(name, done) {
console.log("server: stop: %s of %s", name, Object.keys(servers).toString());
console.log("server: stop: %s of %s", name, Object.keys(servers).toString());
var server = servers[name];
if (server) {
server.stop(done);
delete servers[name];
}
var server = servers[name];
if (server) {
server.stop(done);
delete servers[name];
}
};
exports.Server = Server;
// vim:ts=4
// vim:sw=2:sts=2:ts=8

View File

@@ -1,9 +1,11 @@
var fs = require("fs");
var buster = require("buster");
var server = require("./server.js");
var remote = require("../js/remote.js");
var config = require("./config.js");
var server = require("./server.js");
var amount = require("../js/amount.js");
var remote = require("../js/remote.js");
var Amount = amount.Amount;
// How long to wait for server to start.
var serverDelay = 1500;
@@ -114,8 +116,8 @@ buster.testCase("Websocket commands", {
alpha.request_ledger_entry({
'ledger_closed' : r.ledger_closed,
'type' : 'account_root',
'account_root' : 'iHb9CJAWyB4ij91VRWn96DkukG4bwdtyTh'
'type' : 'account_root',
'account_root' : 'iHb9CJAWyB4ij91VRWn96DkukG4bwdtyTh'
} , function (r) {
// console.log("account_root: %s", JSON.stringify(r));
@@ -134,8 +136,8 @@ buster.testCase("Websocket commands", {
alpha.request_ledger_entry({
'ledger_closed' : r.ledger_closed,
'type' : 'account_root',
'account_root' : 'foobar'
'type' : 'account_root',
'account_root' : 'foobar'
} , function (r) {
// console.log("account_root: %s", JSON.stringify(r));
@@ -154,8 +156,8 @@ buster.testCase("Websocket commands", {
alpha.request_ledger_entry({
'ledger_closed' : r.ledger_closed,
'type' : 'account_root',
'account_root' : 'iG1QQv2nh2gi7RCZ1P8YYcBUKCCN633jCn'
'type' : 'account_root',
'account_root' : 'iG1QQv2nh2gi7RCZ1P8YYcBUKCCN633jCn'
}, function (r) {
console.log("account_root: %s", JSON.stringify(r));
@@ -174,8 +176,8 @@ buster.testCase("Websocket commands", {
alpha.request_ledger_entry({
'ledger_closed' : r.ledger_closed,
'type' : 'account_root',
'index' : "2B6AC232AA4C4BE41BF49D2459FA4A0347E1B543A4C92FCEE0821C0201E2E9A8",
'type' : 'account_root',
'index' : "2B6AC232AA4C4BE41BF49D2459FA4A0347E1B543A4C92FCEE0821C0201E2E9A8",
} , function (r) {
console.log("node: %s", JSON.stringify(r));
@@ -187,7 +189,7 @@ buster.testCase("Websocket commands", {
'create account' :
function (done) {
alpha.send_xns(undefined, 'root', 'alice', 10000, true, function (r) {
alpha.send(undefined, 'root', 'alice', Amount.from_json("10000"), undefined, 'CREATE', function (r) {
console.log(r);
buster.refute(r.error);