Browse Source

Merge pull request #21 from matiu/chore/remove-tabs

replace tabs with 2 spaces using "expand"
patch-2
Ryan X. Charles 11 years ago
parent
commit
8221293922
  1. 12
      Block.js
  2. 220
      Bloom.js
  3. 6
      Connection.js
  4. 6
      Deserialize.js
  5. 8
      SIN.js
  6. 74
      SINKey.js
  7. 110
      Script.js
  8. 130
      ScriptInterpreter.js
  9. 238
      Sign.js
  10. 270
      Wallet.js
  11. 84
      WalletKey.js
  12. 6
      const.js
  13. 156
      test/basic.js
  14. 72
      util/EncFile.js
  15. 2
      util/time.js
  16. 34
      util/util.js

12
Block.js

@ -39,12 +39,12 @@ function spec(b) {
Block.prototype.getHeader = function getHeader() { Block.prototype.getHeader = function getHeader() {
var buf = new Buffer(80); var buf = new Buffer(80);
var ofs = 0; var ofs = 0;
buf.writeUInt32LE(this.version, ofs); ofs += 4; buf.writeUInt32LE(this.version, ofs); ofs += 4;
this.prev_hash.copy(buf, ofs); ofs += 32; this.prev_hash.copy(buf, ofs); ofs += 32;
this.merkle_root.copy(buf, ofs); ofs += 32; this.merkle_root.copy(buf, ofs); ofs += 32;
buf.writeUInt32LE(this.timestamp, ofs); ofs += 4; buf.writeUInt32LE(this.timestamp, ofs); ofs += 4;
buf.writeUInt32LE(this.bits, ofs); ofs += 4; buf.writeUInt32LE(this.bits, ofs); ofs += 4;
buf.writeUInt32LE(this.nonce, ofs); ofs += 4; buf.writeUInt32LE(this.nonce, ofs); ofs += 4;
return buf; return buf;
}; };

220
Bloom.js

@ -1,116 +1,116 @@
require('classtool'); require('classtool');
function ClassSpec(b) { function ClassSpec(b) {
var MAX_BLOOM_FILTER_SIZE = 36000; // bytes var MAX_BLOOM_FILTER_SIZE = 36000; // bytes
var MAX_HASH_FUNCS = 50; var MAX_HASH_FUNCS = 50;
var LN2SQUARED = 0.4804530139182014246671025263266649717305529515945455; var LN2SQUARED = 0.4804530139182014246671025263266649717305529515945455;
var LN2 = 0.6931471805599453094172321214581765680755001343602552; var LN2 = 0.6931471805599453094172321214581765680755001343602552;
var bit_mask = [0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80]; var bit_mask = [0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80];
function Bloom() { function Bloom() {
this.data = ''; this.data = '';
this.hashFuncs = 0; this.hashFuncs = 0;
}; };
function ROTL32(x, r) { function ROTL32(x, r) {
return (x << r) | (x >> (32 - r)); return (x << r) | (x >> (32 - r));
}; };
function getBlockU32(blockIdx, data) { function getBlockU32(blockIdx, data) {
var idx = blockIdx * 4; var idx = blockIdx * 4;
var v = (data[idx + 0] << (0 * 8)) | var v = (data[idx + 0] << (0 * 8)) |
(data[idx + 1] << (1 * 8)) | (data[idx + 1] << (1 * 8)) |
(data[idx + 2] << (2 * 8)) | (data[idx + 2] << (2 * 8)) |
(data[idx + 3] << (3 * 8)); (data[idx + 3] << (3 * 8));
return v; return v;
}; };
Bloom.prototype.hash = function(hashNum, data) { Bloom.prototype.hash = function(hashNum, data) {
var h1 = hashNum * (0xffffffff / (this.hashFuncs - 1)); var h1 = hashNum * (0xffffffff / (this.hashFuncs - 1));
var c1 = 0xcc9e2d51; var c1 = 0xcc9e2d51;
var c2 = 0x1b873593; var c2 = 0x1b873593;
var nBlocks = data.length / 4; var nBlocks = data.length / 4;
// data body // data body
for (var i = -nBlocks; i; i++) { for (var i = -nBlocks; i; i++) {
var k1 = getBlockU32(i); var k1 = getBlockU32(i);
k1 *= c1; k1 *= c1;
k1 = ROTLF32(k1, 15); k1 = ROTLF32(k1, 15);
k1 *= c2; k1 *= c2;
h1 ^= k1; h1 ^= k1;
h1 = ROTFL(h1, 13); h1 = ROTFL(h1, 13);
h1 = h1 * 5 + 0xe6546b64; h1 = h1 * 5 + 0xe6546b64;
} }
// tail (trailing 1-3 bytes) // tail (trailing 1-3 bytes)
var tail = data.slice(nBlocks * 4); var tail = data.slice(nBlocks * 4);
var k1 = 0; var k1 = 0;
switch (data.length & 3) { switch (data.length & 3) {
case 3: k1 ^= tail[2] << 16; case 3: k1 ^= tail[2] << 16;
case 2: k1 ^= tail[1] << 8; case 2: k1 ^= tail[1] << 8;
case 1: k1 ^= tail[0]; case 1: k1 ^= tail[0];
k1 *= c1; k1 *= c1;
k1 = ROTL32(k1, 15); k1 = ROTL32(k1, 15);
k1 *= c2; k1 *= c2;
h1 ^= k1; h1 ^= k1;
} }
// finalize // finalize
h1 ^= data.length; h1 ^= data.length;
h1 ^= h1 >> 16; h1 ^= h1 >> 16;
h1 *= 0x85ebca6b; h1 *= 0x85ebca6b;
h1 ^= h1 >> 13; h1 ^= h1 >> 13;
h1 *= 0xc2b2ae35; h1 *= 0xc2b2ae35;
h1 ^= h1 >> 16; h1 ^= h1 >> 16;
return h1 % (this.data.length * 8); return h1 % (this.data.length * 8);
}; };
Bloom.prototype.insert = function(data) { Bloom.prototype.insert = function(data) {
for (var i = 0; i < this.hashFuncs; i++) { for (var i = 0; i < this.hashFuncs; i++) {
var index = this.hash(i, data); var index = this.hash(i, data);
this.data[index >> 3] |= bit_mask[7 & index]; this.data[index >> 3] |= bit_mask[7 & index];
} }
}; };
Bloom.prototype.contains = function(data) { Bloom.prototype.contains = function(data) {
for (var i = 0; i < this.hashFuncs; i++) { for (var i = 0; i < this.hashFuncs; i++) {
var index = this.hash(i, data); var index = this.hash(i, data);
if (!(this.data[index >> 3] & bit_mask[7 & index])) if (!(this.data[index >> 3] & bit_mask[7 & index]))
return false; return false;
} }
return true; return true;
}; };
Bloom.prototype.sizeOk = function() { Bloom.prototype.sizeOk = function() {
return this.data.length <= MAX_BLOOM_FILTER_SIZE && return this.data.length <= MAX_BLOOM_FILTER_SIZE &&
this.hashFuncs <= MAX_HASH_FUNCS; this.hashFuncs <= MAX_HASH_FUNCS;
}; };
function toInt(v) { function toInt(v) {
return ~~v; return ~~v;
} }
function min(a, b) { function min(a, b) {
if (a < b) if (a < b)
return a; return a;
return b; return b;
} }
Bloom.prototype.init = function(elements, FPRate) { Bloom.prototype.init = function(elements, FPRate) {
var filterSize = min(toInt(-1.0 / LN2SQUARED * elements * Math.log(FPRate)), var filterSize = min(toInt(-1.0 / LN2SQUARED * elements * Math.log(FPRate)),
MAX_BLOOM_FILTER_SIZE * 8) / 8; MAX_BLOOM_FILTER_SIZE * 8) / 8;
this.data[filterSize] = 0; this.data[filterSize] = 0;
this.hashFuncs = min(toInt(this.data.length * 8 / elements * LN2), this.hashFuncs = min(toInt(this.data.length * 8 / elements * LN2),
MAX_HASH_FUNCS); MAX_HASH_FUNCS);
}; };
return Bloom; return Bloom;
}; };
module.defineClass(ClassSpec); module.defineClass(ClassSpec);

6
Connection.js

@ -443,8 +443,8 @@ function spec(b) {
data.headers = []; data.headers = [];
for (i = 0; i < data.count; i++) { for (i = 0; i < data.count; i++) {
var header = new Block(); var header = new Block();
header.parse(parser); header.parse(parser);
data.headers.push(header); data.headers.push(header);
} }
break; break;
@ -474,7 +474,7 @@ function spec(b) {
lock_time: tx.lock_time, lock_time: tx.lock_time,
ins: tx.ins, ins: tx.ins,
outs: tx.outs, outs: tx.outs,
tx: tx, tx: tx,
}; };
case 'getblocks': case 'getblocks':

6
Deserialize.js

@ -1,8 +1,8 @@
exports.intFromCompact = function(c) exports.intFromCompact = function(c)
{ {
var bytes = ((c >>> 24) & 0xff) >>> 0; var bytes = ((c >>> 24) & 0xff) >>> 0;
var v = ((c & 0xffffff) << (8 * (bytes - 3))) >>> 0; var v = ((c & 0xffffff) << (8 * (bytes - 3))) >>> 0;
return v; return v;
} }

8
SIN.js

@ -11,16 +11,16 @@ function ClassSpec(b) {
}; };
this.data = new Buffer(1 + 1 + payload.length); this.data = new Buffer(1 + 1 + payload.length);
this.__proto__ = this.encodings['binary']; this.__proto__ = this.encodings['binary'];
this.prefix(0x0F); // SIN magic number, in numberspace this.prefix(0x0F); // SIN magic number, in numberspace
this.type(type); this.type(type);
this.payload(payload); this.payload(payload);
}; };
SIN.superclass = superclass; SIN.superclass = superclass;
superclass.applyEncodingsTo(SIN); superclass.applyEncodingsTo(SIN);
SIN.SIN_PERSIST_MAINNET = 0x01; // associated with sacrifice TX SIN.SIN_PERSIST_MAINNET = 0x01; // associated with sacrifice TX
SIN.SIN_PERSIST_TESTNET = 0x11; // associated with sacrifice TX SIN.SIN_PERSIST_TESTNET = 0x11; // associated with sacrifice TX
SIN.SIN_EPHEM = 0x02; // generate off-net at any time SIN.SIN_EPHEM = 0x02; // generate off-net at any time
// get or set the prefix data (the first byte of the address) // get or set the prefix data (the first byte of the address)
SIN.prototype.prefix = function(num) { SIN.prototype.prefix = function(num) {

74
SINKey.js

@ -1,43 +1,43 @@
require('classtool'); require('classtool');
function ClassSpec(b) { function ClassSpec(b) {
var coinUtil = require('./util/util'); var coinUtil = require('./util/util');
var timeUtil = require('./util/time'); var timeUtil = require('./util/time');
var KeyModule = require('./Key'); var KeyModule = require('./Key');
var SIN = require('./SIN').class(); var SIN = require('./SIN').class();
function SINKey(cfg) { function SINKey(cfg) {
if (typeof cfg != 'object') if (typeof cfg != 'object')
cfg = {}; cfg = {};
this.created = cfg.created; this.created = cfg.created;
this.privKey = cfg.privKey; this.privKey = cfg.privKey;
}; };
SINKey.prototype.generate = function() { SINKey.prototype.generate = function() {
this.privKey = KeyModule.Key.generateSync(); this.privKey = KeyModule.Key.generateSync();
this.created = timeUtil.curtime(); this.created = timeUtil.curtime();
}; };
SINKey.prototype.pubkeyHash = function() { SINKey.prototype.pubkeyHash = function() {
return coinUtil.sha256ripe160(this.privKey.public); return coinUtil.sha256ripe160(this.privKey.public);
}; };
SINKey.prototype.storeObj = function() { SINKey.prototype.storeObj = function() {
var pubKey = this.privKey.public.toString('hex'); var pubKey = this.privKey.public.toString('hex');
var pubKeyHash = this.pubkeyHash(); var pubKeyHash = this.pubkeyHash();
var sin = new SIN(SIN.SIN_EPHEM, pubKeyHash); var sin = new SIN(SIN.SIN_EPHEM, pubKeyHash);
var obj = { var obj = {
created: this.created, created: this.created,
priv: this.privKey.private.toString('hex'), priv: this.privKey.private.toString('hex'),
pub: pubKey, pub: pubKey,
sin: sin.toString(), sin: sin.toString(),
}; };
return obj; return obj;
}; };
return SINKey; return SINKey;
}; };
module.defineClass(ClassSpec); module.defineClass(ClassSpec);

110
Script.js

@ -84,42 +84,42 @@ function spec(b) {
Script.prototype.isP2SH = function () Script.prototype.isP2SH = function ()
{ {
return (this.chunks.length == 3 && return (this.chunks.length == 3 &&
this.chunks[0] == OP_HASH160 && this.chunks[0] == OP_HASH160 &&
Buffer.isBuffer(this.chunks[1]) && Buffer.isBuffer(this.chunks[1]) &&
this.chunks[1].length == 20 && this.chunks[1].length == 20 &&
this.chunks[2] == OP_EQUAL); this.chunks[2] == OP_EQUAL);
}; };
Script.prototype.isPubkey = function () Script.prototype.isPubkey = function ()
{ {
return (this.chunks.length == 2 && return (this.chunks.length == 2 &&
Buffer.isBuffer(this.chunks[0]) && Buffer.isBuffer(this.chunks[0]) &&
this.chunks[1] == OP_CHECKSIG); this.chunks[1] == OP_CHECKSIG);
}; };
Script.prototype.isPubkeyHash = function () Script.prototype.isPubkeyHash = function ()
{ {
return (this.chunks.length == 5 && return (this.chunks.length == 5 &&
this.chunks[0] == OP_DUP && this.chunks[0] == OP_DUP &&
this.chunks[1] == OP_HASH160 && this.chunks[1] == OP_HASH160 &&
Buffer.isBuffer(this.chunks[2]) && Buffer.isBuffer(this.chunks[2]) &&
this.chunks[2].length == 20 && this.chunks[2].length == 20 &&
this.chunks[3] == OP_EQUALVERIFY && this.chunks[3] == OP_EQUALVERIFY &&
this.chunks[4] == OP_CHECKSIG); this.chunks[4] == OP_CHECKSIG);
}; };
function isSmallIntOp(opcode) function isSmallIntOp(opcode)
{ {
return ((opcode == OP_0) || return ((opcode == OP_0) ||
((opcode >= OP_1) && (opcode <= OP_16))); ((opcode >= OP_1) && (opcode <= OP_16)));
}; };
Script.prototype.isMultiSig = function () Script.prototype.isMultiSig = function ()
{ {
return (this.chunks.length > 3 && return (this.chunks.length > 3 &&
isSmallIntOp(this.chunks[0]) && isSmallIntOp(this.chunks[0]) &&
isSmallIntOp(this.chunks[this.chunks.length-2]) && isSmallIntOp(this.chunks[this.chunks.length-2]) &&
this.chunks[this.chunks.length-1] == OP_CHECKMULTISIG); this.chunks[this.chunks.length-1] == OP_CHECKMULTISIG);
}; };
Script.prototype.finishedMultiSig = function() Script.prototype.finishedMultiSig = function()
@ -166,60 +166,60 @@ function spec(b) {
// is this a script form we know? // is this a script form we know?
Script.prototype.classify = function () Script.prototype.classify = function ()
{ {
if (this.isPubkeyHash()) if (this.isPubkeyHash())
return TX_PUBKEYHASH; return TX_PUBKEYHASH;
if (this.isP2SH()) if (this.isP2SH())
return TX_SCRIPTHASH; return TX_SCRIPTHASH;
if (this.isMultiSig()) if (this.isMultiSig())
return TX_MULTISIG; return TX_MULTISIG;
if (this.isPubkey()) if (this.isPubkey())
return TX_PUBKEY; return TX_PUBKEY;
return TX_UNKNOWN; return TX_UNKNOWN;
}; };
// extract useful data items from known scripts // extract useful data items from known scripts
Script.prototype.capture = function () Script.prototype.capture = function ()
{ {
var txType = this.classify(); var txType = this.classify();
var res = []; var res = [];
switch (txType) { switch (txType) {
case TX_PUBKEY: case TX_PUBKEY:
res.push(this.chunks[0]); res.push(this.chunks[0]);
break; break;
case TX_PUBKEYHASH: case TX_PUBKEYHASH:
res.push(this.chunks[2]); res.push(this.chunks[2]);
break; break;
case TX_MULTISIG: case TX_MULTISIG:
for (var i = 1; i < (this.chunks.length - 2); i++) for (var i = 1; i < (this.chunks.length - 2); i++)
res.push(this.chunks[i]); res.push(this.chunks[i]);
break; break;
case TX_SCRIPTHASH: case TX_SCRIPTHASH:
res.push(this.chunks[1]); res.push(this.chunks[1]);
break; break;
case TX_UNKNOWN: case TX_UNKNOWN:
default: default:
// do nothing // do nothing
break; break;
} }
return res; return res;
}; };
// return first extracted data item from script // return first extracted data item from script
Script.prototype.captureOne = function () Script.prototype.captureOne = function ()
{ {
var arr = this.capture(); var arr = this.capture();
return arr[0]; return arr[0];
}; };
Script.prototype.getOutType = function () Script.prototype.getOutType = function ()
{ {
var txType = this.classify(); var txType = this.classify();
switch (txType) { switch (txType) {
case TX_PUBKEY: return 'Pubkey'; case TX_PUBKEY: return 'Pubkey';
case TX_PUBKEYHASH: return 'Address'; case TX_PUBKEYHASH: return 'Address';
default: return 'Strange'; default: return 'Strange';
} }
}; };
@ -446,7 +446,7 @@ function spec(b) {
var script = new Script(); var script = new Script();
script.writeN(n_required); script.writeN(n_required);
keys.forEach(function(key) { keys.forEach(function(key) {
script.writeBytes(key); script.writeBytes(key);
}); });
script.writeN(keys.length); script.writeN(keys.length);
script.writeOp(OP_CHECKMULTISIG); script.writeOp(OP_CHECKMULTISIG);

130
ScriptInterpreter.js

@ -918,85 +918,85 @@ function spec(b) {
}; };
function verifyStep4(scriptSig, scriptPubKey, txTo, nIn, function verifyStep4(scriptSig, scriptPubKey, txTo, nIn,
hashType, opts, callback, si, siCopy) hashType, opts, callback, si, siCopy)
{ {
if (siCopy.stack.length == 0) { if (siCopy.stack.length == 0) {
callback(null, false); callback(null, false);
return; return;
} }
callback(null, castBool(siCopy.stackBack())); callback(null, castBool(siCopy.stackBack()));
} }
function verifyStep3(scriptSig, scriptPubKey, txTo, nIn, function verifyStep3(scriptSig, scriptPubKey, txTo, nIn,
hashType, opts, callback, si, siCopy) hashType, opts, callback, si, siCopy)
{ {
if (si.stack.length == 0) { if (si.stack.length == 0) {
callback(null, false); callback(null, false);
return; return;
} }
if (castBool(si.stackBack()) == false) { if (castBool(si.stackBack()) == false) {
callback(null, false); callback(null, false);
return; return;
} }
// if not P2SH, we're done // if not P2SH, we're done
if (!opts.verifyP2SH || !scriptPubKey.isP2SH()) { if (!opts.verifyP2SH || !scriptPubKey.isP2SH()) {
callback(null, true); callback(null, true);
return; return;
} }
if (!scriptSig.isPushOnly()) { if (!scriptSig.isPushOnly()) {
callback(null, false); callback(null, false);
return; return;
} }
assert.notEqual(siCopy.length, 0); assert.notEqual(siCopy.length, 0);
var subscript = new Script(siCopy.stackPop()); var subscript = new Script(siCopy.stackPop());
ok = true; ok = true;
siCopy.eval(subscript, txTo, nIn, hashType, function (err) { siCopy.eval(subscript, txTo, nIn, hashType, function (err) {
if (err) if (err)
callback(err); callback(err);
else else
verifyStep4(scriptSig, scriptPubKey, txTo, nIn, verifyStep4(scriptSig, scriptPubKey, txTo, nIn,
hashType, opts, callback, si, siCopy); hashType, opts, callback, si, siCopy);
}); });
} }
function verifyStep2(scriptSig, scriptPubKey, txTo, nIn, function verifyStep2(scriptSig, scriptPubKey, txTo, nIn,
hashType, opts, callback, si, siCopy) hashType, opts, callback, si, siCopy)
{ {
if (opts.verifyP2SH) { if (opts.verifyP2SH) {
si.stack.forEach(function(item) { si.stack.forEach(function(item) {
siCopy.stack.push(item); siCopy.stack.push(item);
}); });
} }
si.eval(scriptPubKey, txTo, nIn, hashType, function (err) { si.eval(scriptPubKey, txTo, nIn, hashType, function (err) {
if (err) if (err)
callback(err); callback(err);
else else
verifyStep3(scriptSig, scriptPubKey, txTo, nIn, verifyStep3(scriptSig, scriptPubKey, txTo, nIn,
hashType, opts, callback, si, siCopy); hashType, opts, callback, si, siCopy);
}); });
} }
ScriptInterpreter.verifyFull = ScriptInterpreter.verifyFull =
function verifyFull(scriptSig, scriptPubKey, txTo, nIn, hashType, function verifyFull(scriptSig, scriptPubKey, txTo, nIn, hashType,
opts, callback) opts, callback)
{ {
var si = new ScriptInterpreter(); var si = new ScriptInterpreter();
var siCopy = new ScriptInterpreter(); var siCopy = new ScriptInterpreter();
si.eval(scriptSig, txTo, nIn, hashType, function (err) { si.eval(scriptSig, txTo, nIn, hashType, function (err) {
if (err) if (err)
callback(err); callback(err);
else else
verifyStep2(scriptSig, scriptPubKey, txTo, nIn, verifyStep2(scriptSig, scriptPubKey, txTo, nIn,
hashType, opts, callback, si, siCopy); hashType, opts, callback, si, siCopy);
}); });
}; };
var checkSig = ScriptInterpreter.checkSig = var checkSig = ScriptInterpreter.checkSig =

238
Sign.js

@ -1,133 +1,133 @@
function signOne(hash, addrStr, keys) function signOne(hash, addrStr, keys)
{ {
var keyObj = keys[addrStr]; var keyObj = keys[addrStr];
var rawPrivKey = new Buffer(keyObj.priv, 'hex'); var rawPrivKey = new Buffer(keyObj.priv, 'hex');
var key = new KeyModule.Key(); var key = new KeyModule.Key();
key.private = rawPrivKey; key.private = rawPrivKey;
var signature = key.signSync(hash); var signature = key.signSync(hash);
return signature; return signature;
} }
function signTxIn(nIn, tx, txInputs, network, keys, scripts) function signTxIn(nIn, tx, txInputs, network, keys, scripts)
{ {
// locate TX input needing a signature // locate TX input needing a signature
var txin = tx.ins[nIn]; var txin = tx.ins[nIn];
var scriptSig = txin.getScript(); var scriptSig = txin.getScript();
// locate TX output, within txInputs // locate TX output, within txInputs
var txoutHash = txin.getOutpointHash(); var txoutHash = txin.getOutpointHash();
if (!(txoutHash in txInputs)) if (!(txoutHash in txInputs))
throw new Error("signTxIn missing input hash"); throw new Error("signTxIn missing input hash");
var txFrom = txInputs[txoutHash]; var txFrom = txInputs[txoutHash];
var txoutIndex = txin.getOutpointIndex(); var txoutIndex = txin.getOutpointIndex();
if (txFrom.outs.length >= txoutIndex) if (txFrom.outs.length >= txoutIndex)
throw new Error("signTxIn missing input index"); throw new Error("signTxIn missing input index");
var txout = txFrom.outs[txoutIndex]; var txout = txFrom.outs[txoutIndex];
var scriptPubKey = txout.getScript(); var scriptPubKey = txout.getScript();
// detect type of transaction, and extract useful elements // detect type of transaction, and extract useful elements
var txType = scriptPubKey.classify(); var txType = scriptPubKey.classify();
if (txType == TX_UNKNOWN) if (txType == TX_UNKNOWN)
throw new Error("unknown TX type"); throw new Error("unknown TX type");
var scriptData = scriptPubKey.capture(); var scriptData = scriptPubKey.capture();
// if P2SH, lookup the script // if P2SH, lookup the script
var subscriptRaw = undefined; var subscriptRaw = undefined;
var subscript = undefined; var subscript = undefined;
var subType = undefined; var subType = undefined;
var subData = undefined; var subData = undefined;
if (txType == TX_SCRIPTHASH) { if (txType == TX_SCRIPTHASH) {
var addr = new Address(network.addressScript, scriptData[0]); var addr = new Address(network.addressScript, scriptData[0]);
var addrStr = addr.toString(); var addrStr = addr.toString();
if (!(addrStr in scripts)) if (!(addrStr in scripts))
throw new Error("unknown script hash address"); throw new Error("unknown script hash address");
subscriptRaw = new Buffer(scripts[addrStr], 'hex'); subscriptRaw = new Buffer(scripts[addrStr], 'hex');
subscript = new Script(subscriptRaw); subscript = new Script(subscriptRaw);
subType = subscript.classify(); subType = subscript.classify();
if (subType == TX_UNKNOWN) if (subType == TX_UNKNOWN)
throw new Error("unknown subscript TX type"); throw new Error("unknown subscript TX type");
subData = subscript.capture(); subData = subscript.capture();
} }
var hash = tx.hashForSignature(scriptPubKey, i, 0); var hash = tx.hashForSignature(scriptPubKey, i, 0);
switch (txType) { switch (txType) {
case TX_PUBKEY: case TX_PUBKEY:
// already signed // already signed
if (scriptSig.chunks.length > 0) if (scriptSig.chunks.length > 0)
return; return;
var pubkeyhash = util.sha256ripe160(scriptData[0]); var pubkeyhash = util.sha256ripe160(scriptData[0]);
var addr = new Address(network.addressPubkey, pubkeyhash); var addr = new Address(network.addressPubkey, pubkeyhash);
var addrStr = addr.toString(); var addrStr = addr.toString();
if (!(addrStr in keys)) if (!(addrStr in keys))
throw new Error("unknown pubkey"); throw new Error("unknown pubkey");
var signature = signOne(hash, addrStr, keys); var signature = signOne(hash, addrStr, keys);
scriptSig.writeBytes(signature); scriptSig.writeBytes(signature);
break; break;
case TX_PUBKEYHASH: case TX_PUBKEYHASH:
// already signed // already signed
if (scriptSig.chunks.length > 0) if (scriptSig.chunks.length > 0)
return; return;
var addr = new Address(network.addressPubkey, scriptData[0]); var addr = new Address(network.addressPubkey, scriptData[0]);
var addrStr = addr.toString(); var addrStr = addr.toString();
if (!(addrStr in keys)) if (!(addrStr in keys))
throw new Error("unknown pubkey hash address"); throw new Error("unknown pubkey hash address");
var signature = signOne(hash, addrStr, keys); var signature = signOne(hash, addrStr, keys);
scriptSig.writeBytes(signature); scriptSig.writeBytes(signature);
scriptSig.writeBytes(key.public); scriptSig.writeBytes(key.public);
break; break;
case TX_SCRIPTHASH: case TX_SCRIPTHASH:
// already signed // already signed
if (scriptSig.chunks.length > 0) if (scriptSig.chunks.length > 0)
return; return;
var addr = new Address(network.addressPubkey, subData[0]); var addr = new Address(network.addressPubkey, subData[0]);
var addrStr = addr.toString(); var addrStr = addr.toString();
if (!(addrStr in keys)) if (!(addrStr in keys))
throw new Error("unknown script(pubkey hash) address"); throw new Error("unknown script(pubkey hash) address");
var signature = signOne(hash, addrStr, keys); var signature = signOne(hash, addrStr, keys);
scriptSig.writeBytes(signature); scriptSig.writeBytes(signature);
scriptSig.writeBytes(key.public); scriptSig.writeBytes(key.public);
break; break;
case TX_MULTISIG: case TX_MULTISIG:
while (scriptSig.chunks.length < scriptData.length) { while (scriptSig.chunks.length < scriptData.length) {
scriptSig.writeBytes(util.EMPTY_BUFFER); scriptSig.writeBytes(util.EMPTY_BUFFER);
} }
for (var i = 0; i < scriptData.length; i++) { for (var i = 0; i < scriptData.length; i++) {
// skip already signed // skip already signed
if (scriptSig.chunks[i].length > 0) if (scriptSig.chunks[i].length > 0)
continue; continue;
var pubkeyhash = util.sha256ripe160(scriptSig.chunks[i]); var pubkeyhash = util.sha256ripe160(scriptSig.chunks[i]);
var addr = new Address(network.addressPubkey, pubkeyhash); var addr = new Address(network.addressPubkey, pubkeyhash);
var addrStr = addr.toString(); var addrStr = addr.toString();
if (!(addrStr in keys)) if (!(addrStr in keys))
continue; continue;
var signature = signOne(hash, addrStr, keys); var signature = signOne(hash, addrStr, keys);
scriptSig.chunks[i] = signature; scriptSig.chunks[i] = signature;
} }
break; break;
} }
if (txtype == TX_SCRIPTHASH) if (txtype == TX_SCRIPTHASH)
scriptSig.writeBytes(subscriptRaw); scriptSig.writeBytes(subscriptRaw);
} }
exports.Transaction = function Transaction(tx, txInputs, network, keys, scripts) exports.Transaction = function Transaction(tx, txInputs, network, keys, scripts)
{ {
for (var i = 0; i < tx.ins.length; i++) for (var i = 0; i < tx.ins.length; i++)
signTxIn(i, tx, txInputs, network, keys, scripts); signTxIn(i, tx, txInputs, network, keys, scripts);
}; };

270
Wallet.js

@ -2,141 +2,141 @@ require('classtool');
var hex = function(hex) {return new Buffer(hex, 'hex');}; var hex = function(hex) {return new Buffer(hex, 'hex');};
function ClassSpec(b) { function ClassSpec(b) {
var fs = require('fs'); var fs = require('fs');
var EncFile = require('./util/EncFile'); var EncFile = require('./util/EncFile');
var Address = require('./Address').class(); var Address = require('./Address').class();
var networks = require('./networks'); var networks = require('./networks');
var util = b.util || require('./util/util'); var util = b.util || require('./util/util');
var ENC_METHOD = 'aes-256-cbc'; var ENC_METHOD = 'aes-256-cbc';
var skeleton = { var skeleton = {
client: 'libcoin', client: 'libcoin',
client_version: '0.0.1', client_version: '0.0.1',
network: 'testnet', network: 'testnet',
version: 1, version: 1,
best_hash: null, best_hash: null,
best_height: -1, best_height: -1,
keys: [], keys: [],
sin: {}, sin: {},
scripts: {}, scripts: {},
}; };
function Wallet(cfg) { function Wallet(cfg) {
if (typeof cfg !== 'object') if (typeof cfg !== 'object')
cfg = {}; cfg = {};
// deep copy (no references) // deep copy (no references)
if (cfg.datastore) if (cfg.datastore)
this.datastore = JSON.parse(JSON.stringify(cfg.datastore)); this.datastore = JSON.parse(JSON.stringify(cfg.datastore));
else else
this.datastore = JSON.parse(JSON.stringify(skeleton)); this.datastore = JSON.parse(JSON.stringify(skeleton));
this.network = undefined; this.network = undefined;
this.dirty = cfg.dirty || true; this.dirty = cfg.dirty || true;
}; };
Wallet.prototype.readSync = function(filename, passphrase) { Wallet.prototype.readSync = function(filename, passphrase) {
this.datastore = EncFile.readJFileSync(ENC_METHOD, this.datastore = EncFile.readJFileSync(ENC_METHOD,
passphrase, filename); passphrase, filename);
this.dirty = false; this.dirty = false;
}; };
Wallet.prototype.writeSync = function(filename, passphrase) { Wallet.prototype.writeSync = function(filename, passphrase) {
var tmp_fn = filename + ".tmp"; var tmp_fn = filename + ".tmp";
EncFile.writeJFileSync(ENC_METHOD, passphrase, tmp_fn, EncFile.writeJFileSync(ENC_METHOD, passphrase, tmp_fn,
this.datastore); this.datastore);
fs.renameSync(tmp_fn, filename); fs.renameSync(tmp_fn, filename);
this.dirty = false; this.dirty = false;
}; };
Wallet.prototype.setNetwork = function(netname) { Wallet.prototype.setNetwork = function(netname) {
if (!netname) if (!netname)
netname = this.datastore.network; netname = this.datastore.network;
switch (netname) { switch (netname) {
case "mainnet": case "mainnet":
case "livenet": case "livenet":
this.network = networks.livenet; this.network = networks.livenet;
break; break;
case "testnet": case "testnet":
this.network = networks.testnet; this.network = networks.testnet;
break; break;
default: default:
throw new Error("Unsupported network"); throw new Error("Unsupported network");
} }
// store+canonicalize name // store+canonicalize name
this.datastore['network'] = this.network.name; this.datastore['network'] = this.network.name;
this.dirty = true; this.dirty = true;
}; };
Wallet.prototype.addKey = function(wkey) { Wallet.prototype.addKey = function(wkey) {
this.datastore.keys.push(wkey); this.datastore.keys.push(wkey);
this.dirty = true; this.dirty = true;
}; };
Wallet.prototype.addSIN = function(sinObj) { Wallet.prototype.addSIN = function(sinObj) {
this.datastore.sin[sinObj.sin] = sinObj; this.datastore.sin[sinObj.sin] = sinObj;
this.dirty = true; this.dirty = true;
}; };
Wallet.prototype.findKeyHash = function(pubKeyHash) { Wallet.prototype.findKeyHash = function(pubKeyHash) {
var pkhStr = pubKeyHash.toString(); var pkhStr = pubKeyHash.toString();
for (var i = 0; i < this.datastore.keys.length; i++) { for (var i = 0; i < this.datastore.keys.length; i++) {
var obj = this.datastore.keys[i]; var obj = this.datastore.keys[i];
var addrStr = obj.addr; var addrStr = obj.addr;
var addr = new Address(addrStr); var addr = new Address(addrStr);
if (addr.payload().toString() == pkhStr) if (addr.payload().toString() == pkhStr)
return obj; return obj;
} }
return undefined; return undefined;
}; };
Wallet.prototype.expandKey = function(key) { Wallet.prototype.expandKey = function(key) {
var addr = new Address(key); var addr = new Address(key);
var isAddr = true; var isAddr = true;
try { try {
addr.validate(); addr.validate();
var b = addr.payload(); var b = addr.payload();
var obj = this.findKeyHash(b); var obj = this.findKeyHash(b);
key = obj.pub; key = obj.pub;
} catch(e) { } catch(e) {
// do nothing // do nothing
} }
var re = /^[a-fA-F0-9]+$/; var re = /^[a-fA-F0-9]+$/;
if (!key.match(re)) if (!key.match(re))
throw new Error("Unknown key type"); throw new Error("Unknown key type");
return hex(key); return hex(key);
}; };
Wallet.prototype.expandKeys = function(keys) { Wallet.prototype.expandKeys = function(keys) {
var res = []; var res = [];
var us = this; var us = this;
keys.forEach(function(key) { keys.forEach(function(key) {
var expKey = us.expandKey(key); var expKey = us.expandKey(key);
res.push(expKey); res.push(expKey);
}); });
return res; return res;
}; };
Wallet.prototype.addScript = function(script) { Wallet.prototype.addScript = function(script) {
var buf = script.getBuffer(); var buf = script.getBuffer();
var hash = util.sha256ripe160(buf); var hash = util.sha256ripe160(buf);
var addr = new Address(this.network.addressScript, hash); var addr = new Address(this.network.addressScript, hash);
var addrStr = addr.as('base58'); var addrStr = addr.as('base58');
this.datastore.scripts[addrStr] = buf.toString('hex'); this.datastore.scripts[addrStr] = buf.toString('hex');
this.dirty = true; this.dirty = true;
return addrStr; return addrStr;
}; };
return Wallet; return Wallet;
}; };
module.defineClass(ClassSpec); module.defineClass(ClassSpec);

84
WalletKey.js

@ -1,53 +1,53 @@
require('classtool'); require('classtool');
function ClassSpec(b) { function ClassSpec(b) {
var coinUtil = require('./util/util'); var coinUtil = require('./util/util');
var timeUtil = require('./util/time'); var timeUtil = require('./util/time');
var KeyModule = require('./Key'); var KeyModule = require('./Key');
var PrivateKey = require('./PrivateKey').class(); var PrivateKey = require('./PrivateKey').class();
var Address = require('./Address').class(); var Address = require('./Address').class();
function WalletKey(cfg) { function WalletKey(cfg) {
this.network = cfg.network; // required this.network = cfg.network; // required
this.created = cfg.created; this.created = cfg.created;
this.privKey = cfg.privKey; this.privKey = cfg.privKey;
}; };
WalletKey.prototype.generate = function() { WalletKey.prototype.generate = function() {
this.privKey = KeyModule.Key.generateSync(); this.privKey = KeyModule.Key.generateSync();
this.created = timeUtil.curtime(); this.created = timeUtil.curtime();
}; };
WalletKey.prototype.storeObj = function() { WalletKey.prototype.storeObj = function() {
var pubKey = this.privKey.public.toString('hex'); var pubKey = this.privKey.public.toString('hex');
var pubKeyHash = coinUtil.sha256ripe160(this.privKey.public); var pubKeyHash = coinUtil.sha256ripe160(this.privKey.public);
var addr = new Address(this.network.addressPubkey, pubKeyHash); var addr = new Address(this.network.addressPubkey, pubKeyHash);
var priv = new PrivateKey(this.network.keySecret, this.privKey.private, this.privKey.compressed); var priv = new PrivateKey(this.network.keySecret, this.privKey.private, this.privKey.compressed);
var obj = { var obj = {
created: this.created, created: this.created,
priv: priv.toString(), priv: priv.toString(),
pub: pubKey, pub: pubKey,
addr: addr.toString(), addr: addr.toString(),
}; };
return obj; return obj;
}; };
WalletKey.prototype.fromObj = function(obj) { WalletKey.prototype.fromObj = function(obj) {
this.created = obj.created; this.created = obj.created;
this.privKey = new KeyModule.Key(); this.privKey = new KeyModule.Key();
if (obj.priv.length==64) { if (obj.priv.length==64) {
this.privKey.private = new Buffer(obj.priv,'hex'); this.privKey.private = new Buffer(obj.priv,'hex');
this.privKey.compressed = true; this.privKey.compressed = true;
} }
else { else {
var priv = new PrivateKey(obj.priv); var priv = new PrivateKey(obj.priv);
this.privKey.private = new Buffer(priv.payload()); this.privKey.private = new Buffer(priv.payload());
this.privKey.compressed = priv.compressed(); this.privKey.compressed = priv.compressed();
} }
this.privKey.regenerateSync(); this.privKey.regenerateSync();
}; };
return WalletKey; return WalletKey;
}; };
module.defineClass(ClassSpec); module.defineClass(ClassSpec);

6
const.js

@ -1,8 +1,8 @@
MSG = { MSG = {
TX: 1, TX: 1,
BLOCK: 2, BLOCK: 2,
FILTERED_BLOCK: 3, FILTERED_BLOCK: 3,
}; };
MSG.to_str = function(t) { MSG.to_str = function(t) {

156
test/basic.js

@ -10,104 +10,104 @@ suite('basic');
function test_encode_priv(b58, payload, isTestnet, isCompressed) function test_encode_priv(b58, payload, isTestnet, isCompressed)
{ {
var network = isTestnet ? networks.testnet : networks.livenet; var network = isTestnet ? networks.testnet : networks.livenet;
var version = network.keySecret; var version = network.keySecret;
var buf_pl = new Buffer(payload, 'hex'); var buf_pl = new Buffer(payload, 'hex');
var buf; var buf;
if (isCompressed) { if (isCompressed) {
buf = new Buffer(buf_pl.length + 1); buf = new Buffer(buf_pl.length + 1);
buf_pl.copy(buf); buf_pl.copy(buf);
buf[buf_pl.length] = 1; buf[buf_pl.length] = 1;
} else } else
buf = buf_pl; buf = buf_pl;
var key = new KeyModule.Key(); var key = new KeyModule.Key();
key.private = buf; key.private = buf;
key.compressed = isCompressed; key.compressed = isCompressed;
var privkey = new PrivateKey(version, buf); var privkey = new PrivateKey(version, buf);
assert.equal(privkey.toString(), b58); assert.equal(privkey.toString(), b58);
} }
function test_encode_pub(b58, payload, isTestnet, addrType) function test_encode_pub(b58, payload, isTestnet, addrType)
{ {
var isScript = (addrType == 'script'); var isScript = (addrType == 'script');
var network = isTestnet ? networks.testnet : networks.livenet; var network = isTestnet ? networks.testnet : networks.livenet;
var version = isScript ? network.addressScript : network.addressPubkey; var version = isScript ? network.addressScript : network.addressPubkey;
var buf = new Buffer(payload, 'hex'); var buf = new Buffer(payload, 'hex');
var addr = new Address(version, buf); var addr = new Address(version, buf);
assert.equal(addr.toString(), b58); assert.equal(addr.toString(), b58);
} }
function test_decode_priv(b58, payload, isTestnet, isCompressed) function test_decode_priv(b58, payload, isTestnet, isCompressed)
{ {
var network = isTestnet ? networks.testnet : networks.livenet; var network = isTestnet ? networks.testnet : networks.livenet;
var version = network.keySecret; var version = network.keySecret;
var buf_pl = new Buffer(payload, 'hex'); var buf_pl = new Buffer(payload, 'hex');
var buf; var buf;
if (isCompressed) { if (isCompressed) {
buf = new Buffer(buf_pl.length + 1); buf = new Buffer(buf_pl.length + 1);
buf_pl.copy(buf); buf_pl.copy(buf);
buf[buf_pl.length] = 1; buf[buf_pl.length] = 1;
} else } else
buf = buf_pl; buf = buf_pl;
var privkey = new PrivateKey(b58); var privkey = new PrivateKey(b58);
assert.equal(version, privkey.version()); assert.equal(version, privkey.version());
assert.equal(buf_pl.toString(), privkey.payload().toString()); assert.equal(buf_pl.toString(), privkey.payload().toString());
} }
function test_decode_pub(b58, payload, isTestnet, addrType) function test_decode_pub(b58, payload, isTestnet, addrType)
{ {
var isScript = (addrType == 'script'); var isScript = (addrType == 'script');
var network = isTestnet ? networks.testnet : networks.livenet; var network = isTestnet ? networks.testnet : networks.livenet;
var version = isScript ? network.addressScript : network.addressPubkey; var version = isScript ? network.addressScript : network.addressPubkey;
var buf = new Buffer(payload, 'hex'); var buf = new Buffer(payload, 'hex');
var addr = new Address(b58); var addr = new Address(b58);
assert.equal(version, addr.version()); assert.equal(version, addr.version());
assert.equal(buf.toString(), addr.payload().toString()); assert.equal(buf.toString(), addr.payload().toString());
} }
function is_valid(datum) function is_valid(datum)
{ {
var b58 = datum[0]; var b58 = datum[0];
var payload = datum[1]; var payload = datum[1];
var obj = datum[2]; var obj = datum[2];
var isPrivkey = obj['isPrivkey']; var isPrivkey = obj['isPrivkey'];
var isTestnet = obj['isTestnet']; var isTestnet = obj['isTestnet'];
if (isPrivkey) { if (isPrivkey) {
var isCompressed = obj['isCompressed']; var isCompressed = obj['isCompressed'];
test_encode_priv(b58, payload, isTestnet, isCompressed); test_encode_priv(b58, payload, isTestnet, isCompressed);
test_decode_priv(b58, payload, isTestnet, isCompressed); test_decode_priv(b58, payload, isTestnet, isCompressed);
} else { } else {
var addrType = obj['addrType']; var addrType = obj['addrType'];
test_encode_pub(b58, payload, isTestnet, addrType); test_encode_pub(b58, payload, isTestnet, addrType);
test_decode_pub(b58, payload, isTestnet, addrType); test_decode_pub(b58, payload, isTestnet, addrType);
} }
} }
function is_invalid(datum) function is_invalid(datum)
{ {
if (datum.length < 1) if (datum.length < 1)
throw new Error("Bad test"); throw new Error("Bad test");
// ignore succeeding elements, as comments // ignore succeeding elements, as comments
var b58 = datum[0]; var b58 = datum[0];
var privkey = new PrivateKey(b58); var privkey = new PrivateKey(b58);
var addr = new Address(b58); var addr = new Address(b58);
var valid = true; var valid = true;
try { try {
privkey.validate(); privkey.validate();
addr.validate(); addr.validate();
} catch(e) { } catch(e) {
valid = false; valid = false;
} }
assert.equal(valid, false); assert.equal(valid, false);
} }
var dataValid = JSON.parse(fs.readFileSync('test/base58_keys_valid.json')); var dataValid = JSON.parse(fs.readFileSync('test/base58_keys_valid.json'));

72
util/EncFile.js

@ -4,56 +4,56 @@ var crypto = require('crypto');
exports.readFileSync = function(enc_method, enc_passphrase, filename) exports.readFileSync = function(enc_method, enc_passphrase, filename)
{ {
// read entire file into memory // read entire file into memory
var fileData = fs.readFileSync(filename, 'binary'); var fileData = fs.readFileSync(filename, 'binary');
if (fileData.length < 32) if (fileData.length < 32)
throw new Error("Crypted file " + filename + " truncated"); throw new Error("Crypted file " + filename + " truncated");
// separate into data, hmac parts // separate into data, hmac parts
var fileCrypted = fileData.slice(0, -32); var fileCrypted = fileData.slice(0, -32);
var fileHmac = fileData.slice(-32); var fileHmac = fileData.slice(-32);
// generate and verify HMAC // generate and verify HMAC
var hmac = crypto.createHmac('sha256', enc_passphrase); var hmac = crypto.createHmac('sha256', enc_passphrase);
hmac.update(fileCrypted); hmac.update(fileCrypted);
var digest = hmac.digest('binary'); var digest = hmac.digest('binary');
if (digest.toString() != fileHmac.toString()) if (digest.toString() != fileHmac.toString())
throw new Error("Crypted file " + filename + " failed HMAC checksum verification"); throw new Error("Crypted file " + filename + " failed HMAC checksum verification");
// decrypt to plaintext // decrypt to plaintext
var decipher = crypto.createDecipher(enc_method, enc_passphrase); var decipher = crypto.createDecipher(enc_method, enc_passphrase);
var dec = decipher.update(fileCrypted, 'binary', 'binary'); var dec = decipher.update(fileCrypted, 'binary', 'binary');
dec += decipher.final('binary'); dec += decipher.final('binary');
return dec; return dec;
}; };
exports.readJFileSync = function(enc_method, enc_passphrase, filename) exports.readJFileSync = function(enc_method, enc_passphrase, filename)
{ {
var raw = this.readFileSync(enc_method, enc_passphrase, filename); var raw = this.readFileSync(enc_method, enc_passphrase, filename);
return JSON.parse(raw); return JSON.parse(raw);
}; };
exports.writeFileSync = function(enc_method, enc_passphrase, filename, data) exports.writeFileSync = function(enc_method, enc_passphrase, filename, data)
{ {
// encrypt to ciphertext // encrypt to ciphertext
var cipher = crypto.createCipher(enc_method, enc_passphrase); var cipher = crypto.createCipher(enc_method, enc_passphrase);
var crypted = cipher.update(data, 'binary', 'binary'); var crypted = cipher.update(data, 'binary', 'binary');
crypted += cipher.final('binary'); crypted += cipher.final('binary');
// compute HMAC // compute HMAC
var hmac = crypto.createHmac('sha256', enc_passphrase); var hmac = crypto.createHmac('sha256', enc_passphrase);
hmac.update(crypted); hmac.update(crypted);
var digest = hmac.digest('binary'); var digest = hmac.digest('binary');
fs.writeFileSync(filename, crypted + digest, 'binary'); fs.writeFileSync(filename, crypted + digest, 'binary');
return true; return true;
}; };
exports.writeJFileSync = function(enc_method, enc_passphrase, filename, obj) exports.writeJFileSync = function(enc_method, enc_passphrase, filename, obj)
{ {
var raw = JSON.stringify(obj); var raw = JSON.stringify(obj);
return this.writeFileSync(enc_method, enc_passphrase, filename, raw); return this.writeFileSync(enc_method, enc_passphrase, filename, raw);
}; };

2
util/time.js

@ -2,6 +2,6 @@
// current time, in seconds // current time, in seconds
exports.curtime = function curtime() exports.curtime = function curtime()
{ {
return Math.round(Date.now() / 1000); return Math.round(Date.now() / 1000);
} }

34
util/util.js

@ -113,42 +113,42 @@ var reWholeVal = /^\s*(\d+)/;
function padFrac(frac) function padFrac(frac)
{ {
frac=frac.substr(0,8); //truncate to 8 decimal places frac=frac.substr(0,8); //truncate to 8 decimal places
while (frac.length < 8) while (frac.length < 8)
frac = frac + '0'; frac = frac + '0';
return frac; return frac;
} }
function parseFullValue(res) function parseFullValue(res)
{ {
return bignum(res[1]).mul('100000000').add(padFrac(res[2])); return bignum(res[1]).mul('100000000').add(padFrac(res[2]));
} }
function parseFracValue(res) function parseFracValue(res)
{ {
return bignum(padFrac(res[1])); return bignum(padFrac(res[1]));
} }
function parseWholeValue(res) function parseWholeValue(res)
{ {
return bignum(res[1]).mul('100000000'); return bignum(res[1]).mul('100000000');
} }
exports.parseValue = function parseValue(valueStr) exports.parseValue = function parseValue(valueStr)
{ {
var res = valueStr.match(reFullVal); var res = valueStr.match(reFullVal);
if (res) if (res)
return parseFullValue(res); return parseFullValue(res);
res = valueStr.match(reFracVal); res = valueStr.match(reFracVal);
if (res) if (res)
return parseFracValue(res); return parseFracValue(res);
res = valueStr.match(reWholeVal); res = valueStr.match(reWholeVal);
if (res) if (res)
return parseWholeValue(res); return parseWholeValue(res);
return undefined; return undefined;
}; };
// Utility that synchronizes function calls based on a key // Utility that synchronizes function calls based on a key

Loading…
Cancel
Save