diff --git a/Block.js b/Block.js index 9efa15f..cfe976b 100644 --- a/Block.js +++ b/Block.js @@ -39,12 +39,12 @@ function spec(b) { Block.prototype.getHeader = function getHeader() { var buf = new Buffer(80); var ofs = 0; - buf.writeUInt32LE(this.version, ofs); ofs += 4; - this.prev_hash.copy(buf, ofs); ofs += 32; - this.merkle_root.copy(buf, ofs); ofs += 32; - buf.writeUInt32LE(this.timestamp, ofs); ofs += 4; - buf.writeUInt32LE(this.bits, ofs); ofs += 4; - buf.writeUInt32LE(this.nonce, ofs); ofs += 4; + buf.writeUInt32LE(this.version, ofs); ofs += 4; + this.prev_hash.copy(buf, ofs); ofs += 32; + this.merkle_root.copy(buf, ofs); ofs += 32; + buf.writeUInt32LE(this.timestamp, ofs); ofs += 4; + buf.writeUInt32LE(this.bits, ofs); ofs += 4; + buf.writeUInt32LE(this.nonce, ofs); ofs += 4; return buf; }; diff --git a/Bloom.js b/Bloom.js index 8cf1ee8..d514053 100644 --- a/Bloom.js +++ b/Bloom.js @@ -1,116 +1,116 @@ require('classtool'); function ClassSpec(b) { - var MAX_BLOOM_FILTER_SIZE = 36000; // bytes - var MAX_HASH_FUNCS = 50; - var LN2SQUARED = 0.4804530139182014246671025263266649717305529515945455; - var LN2 = 0.6931471805599453094172321214581765680755001343602552; - var bit_mask = [0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80]; - - function Bloom() { - this.data = ''; - this.hashFuncs = 0; - }; - - function ROTL32(x, r) { - return (x << r) | (x >> (32 - r)); - }; - - function getBlockU32(blockIdx, data) { - var idx = blockIdx * 4; - var v = (data[idx + 0] << (0 * 8)) | - (data[idx + 1] << (1 * 8)) | - (data[idx + 2] << (2 * 8)) | - (data[idx + 3] << (3 * 8)); - return v; - }; - - Bloom.prototype.hash = function(hashNum, data) { - var h1 = hashNum * (0xffffffff / (this.hashFuncs - 1)); - var c1 = 0xcc9e2d51; - var c2 = 0x1b873593; - var nBlocks = data.length / 4; - - // data body - for (var i = -nBlocks; i; i++) { - var k1 = getBlockU32(i); - - k1 *= c1; - k1 = ROTLF32(k1, 15); - k1 *= c2; - - h1 ^= k1; - h1 = ROTFL(h1, 13); - h1 = h1 * 5 + 0xe6546b64; - } - - // tail (trailing 1-3 bytes) - var tail = data.slice(nBlocks * 4); - - var k1 = 0; - - switch (data.length & 3) { - case 3: k1 ^= tail[2] << 16; - case 2: k1 ^= tail[1] << 8; - case 1: k1 ^= tail[0]; - k1 *= c1; - k1 = ROTL32(k1, 15); - k1 *= c2; - h1 ^= k1; - } - - // finalize - h1 ^= data.length; - h1 ^= h1 >> 16; - h1 *= 0x85ebca6b; - h1 ^= h1 >> 13; - h1 *= 0xc2b2ae35; - h1 ^= h1 >> 16; - - return h1 % (this.data.length * 8); - }; - - Bloom.prototype.insert = function(data) { - for (var i = 0; i < this.hashFuncs; i++) { - var index = this.hash(i, data); - this.data[index >> 3] |= bit_mask[7 & index]; - } - }; - - Bloom.prototype.contains = function(data) { - for (var i = 0; i < this.hashFuncs; i++) { - var index = this.hash(i, data); - if (!(this.data[index >> 3] & bit_mask[7 & index])) - return false; - } - - return true; - }; - - Bloom.prototype.sizeOk = function() { - return this.data.length <= MAX_BLOOM_FILTER_SIZE && - this.hashFuncs <= MAX_HASH_FUNCS; - }; - - function toInt(v) { - return ~~v; - } - - function min(a, b) { - if (a < b) - return a; - return b; - } - - Bloom.prototype.init = function(elements, FPRate) { - var filterSize = min(toInt(-1.0 / LN2SQUARED * elements * Math.log(FPRate)), - MAX_BLOOM_FILTER_SIZE * 8) / 8; - this.data[filterSize] = 0; - this.hashFuncs = min(toInt(this.data.length * 8 / elements * LN2), - MAX_HASH_FUNCS); - }; - - return Bloom; + var MAX_BLOOM_FILTER_SIZE = 36000; // bytes + var MAX_HASH_FUNCS = 50; + var LN2SQUARED = 0.4804530139182014246671025263266649717305529515945455; + var LN2 = 0.6931471805599453094172321214581765680755001343602552; + var bit_mask = [0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80]; + + function Bloom() { + this.data = ''; + this.hashFuncs = 0; + }; + + function ROTL32(x, r) { + return (x << r) | (x >> (32 - r)); + }; + + function getBlockU32(blockIdx, data) { + var idx = blockIdx * 4; + var v = (data[idx + 0] << (0 * 8)) | + (data[idx + 1] << (1 * 8)) | + (data[idx + 2] << (2 * 8)) | + (data[idx + 3] << (3 * 8)); + return v; + }; + + Bloom.prototype.hash = function(hashNum, data) { + var h1 = hashNum * (0xffffffff / (this.hashFuncs - 1)); + var c1 = 0xcc9e2d51; + var c2 = 0x1b873593; + var nBlocks = data.length / 4; + + // data body + for (var i = -nBlocks; i; i++) { + var k1 = getBlockU32(i); + + k1 *= c1; + k1 = ROTLF32(k1, 15); + k1 *= c2; + + h1 ^= k1; + h1 = ROTFL(h1, 13); + h1 = h1 * 5 + 0xe6546b64; + } + + // tail (trailing 1-3 bytes) + var tail = data.slice(nBlocks * 4); + + var k1 = 0; + + switch (data.length & 3) { + case 3: k1 ^= tail[2] << 16; + case 2: k1 ^= tail[1] << 8; + case 1: k1 ^= tail[0]; + k1 *= c1; + k1 = ROTL32(k1, 15); + k1 *= c2; + h1 ^= k1; + } + + // finalize + h1 ^= data.length; + h1 ^= h1 >> 16; + h1 *= 0x85ebca6b; + h1 ^= h1 >> 13; + h1 *= 0xc2b2ae35; + h1 ^= h1 >> 16; + + return h1 % (this.data.length * 8); + }; + + Bloom.prototype.insert = function(data) { + for (var i = 0; i < this.hashFuncs; i++) { + var index = this.hash(i, data); + this.data[index >> 3] |= bit_mask[7 & index]; + } + }; + + Bloom.prototype.contains = function(data) { + for (var i = 0; i < this.hashFuncs; i++) { + var index = this.hash(i, data); + if (!(this.data[index >> 3] & bit_mask[7 & index])) + return false; + } + + return true; + }; + + Bloom.prototype.sizeOk = function() { + return this.data.length <= MAX_BLOOM_FILTER_SIZE && + this.hashFuncs <= MAX_HASH_FUNCS; + }; + + function toInt(v) { + return ~~v; + } + + function min(a, b) { + if (a < b) + return a; + return b; + } + + Bloom.prototype.init = function(elements, FPRate) { + var filterSize = min(toInt(-1.0 / LN2SQUARED * elements * Math.log(FPRate)), + MAX_BLOOM_FILTER_SIZE * 8) / 8; + this.data[filterSize] = 0; + this.hashFuncs = min(toInt(this.data.length * 8 / elements * LN2), + MAX_HASH_FUNCS); + }; + + return Bloom; }; module.defineClass(ClassSpec); diff --git a/Connection.js b/Connection.js index 21a4670..6e9e365 100644 --- a/Connection.js +++ b/Connection.js @@ -443,8 +443,8 @@ function spec(b) { data.headers = []; for (i = 0; i < data.count; i++) { var header = new Block(); - header.parse(parser); - data.headers.push(header); + header.parse(parser); + data.headers.push(header); } break; @@ -474,7 +474,7 @@ function spec(b) { lock_time: tx.lock_time, ins: tx.ins, outs: tx.outs, - tx: tx, + tx: tx, }; case 'getblocks': diff --git a/Deserialize.js b/Deserialize.js index dde4dd9..48befd6 100644 --- a/Deserialize.js +++ b/Deserialize.js @@ -1,8 +1,8 @@ exports.intFromCompact = function(c) { - var bytes = ((c >>> 24) & 0xff) >>> 0; - var v = ((c & 0xffffff) << (8 * (bytes - 3))) >>> 0; - return v; + var bytes = ((c >>> 24) & 0xff) >>> 0; + var v = ((c & 0xffffff) << (8 * (bytes - 3))) >>> 0; + return v; } diff --git a/SIN.js b/SIN.js index dba5cc3..95f7a88 100644 --- a/SIN.js +++ b/SIN.js @@ -11,16 +11,16 @@ function ClassSpec(b) { }; this.data = new Buffer(1 + 1 + payload.length); 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.payload(payload); }; SIN.superclass = superclass; superclass.applyEncodingsTo(SIN); - SIN.SIN_PERSIST_MAINNET = 0x01; // 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_PERSIST_MAINNET = 0x01; // associated with sacrifice TX + SIN.SIN_PERSIST_TESTNET = 0x11; // associated with sacrifice TX + SIN.SIN_EPHEM = 0x02; // generate off-net at any time // get or set the prefix data (the first byte of the address) SIN.prototype.prefix = function(num) { diff --git a/SINKey.js b/SINKey.js index b80a8a1..5df712a 100644 --- a/SINKey.js +++ b/SINKey.js @@ -1,43 +1,43 @@ require('classtool'); function ClassSpec(b) { - var coinUtil = require('./util/util'); - var timeUtil = require('./util/time'); - var KeyModule = require('./Key'); - var SIN = require('./SIN').class(); - - function SINKey(cfg) { - if (typeof cfg != 'object') - cfg = {}; - - this.created = cfg.created; - this.privKey = cfg.privKey; - }; - - SINKey.prototype.generate = function() { - this.privKey = KeyModule.Key.generateSync(); - this.created = timeUtil.curtime(); - }; - - SINKey.prototype.pubkeyHash = function() { - return coinUtil.sha256ripe160(this.privKey.public); - }; - - SINKey.prototype.storeObj = function() { - var pubKey = this.privKey.public.toString('hex'); - var pubKeyHash = this.pubkeyHash(); - var sin = new SIN(SIN.SIN_EPHEM, pubKeyHash); - var obj = { - created: this.created, - priv: this.privKey.private.toString('hex'), - pub: pubKey, - sin: sin.toString(), - }; - - return obj; - }; - - return SINKey; + var coinUtil = require('./util/util'); + var timeUtil = require('./util/time'); + var KeyModule = require('./Key'); + var SIN = require('./SIN').class(); + + function SINKey(cfg) { + if (typeof cfg != 'object') + cfg = {}; + + this.created = cfg.created; + this.privKey = cfg.privKey; + }; + + SINKey.prototype.generate = function() { + this.privKey = KeyModule.Key.generateSync(); + this.created = timeUtil.curtime(); + }; + + SINKey.prototype.pubkeyHash = function() { + return coinUtil.sha256ripe160(this.privKey.public); + }; + + SINKey.prototype.storeObj = function() { + var pubKey = this.privKey.public.toString('hex'); + var pubKeyHash = this.pubkeyHash(); + var sin = new SIN(SIN.SIN_EPHEM, pubKeyHash); + var obj = { + created: this.created, + priv: this.privKey.private.toString('hex'), + pub: pubKey, + sin: sin.toString(), + }; + + return obj; + }; + + return SINKey; }; module.defineClass(ClassSpec); diff --git a/Script.js b/Script.js index 9df0d6f..33c4dee 100644 --- a/Script.js +++ b/Script.js @@ -84,42 +84,42 @@ function spec(b) { Script.prototype.isP2SH = function () { return (this.chunks.length == 3 && - this.chunks[0] == OP_HASH160 && - Buffer.isBuffer(this.chunks[1]) && - this.chunks[1].length == 20 && - this.chunks[2] == OP_EQUAL); + this.chunks[0] == OP_HASH160 && + Buffer.isBuffer(this.chunks[1]) && + this.chunks[1].length == 20 && + this.chunks[2] == OP_EQUAL); }; Script.prototype.isPubkey = function () { return (this.chunks.length == 2 && - Buffer.isBuffer(this.chunks[0]) && - this.chunks[1] == OP_CHECKSIG); + Buffer.isBuffer(this.chunks[0]) && + this.chunks[1] == OP_CHECKSIG); }; Script.prototype.isPubkeyHash = function () { return (this.chunks.length == 5 && - this.chunks[0] == OP_DUP && - this.chunks[1] == OP_HASH160 && - Buffer.isBuffer(this.chunks[2]) && - this.chunks[2].length == 20 && - this.chunks[3] == OP_EQUALVERIFY && - this.chunks[4] == OP_CHECKSIG); + this.chunks[0] == OP_DUP && + this.chunks[1] == OP_HASH160 && + Buffer.isBuffer(this.chunks[2]) && + this.chunks[2].length == 20 && + this.chunks[3] == OP_EQUALVERIFY && + this.chunks[4] == OP_CHECKSIG); }; function isSmallIntOp(opcode) { return ((opcode == OP_0) || - ((opcode >= OP_1) && (opcode <= OP_16))); + ((opcode >= OP_1) && (opcode <= OP_16))); }; Script.prototype.isMultiSig = function () { return (this.chunks.length > 3 && - isSmallIntOp(this.chunks[0]) && - isSmallIntOp(this.chunks[this.chunks.length-2]) && - this.chunks[this.chunks.length-1] == OP_CHECKMULTISIG); + isSmallIntOp(this.chunks[0]) && + isSmallIntOp(this.chunks[this.chunks.length-2]) && + this.chunks[this.chunks.length-1] == OP_CHECKMULTISIG); }; Script.prototype.finishedMultiSig = function() @@ -166,60 +166,60 @@ function spec(b) { // is this a script form we know? Script.prototype.classify = function () { - if (this.isPubkeyHash()) - return TX_PUBKEYHASH; - if (this.isP2SH()) - return TX_SCRIPTHASH; - if (this.isMultiSig()) - return TX_MULTISIG; - if (this.isPubkey()) - return TX_PUBKEY; - return TX_UNKNOWN; + if (this.isPubkeyHash()) + return TX_PUBKEYHASH; + if (this.isP2SH()) + return TX_SCRIPTHASH; + if (this.isMultiSig()) + return TX_MULTISIG; + if (this.isPubkey()) + return TX_PUBKEY; + return TX_UNKNOWN; }; // extract useful data items from known scripts Script.prototype.capture = function () { - var txType = this.classify(); - var res = []; - switch (txType) { - case TX_PUBKEY: - res.push(this.chunks[0]); - break; - case TX_PUBKEYHASH: - res.push(this.chunks[2]); - break; - case TX_MULTISIG: - for (var i = 1; i < (this.chunks.length - 2); i++) - res.push(this.chunks[i]); - break; - case TX_SCRIPTHASH: - res.push(this.chunks[1]); - break; - - case TX_UNKNOWN: - default: - // do nothing - break; - } - - return res; + var txType = this.classify(); + var res = []; + switch (txType) { + case TX_PUBKEY: + res.push(this.chunks[0]); + break; + case TX_PUBKEYHASH: + res.push(this.chunks[2]); + break; + case TX_MULTISIG: + for (var i = 1; i < (this.chunks.length - 2); i++) + res.push(this.chunks[i]); + break; + case TX_SCRIPTHASH: + res.push(this.chunks[1]); + break; + + case TX_UNKNOWN: + default: + // do nothing + break; + } + + return res; }; // return first extracted data item from script Script.prototype.captureOne = function () { - var arr = this.capture(); - return arr[0]; + var arr = this.capture(); + return arr[0]; }; Script.prototype.getOutType = function () { var txType = this.classify(); switch (txType) { - case TX_PUBKEY: return 'Pubkey'; - case TX_PUBKEYHASH: return 'Address'; - default: return 'Strange'; + case TX_PUBKEY: return 'Pubkey'; + case TX_PUBKEYHASH: return 'Address'; + default: return 'Strange'; } }; @@ -446,7 +446,7 @@ function spec(b) { var script = new Script(); script.writeN(n_required); keys.forEach(function(key) { - script.writeBytes(key); + script.writeBytes(key); }); script.writeN(keys.length); script.writeOp(OP_CHECKMULTISIG); diff --git a/ScriptInterpreter.js b/ScriptInterpreter.js index 5eeea94..c83d18d 100644 --- a/ScriptInterpreter.js +++ b/ScriptInterpreter.js @@ -918,85 +918,85 @@ function spec(b) { }; function verifyStep4(scriptSig, scriptPubKey, txTo, nIn, - hashType, opts, callback, si, siCopy) + hashType, opts, callback, si, siCopy) { - if (siCopy.stack.length == 0) { - callback(null, false); - return; - } - - callback(null, castBool(siCopy.stackBack())); + if (siCopy.stack.length == 0) { + callback(null, false); + return; + } + + callback(null, castBool(siCopy.stackBack())); } function verifyStep3(scriptSig, scriptPubKey, txTo, nIn, - hashType, opts, callback, si, siCopy) + hashType, opts, callback, si, siCopy) { - if (si.stack.length == 0) { - callback(null, false); - return; - } - if (castBool(si.stackBack()) == false) { - callback(null, false); - return; - } - - // if not P2SH, we're done - if (!opts.verifyP2SH || !scriptPubKey.isP2SH()) { - callback(null, true); - return; - } - - if (!scriptSig.isPushOnly()) { - callback(null, false); - return; - } - - assert.notEqual(siCopy.length, 0); - - var subscript = new Script(siCopy.stackPop()); - - ok = true; - siCopy.eval(subscript, txTo, nIn, hashType, function (err) { - if (err) - callback(err); - else - verifyStep4(scriptSig, scriptPubKey, txTo, nIn, - hashType, opts, callback, si, siCopy); - }); + if (si.stack.length == 0) { + callback(null, false); + return; + } + if (castBool(si.stackBack()) == false) { + callback(null, false); + return; + } + + // if not P2SH, we're done + if (!opts.verifyP2SH || !scriptPubKey.isP2SH()) { + callback(null, true); + return; + } + + if (!scriptSig.isPushOnly()) { + callback(null, false); + return; + } + + assert.notEqual(siCopy.length, 0); + + var subscript = new Script(siCopy.stackPop()); + + ok = true; + siCopy.eval(subscript, txTo, nIn, hashType, function (err) { + if (err) + callback(err); + else + verifyStep4(scriptSig, scriptPubKey, txTo, nIn, + hashType, opts, callback, si, siCopy); + }); } function verifyStep2(scriptSig, scriptPubKey, txTo, nIn, - hashType, opts, callback, si, siCopy) + hashType, opts, callback, si, siCopy) { - if (opts.verifyP2SH) { - si.stack.forEach(function(item) { - siCopy.stack.push(item); - }); - } - - si.eval(scriptPubKey, txTo, nIn, hashType, function (err) { - if (err) - callback(err); - else - verifyStep3(scriptSig, scriptPubKey, txTo, nIn, - hashType, opts, callback, si, siCopy); - }); + if (opts.verifyP2SH) { + si.stack.forEach(function(item) { + siCopy.stack.push(item); + }); + } + + si.eval(scriptPubKey, txTo, nIn, hashType, function (err) { + if (err) + callback(err); + else + verifyStep3(scriptSig, scriptPubKey, txTo, nIn, + hashType, opts, callback, si, siCopy); + }); } ScriptInterpreter.verifyFull = function verifyFull(scriptSig, scriptPubKey, txTo, nIn, hashType, - opts, callback) + opts, callback) { - var si = new ScriptInterpreter(); - var siCopy = new ScriptInterpreter(); - - si.eval(scriptSig, txTo, nIn, hashType, function (err) { - if (err) - callback(err); - else - verifyStep2(scriptSig, scriptPubKey, txTo, nIn, - hashType, opts, callback, si, siCopy); - }); + var si = new ScriptInterpreter(); + var siCopy = new ScriptInterpreter(); + + si.eval(scriptSig, txTo, nIn, hashType, function (err) { + if (err) + callback(err); + else + verifyStep2(scriptSig, scriptPubKey, txTo, nIn, + hashType, opts, callback, si, siCopy); + }); }; var checkSig = ScriptInterpreter.checkSig = diff --git a/Sign.js b/Sign.js index 24889dc..6ceb7de 100644 --- a/Sign.js +++ b/Sign.js @@ -1,133 +1,133 @@ function signOne(hash, addrStr, keys) { - var keyObj = keys[addrStr]; - var rawPrivKey = new Buffer(keyObj.priv, 'hex'); - var key = new KeyModule.Key(); - key.private = rawPrivKey; - var signature = key.signSync(hash); + var keyObj = keys[addrStr]; + var rawPrivKey = new Buffer(keyObj.priv, 'hex'); + var key = new KeyModule.Key(); + key.private = rawPrivKey; + var signature = key.signSync(hash); - return signature; + return signature; } function signTxIn(nIn, tx, txInputs, network, keys, scripts) { - // locate TX input needing a signature - var txin = tx.ins[nIn]; - var scriptSig = txin.getScript(); - - // locate TX output, within txInputs - var txoutHash = txin.getOutpointHash(); - if (!(txoutHash in txInputs)) - throw new Error("signTxIn missing input hash"); - var txFrom = txInputs[txoutHash]; - var txoutIndex = txin.getOutpointIndex(); - if (txFrom.outs.length >= txoutIndex) - throw new Error("signTxIn missing input index"); - var txout = txFrom.outs[txoutIndex]; - var scriptPubKey = txout.getScript(); - - // detect type of transaction, and extract useful elements - var txType = scriptPubKey.classify(); - if (txType == TX_UNKNOWN) - throw new Error("unknown TX type"); - var scriptData = scriptPubKey.capture(); - - // if P2SH, lookup the script - var subscriptRaw = undefined; - var subscript = undefined; - var subType = undefined; - var subData = undefined; - if (txType == TX_SCRIPTHASH) { - var addr = new Address(network.addressScript, scriptData[0]); - var addrStr = addr.toString(); - if (!(addrStr in scripts)) - throw new Error("unknown script hash address"); - - subscriptRaw = new Buffer(scripts[addrStr], 'hex'); - subscript = new Script(subscriptRaw); - subType = subscript.classify(); - if (subType == TX_UNKNOWN) - throw new Error("unknown subscript TX type"); - subData = subscript.capture(); - } - - var hash = tx.hashForSignature(scriptPubKey, i, 0); - - switch (txType) { - case TX_PUBKEY: - // already signed - if (scriptSig.chunks.length > 0) - return; - - var pubkeyhash = util.sha256ripe160(scriptData[0]); - var addr = new Address(network.addressPubkey, pubkeyhash); - var addrStr = addr.toString(); - if (!(addrStr in keys)) - throw new Error("unknown pubkey"); - - var signature = signOne(hash, addrStr, keys); - scriptSig.writeBytes(signature); - break; - - case TX_PUBKEYHASH: - // already signed - if (scriptSig.chunks.length > 0) - return; - - var addr = new Address(network.addressPubkey, scriptData[0]); - var addrStr = addr.toString(); - if (!(addrStr in keys)) - throw new Error("unknown pubkey hash address"); - - var signature = signOne(hash, addrStr, keys); - scriptSig.writeBytes(signature); - scriptSig.writeBytes(key.public); - break; - - case TX_SCRIPTHASH: - // already signed - if (scriptSig.chunks.length > 0) - return; - - var addr = new Address(network.addressPubkey, subData[0]); - var addrStr = addr.toString(); - if (!(addrStr in keys)) - throw new Error("unknown script(pubkey hash) address"); - - var signature = signOne(hash, addrStr, keys); - scriptSig.writeBytes(signature); - scriptSig.writeBytes(key.public); - break; - - case TX_MULTISIG: - while (scriptSig.chunks.length < scriptData.length) { - scriptSig.writeBytes(util.EMPTY_BUFFER); - } - for (var i = 0; i < scriptData.length; i++) { - // skip already signed - if (scriptSig.chunks[i].length > 0) - continue; - - var pubkeyhash = util.sha256ripe160(scriptSig.chunks[i]); - var addr = new Address(network.addressPubkey, pubkeyhash); - var addrStr = addr.toString(); - if (!(addrStr in keys)) - continue; - - var signature = signOne(hash, addrStr, keys); - scriptSig.chunks[i] = signature; - } - break; - } - - if (txtype == TX_SCRIPTHASH) - scriptSig.writeBytes(subscriptRaw); + // locate TX input needing a signature + var txin = tx.ins[nIn]; + var scriptSig = txin.getScript(); + + // locate TX output, within txInputs + var txoutHash = txin.getOutpointHash(); + if (!(txoutHash in txInputs)) + throw new Error("signTxIn missing input hash"); + var txFrom = txInputs[txoutHash]; + var txoutIndex = txin.getOutpointIndex(); + if (txFrom.outs.length >= txoutIndex) + throw new Error("signTxIn missing input index"); + var txout = txFrom.outs[txoutIndex]; + var scriptPubKey = txout.getScript(); + + // detect type of transaction, and extract useful elements + var txType = scriptPubKey.classify(); + if (txType == TX_UNKNOWN) + throw new Error("unknown TX type"); + var scriptData = scriptPubKey.capture(); + + // if P2SH, lookup the script + var subscriptRaw = undefined; + var subscript = undefined; + var subType = undefined; + var subData = undefined; + if (txType == TX_SCRIPTHASH) { + var addr = new Address(network.addressScript, scriptData[0]); + var addrStr = addr.toString(); + if (!(addrStr in scripts)) + throw new Error("unknown script hash address"); + + subscriptRaw = new Buffer(scripts[addrStr], 'hex'); + subscript = new Script(subscriptRaw); + subType = subscript.classify(); + if (subType == TX_UNKNOWN) + throw new Error("unknown subscript TX type"); + subData = subscript.capture(); + } + + var hash = tx.hashForSignature(scriptPubKey, i, 0); + + switch (txType) { + case TX_PUBKEY: + // already signed + if (scriptSig.chunks.length > 0) + return; + + var pubkeyhash = util.sha256ripe160(scriptData[0]); + var addr = new Address(network.addressPubkey, pubkeyhash); + var addrStr = addr.toString(); + if (!(addrStr in keys)) + throw new Error("unknown pubkey"); + + var signature = signOne(hash, addrStr, keys); + scriptSig.writeBytes(signature); + break; + + case TX_PUBKEYHASH: + // already signed + if (scriptSig.chunks.length > 0) + return; + + var addr = new Address(network.addressPubkey, scriptData[0]); + var addrStr = addr.toString(); + if (!(addrStr in keys)) + throw new Error("unknown pubkey hash address"); + + var signature = signOne(hash, addrStr, keys); + scriptSig.writeBytes(signature); + scriptSig.writeBytes(key.public); + break; + + case TX_SCRIPTHASH: + // already signed + if (scriptSig.chunks.length > 0) + return; + + var addr = new Address(network.addressPubkey, subData[0]); + var addrStr = addr.toString(); + if (!(addrStr in keys)) + throw new Error("unknown script(pubkey hash) address"); + + var signature = signOne(hash, addrStr, keys); + scriptSig.writeBytes(signature); + scriptSig.writeBytes(key.public); + break; + + case TX_MULTISIG: + while (scriptSig.chunks.length < scriptData.length) { + scriptSig.writeBytes(util.EMPTY_BUFFER); + } + for (var i = 0; i < scriptData.length; i++) { + // skip already signed + if (scriptSig.chunks[i].length > 0) + continue; + + var pubkeyhash = util.sha256ripe160(scriptSig.chunks[i]); + var addr = new Address(network.addressPubkey, pubkeyhash); + var addrStr = addr.toString(); + if (!(addrStr in keys)) + continue; + + var signature = signOne(hash, addrStr, keys); + scriptSig.chunks[i] = signature; + } + break; + } + + if (txtype == TX_SCRIPTHASH) + scriptSig.writeBytes(subscriptRaw); } exports.Transaction = function Transaction(tx, txInputs, network, keys, scripts) { - for (var i = 0; i < tx.ins.length; i++) - signTxIn(i, tx, txInputs, network, keys, scripts); + for (var i = 0; i < tx.ins.length; i++) + signTxIn(i, tx, txInputs, network, keys, scripts); }; diff --git a/Wallet.js b/Wallet.js index 9ca8b79..3f1f63f 100644 --- a/Wallet.js +++ b/Wallet.js @@ -2,141 +2,141 @@ require('classtool'); var hex = function(hex) {return new Buffer(hex, 'hex');}; function ClassSpec(b) { - var fs = require('fs'); - var EncFile = require('./util/EncFile'); - var Address = require('./Address').class(); - var networks = require('./networks'); - var util = b.util || require('./util/util'); - var ENC_METHOD = 'aes-256-cbc'; - - var skeleton = { - client: 'libcoin', - client_version: '0.0.1', - network: 'testnet', - version: 1, - best_hash: null, - best_height: -1, - keys: [], - sin: {}, - scripts: {}, - }; - - function Wallet(cfg) { - if (typeof cfg !== 'object') - cfg = {}; - - // deep copy (no references) - if (cfg.datastore) - this.datastore = JSON.parse(JSON.stringify(cfg.datastore)); - else - this.datastore = JSON.parse(JSON.stringify(skeleton)); - - this.network = undefined; - this.dirty = cfg.dirty || true; - }; - - Wallet.prototype.readSync = function(filename, passphrase) { - this.datastore = EncFile.readJFileSync(ENC_METHOD, - passphrase, filename); - this.dirty = false; - }; - - Wallet.prototype.writeSync = function(filename, passphrase) { - var tmp_fn = filename + ".tmp"; - - EncFile.writeJFileSync(ENC_METHOD, passphrase, tmp_fn, - this.datastore); - fs.renameSync(tmp_fn, filename); - - this.dirty = false; - }; - - Wallet.prototype.setNetwork = function(netname) { - if (!netname) - netname = this.datastore.network; - - switch (netname) { - case "mainnet": - case "livenet": - this.network = networks.livenet; - break; - case "testnet": - this.network = networks.testnet; - break; - default: - throw new Error("Unsupported network"); - } - - // store+canonicalize name - this.datastore['network'] = this.network.name; - this.dirty = true; - }; - - Wallet.prototype.addKey = function(wkey) { - this.datastore.keys.push(wkey); - this.dirty = true; - }; - - Wallet.prototype.addSIN = function(sinObj) { - this.datastore.sin[sinObj.sin] = sinObj; - this.dirty = true; - }; - - Wallet.prototype.findKeyHash = function(pubKeyHash) { - var pkhStr = pubKeyHash.toString(); - - for (var i = 0; i < this.datastore.keys.length; i++) { - var obj = this.datastore.keys[i]; - var addrStr = obj.addr; - var addr = new Address(addrStr); - if (addr.payload().toString() == pkhStr) - return obj; - } - - return undefined; - }; - - Wallet.prototype.expandKey = function(key) { - var addr = new Address(key); - var isAddr = true; - - try { - addr.validate(); - var b = addr.payload(); - var obj = this.findKeyHash(b); - key = obj.pub; - } catch(e) { - // do nothing - } - - var re = /^[a-fA-F0-9]+$/; - if (!key.match(re)) - throw new Error("Unknown key type"); - return hex(key); - }; - - Wallet.prototype.expandKeys = function(keys) { - var res = []; - var us = this; - keys.forEach(function(key) { - var expKey = us.expandKey(key); - res.push(expKey); - }); - return res; - }; - - Wallet.prototype.addScript = function(script) { - var buf = script.getBuffer(); - var hash = util.sha256ripe160(buf); - var addr = new Address(this.network.addressScript, hash); - var addrStr = addr.as('base58'); - this.datastore.scripts[addrStr] = buf.toString('hex'); - this.dirty = true; - - return addrStr; - }; - - return Wallet; + var fs = require('fs'); + var EncFile = require('./util/EncFile'); + var Address = require('./Address').class(); + var networks = require('./networks'); + var util = b.util || require('./util/util'); + var ENC_METHOD = 'aes-256-cbc'; + + var skeleton = { + client: 'libcoin', + client_version: '0.0.1', + network: 'testnet', + version: 1, + best_hash: null, + best_height: -1, + keys: [], + sin: {}, + scripts: {}, + }; + + function Wallet(cfg) { + if (typeof cfg !== 'object') + cfg = {}; + + // deep copy (no references) + if (cfg.datastore) + this.datastore = JSON.parse(JSON.stringify(cfg.datastore)); + else + this.datastore = JSON.parse(JSON.stringify(skeleton)); + + this.network = undefined; + this.dirty = cfg.dirty || true; + }; + + Wallet.prototype.readSync = function(filename, passphrase) { + this.datastore = EncFile.readJFileSync(ENC_METHOD, + passphrase, filename); + this.dirty = false; + }; + + Wallet.prototype.writeSync = function(filename, passphrase) { + var tmp_fn = filename + ".tmp"; + + EncFile.writeJFileSync(ENC_METHOD, passphrase, tmp_fn, + this.datastore); + fs.renameSync(tmp_fn, filename); + + this.dirty = false; + }; + + Wallet.prototype.setNetwork = function(netname) { + if (!netname) + netname = this.datastore.network; + + switch (netname) { + case "mainnet": + case "livenet": + this.network = networks.livenet; + break; + case "testnet": + this.network = networks.testnet; + break; + default: + throw new Error("Unsupported network"); + } + + // store+canonicalize name + this.datastore['network'] = this.network.name; + this.dirty = true; + }; + + Wallet.prototype.addKey = function(wkey) { + this.datastore.keys.push(wkey); + this.dirty = true; + }; + + Wallet.prototype.addSIN = function(sinObj) { + this.datastore.sin[sinObj.sin] = sinObj; + this.dirty = true; + }; + + Wallet.prototype.findKeyHash = function(pubKeyHash) { + var pkhStr = pubKeyHash.toString(); + + for (var i = 0; i < this.datastore.keys.length; i++) { + var obj = this.datastore.keys[i]; + var addrStr = obj.addr; + var addr = new Address(addrStr); + if (addr.payload().toString() == pkhStr) + return obj; + } + + return undefined; + }; + + Wallet.prototype.expandKey = function(key) { + var addr = new Address(key); + var isAddr = true; + + try { + addr.validate(); + var b = addr.payload(); + var obj = this.findKeyHash(b); + key = obj.pub; + } catch(e) { + // do nothing + } + + var re = /^[a-fA-F0-9]+$/; + if (!key.match(re)) + throw new Error("Unknown key type"); + return hex(key); + }; + + Wallet.prototype.expandKeys = function(keys) { + var res = []; + var us = this; + keys.forEach(function(key) { + var expKey = us.expandKey(key); + res.push(expKey); + }); + return res; + }; + + Wallet.prototype.addScript = function(script) { + var buf = script.getBuffer(); + var hash = util.sha256ripe160(buf); + var addr = new Address(this.network.addressScript, hash); + var addrStr = addr.as('base58'); + this.datastore.scripts[addrStr] = buf.toString('hex'); + this.dirty = true; + + return addrStr; + }; + + return Wallet; }; module.defineClass(ClassSpec); diff --git a/WalletKey.js b/WalletKey.js index 0958509..3f6c92e 100644 --- a/WalletKey.js +++ b/WalletKey.js @@ -1,53 +1,53 @@ require('classtool'); function ClassSpec(b) { - var coinUtil = require('./util/util'); - var timeUtil = require('./util/time'); - var KeyModule = require('./Key'); - var PrivateKey = require('./PrivateKey').class(); - var Address = require('./Address').class(); + var coinUtil = require('./util/util'); + var timeUtil = require('./util/time'); + var KeyModule = require('./Key'); + var PrivateKey = require('./PrivateKey').class(); + var Address = require('./Address').class(); - function WalletKey(cfg) { - this.network = cfg.network; // required - this.created = cfg.created; - this.privKey = cfg.privKey; - }; + function WalletKey(cfg) { + this.network = cfg.network; // required + this.created = cfg.created; + this.privKey = cfg.privKey; + }; - WalletKey.prototype.generate = function() { - this.privKey = KeyModule.Key.generateSync(); - this.created = timeUtil.curtime(); - }; + WalletKey.prototype.generate = function() { + this.privKey = KeyModule.Key.generateSync(); + this.created = timeUtil.curtime(); + }; - WalletKey.prototype.storeObj = function() { - var pubKey = this.privKey.public.toString('hex'); - var pubKeyHash = coinUtil.sha256ripe160(this.privKey.public); - var addr = new Address(this.network.addressPubkey, pubKeyHash); - var priv = new PrivateKey(this.network.keySecret, this.privKey.private, this.privKey.compressed); - var obj = { - created: this.created, - priv: priv.toString(), - pub: pubKey, - addr: addr.toString(), - }; + WalletKey.prototype.storeObj = function() { + var pubKey = this.privKey.public.toString('hex'); + var pubKeyHash = coinUtil.sha256ripe160(this.privKey.public); + var addr = new Address(this.network.addressPubkey, pubKeyHash); + var priv = new PrivateKey(this.network.keySecret, this.privKey.private, this.privKey.compressed); + var obj = { + created: this.created, + priv: priv.toString(), + pub: pubKey, + addr: addr.toString(), + }; - return obj; - }; + return obj; + }; - WalletKey.prototype.fromObj = function(obj) { - this.created = obj.created; - this.privKey = new KeyModule.Key(); - if (obj.priv.length==64) { - this.privKey.private = new Buffer(obj.priv,'hex'); - this.privKey.compressed = true; - } - else { - var priv = new PrivateKey(obj.priv); - this.privKey.private = new Buffer(priv.payload()); - this.privKey.compressed = priv.compressed(); - } - this.privKey.regenerateSync(); - }; + WalletKey.prototype.fromObj = function(obj) { + this.created = obj.created; + this.privKey = new KeyModule.Key(); + if (obj.priv.length==64) { + this.privKey.private = new Buffer(obj.priv,'hex'); + this.privKey.compressed = true; + } + else { + var priv = new PrivateKey(obj.priv); + this.privKey.private = new Buffer(priv.payload()); + this.privKey.compressed = priv.compressed(); + } + this.privKey.regenerateSync(); + }; - return WalletKey; + return WalletKey; }; module.defineClass(ClassSpec); diff --git a/const.js b/const.js index 9edf459..7e4d7ed 100644 --- a/const.js +++ b/const.js @@ -1,8 +1,8 @@ MSG = { - TX: 1, - BLOCK: 2, - FILTERED_BLOCK: 3, + TX: 1, + BLOCK: 2, + FILTERED_BLOCK: 3, }; MSG.to_str = function(t) { diff --git a/test/basic.js b/test/basic.js index 1ed1148..3939786 100644 --- a/test/basic.js +++ b/test/basic.js @@ -10,104 +10,104 @@ suite('basic'); function test_encode_priv(b58, payload, isTestnet, isCompressed) { - var network = isTestnet ? networks.testnet : networks.livenet; - var version = network.keySecret; - - var buf_pl = new Buffer(payload, 'hex'); - var buf; - if (isCompressed) { - buf = new Buffer(buf_pl.length + 1); - buf_pl.copy(buf); - buf[buf_pl.length] = 1; - } else - buf = buf_pl; - - var key = new KeyModule.Key(); - key.private = buf; - key.compressed = isCompressed; - - var privkey = new PrivateKey(version, buf); - assert.equal(privkey.toString(), b58); + var network = isTestnet ? networks.testnet : networks.livenet; + var version = network.keySecret; + + var buf_pl = new Buffer(payload, 'hex'); + var buf; + if (isCompressed) { + buf = new Buffer(buf_pl.length + 1); + buf_pl.copy(buf); + buf[buf_pl.length] = 1; + } else + buf = buf_pl; + + var key = new KeyModule.Key(); + key.private = buf; + key.compressed = isCompressed; + + var privkey = new PrivateKey(version, buf); + assert.equal(privkey.toString(), b58); } function test_encode_pub(b58, payload, isTestnet, addrType) { - var isScript = (addrType == 'script'); - var network = isTestnet ? networks.testnet : networks.livenet; - var version = isScript ? network.addressScript : network.addressPubkey; - var buf = new Buffer(payload, 'hex'); - var addr = new Address(version, buf); - assert.equal(addr.toString(), b58); + var isScript = (addrType == 'script'); + var network = isTestnet ? networks.testnet : networks.livenet; + var version = isScript ? network.addressScript : network.addressPubkey; + var buf = new Buffer(payload, 'hex'); + var addr = new Address(version, buf); + assert.equal(addr.toString(), b58); } function test_decode_priv(b58, payload, isTestnet, isCompressed) { - var network = isTestnet ? networks.testnet : networks.livenet; - var version = network.keySecret; - - var buf_pl = new Buffer(payload, 'hex'); - var buf; - if (isCompressed) { - buf = new Buffer(buf_pl.length + 1); - buf_pl.copy(buf); - buf[buf_pl.length] = 1; - } else - buf = buf_pl; - - var privkey = new PrivateKey(b58); - assert.equal(version, privkey.version()); - assert.equal(buf_pl.toString(), privkey.payload().toString()); + var network = isTestnet ? networks.testnet : networks.livenet; + var version = network.keySecret; + + var buf_pl = new Buffer(payload, 'hex'); + var buf; + if (isCompressed) { + buf = new Buffer(buf_pl.length + 1); + buf_pl.copy(buf); + buf[buf_pl.length] = 1; + } else + buf = buf_pl; + + var privkey = new PrivateKey(b58); + assert.equal(version, privkey.version()); + assert.equal(buf_pl.toString(), privkey.payload().toString()); } function test_decode_pub(b58, payload, isTestnet, addrType) { - var isScript = (addrType == 'script'); - var network = isTestnet ? networks.testnet : networks.livenet; - var version = isScript ? network.addressScript : network.addressPubkey; - var buf = new Buffer(payload, 'hex'); - var addr = new Address(b58); - - assert.equal(version, addr.version()); - assert.equal(buf.toString(), addr.payload().toString()); + var isScript = (addrType == 'script'); + var network = isTestnet ? networks.testnet : networks.livenet; + var version = isScript ? network.addressScript : network.addressPubkey; + var buf = new Buffer(payload, 'hex'); + var addr = new Address(b58); + + assert.equal(version, addr.version()); + assert.equal(buf.toString(), addr.payload().toString()); } function is_valid(datum) { - var b58 = datum[0]; - var payload = datum[1]; - var obj = datum[2]; - var isPrivkey = obj['isPrivkey']; - var isTestnet = obj['isTestnet']; - - if (isPrivkey) { - var isCompressed = obj['isCompressed']; - test_encode_priv(b58, payload, isTestnet, isCompressed); - test_decode_priv(b58, payload, isTestnet, isCompressed); - } else { - var addrType = obj['addrType']; - test_encode_pub(b58, payload, isTestnet, addrType); - test_decode_pub(b58, payload, isTestnet, addrType); - } + var b58 = datum[0]; + var payload = datum[1]; + var obj = datum[2]; + var isPrivkey = obj['isPrivkey']; + var isTestnet = obj['isTestnet']; + + if (isPrivkey) { + var isCompressed = obj['isCompressed']; + test_encode_priv(b58, payload, isTestnet, isCompressed); + test_decode_priv(b58, payload, isTestnet, isCompressed); + } else { + var addrType = obj['addrType']; + test_encode_pub(b58, payload, isTestnet, addrType); + test_decode_pub(b58, payload, isTestnet, addrType); + } } function is_invalid(datum) { - if (datum.length < 1) - throw new Error("Bad test"); - - // ignore succeeding elements, as comments - var b58 = datum[0]; - var privkey = new PrivateKey(b58); - var addr = new Address(b58); - - var valid = true; - try { - privkey.validate(); - addr.validate(); - } catch(e) { - valid = false; - } - assert.equal(valid, false); + if (datum.length < 1) + throw new Error("Bad test"); + + // ignore succeeding elements, as comments + var b58 = datum[0]; + var privkey = new PrivateKey(b58); + var addr = new Address(b58); + + var valid = true; + try { + privkey.validate(); + addr.validate(); + } catch(e) { + valid = false; + } + assert.equal(valid, false); } var dataValid = JSON.parse(fs.readFileSync('test/base58_keys_valid.json')); diff --git a/util/EncFile.js b/util/EncFile.js index 4aeffa2..8cbd547 100644 --- a/util/EncFile.js +++ b/util/EncFile.js @@ -4,56 +4,56 @@ var crypto = require('crypto'); exports.readFileSync = function(enc_method, enc_passphrase, filename) { - // read entire file into memory - var fileData = fs.readFileSync(filename, 'binary'); - if (fileData.length < 32) - throw new Error("Crypted file " + filename + " truncated"); - - // separate into data, hmac parts - var fileCrypted = fileData.slice(0, -32); - var fileHmac = fileData.slice(-32); - - // generate and verify HMAC - var hmac = crypto.createHmac('sha256', enc_passphrase); - hmac.update(fileCrypted); - var digest = hmac.digest('binary'); - - if (digest.toString() != fileHmac.toString()) - throw new Error("Crypted file " + filename + " failed HMAC checksum verification"); - - // decrypt to plaintext - var decipher = crypto.createDecipher(enc_method, enc_passphrase); - var dec = decipher.update(fileCrypted, 'binary', 'binary'); - dec += decipher.final('binary'); - return dec; + // read entire file into memory + var fileData = fs.readFileSync(filename, 'binary'); + if (fileData.length < 32) + throw new Error("Crypted file " + filename + " truncated"); + + // separate into data, hmac parts + var fileCrypted = fileData.slice(0, -32); + var fileHmac = fileData.slice(-32); + + // generate and verify HMAC + var hmac = crypto.createHmac('sha256', enc_passphrase); + hmac.update(fileCrypted); + var digest = hmac.digest('binary'); + + if (digest.toString() != fileHmac.toString()) + throw new Error("Crypted file " + filename + " failed HMAC checksum verification"); + + // decrypt to plaintext + var decipher = crypto.createDecipher(enc_method, enc_passphrase); + var dec = decipher.update(fileCrypted, 'binary', 'binary'); + dec += decipher.final('binary'); + return dec; }; exports.readJFileSync = function(enc_method, enc_passphrase, filename) { - var raw = this.readFileSync(enc_method, enc_passphrase, filename); - return JSON.parse(raw); + var raw = this.readFileSync(enc_method, enc_passphrase, filename); + return JSON.parse(raw); }; exports.writeFileSync = function(enc_method, enc_passphrase, filename, data) { - // encrypt to ciphertext - var cipher = crypto.createCipher(enc_method, enc_passphrase); - var crypted = cipher.update(data, 'binary', 'binary'); - crypted += cipher.final('binary'); + // encrypt to ciphertext + var cipher = crypto.createCipher(enc_method, enc_passphrase); + var crypted = cipher.update(data, 'binary', 'binary'); + crypted += cipher.final('binary'); - // compute HMAC - var hmac = crypto.createHmac('sha256', enc_passphrase); - hmac.update(crypted); - var digest = hmac.digest('binary'); + // compute HMAC + var hmac = crypto.createHmac('sha256', enc_passphrase); + hmac.update(crypted); + 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) { - var raw = JSON.stringify(obj); - return this.writeFileSync(enc_method, enc_passphrase, filename, raw); + var raw = JSON.stringify(obj); + return this.writeFileSync(enc_method, enc_passphrase, filename, raw); }; diff --git a/util/time.js b/util/time.js index e5387f5..1582fca 100644 --- a/util/time.js +++ b/util/time.js @@ -2,6 +2,6 @@ // current time, in seconds exports.curtime = function curtime() { - return Math.round(Date.now() / 1000); + return Math.round(Date.now() / 1000); } diff --git a/util/util.js b/util/util.js index 145568d..f12a38f 100644 --- a/util/util.js +++ b/util/util.js @@ -113,42 +113,42 @@ var reWholeVal = /^\s*(\d+)/; function padFrac(frac) { - frac=frac.substr(0,8); //truncate to 8 decimal places - while (frac.length < 8) - frac = frac + '0'; - return frac; + frac=frac.substr(0,8); //truncate to 8 decimal places + while (frac.length < 8) + frac = frac + '0'; + return frac; } 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) { - return bignum(padFrac(res[1])); + return bignum(padFrac(res[1])); } function parseWholeValue(res) { - return bignum(res[1]).mul('100000000'); + return bignum(res[1]).mul('100000000'); } exports.parseValue = function parseValue(valueStr) { - var res = valueStr.match(reFullVal); - if (res) - return parseFullValue(res); + var res = valueStr.match(reFullVal); + if (res) + return parseFullValue(res); - res = valueStr.match(reFracVal); - if (res) - return parseFracValue(res); + res = valueStr.match(reFracVal); + if (res) + return parseFracValue(res); - res = valueStr.match(reWholeVal); - if (res) - return parseWholeValue(res); + res = valueStr.match(reWholeVal); + if (res) + return parseWholeValue(res); - return undefined; + return undefined; }; // Utility that synchronizes function calls based on a key