diff --git a/lib/bitcore-0.1.35-paypro.js b/lib/bitcore-0.1.35-paypro.js index 77cc02a90..c9206c735 100644 --- a/lib/bitcore-0.1.35-paypro.js +++ b/lib/bitcore-0.1.35-paypro.js @@ -1,315 +1,31828 @@ require=(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);throw new Error("Cannot find module '"+o+"'")}var f=n[o]={exports:{}};t[o][0].call(f.exports,function(e){var n=t[o][1][e];return s(n?n:e)},f,f.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;oi;i++)n[i]^=r[i];var o=t.fromUncompressedPubKey(e);o=t.multiply(o,n);var u=o.toUncompressedPubKey();return u},r.prototype.next=function(){var e=this.generatePubKey();return new r(this.chaincode,e)},r.fromMasterPublicKey=function(e){var n=e.substr(0,130),t=e.substr(130,e.length);return new r(t,n)},r.decodeSeed=function(t){for(var i=t.trim().split("\n"),o=[],c=0;ct)return!0;if(t>n)return!1;var o=r.slice(4,8).readUInt32BE(0),i=e.slice(4,8).readUInt32BE(0);return o>i?!0:!1},a._encrypt=function(r,e,t,o){var i=n.encrypt(r,e,t,o);return i},a._decrypt=function(r,e){var t=n.decrypt(r,e);return t},a._sign=function(r,n){var t=e.sign(n,r);return t},a._verify=function(r,n,t){var o=e.verifyWithPubKey(r,t,n);return o},module.exports=a}).call(this,require("buffer").Buffer); -},{"./ECIES":"0Qraa1","./Key":"ALJ4PS","./Message":"CBDCgz","buffer":97,"preconditions":165}],"./lib/AuthMessage":[function(require,module,exports){ +(function (Buffer){ +var Point = require('./Point'), + Key = require('./Key'), + sha256 = require('../util').sha256, + twoSha256 = require('../util').twoSha256; + +/** + * For now, this class can only supports derivation from public key + * It doesn't support private key derivation (TODO). + * + * @example examples/Armory.js + */ +function Armory(chaincode, pubkey) { + this.chaincode = new Buffer(chaincode, 'hex'); + this.pubkey = new Buffer(pubkey, 'hex'); +} + +Armory.prototype.generatePubKey = function() { + var pubKey = this.pubkey; + var chainCode = this.chaincode; + var chainXor = twoSha256(pubKey); + + for (var i = 0; i < 32; i++) + chainXor[i] ^= chainCode[i]; + + var pt = Point.fromUncompressedPubKey(pubKey); + pt = Point.multiply(pt, chainXor); + + var new_pubkey = pt.toUncompressedPubKey(); + + return new_pubkey; +}; + +Armory.prototype.next = function() { + var next_pubkey = this.generatePubKey(); + return new Armory(this.chaincode, next_pubkey); +}; + +/** + * PS: MPK here represents the pubkey concatenated + * with the chain code. It is an unofficial standard. + * + * Armory will soon release an officially supported + * format: + * + * https://github.com/etotheipi/BitcoinArmory/issues/204#issuecomment-42217801 + */ +Armory.fromMasterPublicKey = function(mpk) { + var pubkey = mpk.substr(0, 130); + var chaincode = mpk.substr(130, mpk.length); + return new Armory(chaincode, pubkey); +}; + +function decode(str) { + var from = '0123456789abcdef'; + var to = 'asdfghjkwertuion'; + var res = ''; + for (var i = 0; i < str.length; i++) + res += from.charAt(to.indexOf(str.charAt(i))); + return res; +} + +Armory.decodeSeed = function(seed) { + var keys = seed.trim().split('\n'); + var lines = []; + + for (var i = 0; i < keys.length; i++) { + var k = keys[i].replace(' ', ''); + var raw = new Buffer(decode(k), 'hex'); + var data = raw.slice(0, 16); + lines.push(data); + } + + var privKey = Buffer.concat([lines[0], lines[1]]); + var chainCode = (lines.length == 4) ? + Buffer.concat([lines[2], lines[3]]) : Armory.deriveChaincode(privKey); + + return { + privKey: privKey, + chainCode: chainCode + }; +}; + +// Derive chain code from root key +Armory.fromSeed = function(seed) { + var res = Armory.decodeSeed(seed); + // generate first public key + var key = new Key(); + key.private = res.privKey; + key.compressed = false; + key.regenerateSync(); + + return new Armory(res.chainCode, key.public); +}; + +Armory.deriveChaincode = function(root) { + var msg = 'Derive Chaincode from Root Key'; + var hash = twoSha256(root); + + var okey = []; + var ikey = []; + for (var i = 0; i < hash.length; i++) { + okey.push(0x5c ^ hash[i]); + ikey.push(0x36 ^ hash[i]); + } + + okey = new Buffer(okey); + ikey = new Buffer(ikey); + + var m = new Buffer(msg, 'utf8'); + var a = sha256(Buffer.concat([ikey, m])); + var b = sha256(Buffer.concat([okey, a])); + return b; +}; + +module.exports = Armory; + +}).call(this,require("buffer").Buffer) +},{"../util":206,"./Key":"ALJ4PS","./Point":"6tXgqr","buffer":108}],"cBnJMk":[function(require,module,exports){ +(function (Buffer){ +'use strict'; + +var Message = require('./Message'); +var ECIES = require('./ECIES'); +var preconditions = require('preconditions').singleton(); +var Key = require('./Key'); + + +var majorVersion = 1; +var minorVersion = 0; + +/* Encrypted, authenticated messages to be shared between copayers */ +var AuthMessage = function() {}; + +AuthMessage.setVersion = function(major, minor) { + majorVersion = major; + minorVersion = minor; +}; + +AuthMessage.encode = function(topubkey, fromkey, payload, opts) { + preconditions.checkArgument(fromkey instanceof Key, 'fromkey'); + if (typeof topubkey === 'string') { + topubkey = new Buffer(topubkey, 'hex'); + } + if (!(payload instanceof Buffer)) { + payload = new Buffer(JSON.stringify(payload)); + } + //peers should reject messges containing bigger major version + //i.e., increment to prevent communications with old clients + var version1 = new Buffer([majorVersion]); + + //peers should not reject messages containing not-understood minorversion + //i.e., increment to allow communication with old clients, but signal new clients + var version2 = new Buffer([minorVersion]); + + if (opts && opts.nonce && Buffer.isBuffer(opts.nonce) && opts.nonce.length == 8) { + var nonce = opts.nonce; + } else { + var nonce = new Buffer(8); + nonce.fill(0); //nonce is a big endian 8 byte number + } + + var toencrypt = Buffer.concat([version1, version2, nonce, payload]); + var toencrypthexbuf = new Buffer(toencrypt.toString('hex')); //due to bug in sjcl/bitcore, must use hex string + var encrypted = AuthMessage._encrypt(topubkey, toencrypthexbuf); + var sig = AuthMessage._sign(fromkey, encrypted); + var encoded = { + pubkey: fromkey.public.toString('hex'), + sig: sig.toString('hex'), + encrypted: encrypted.toString('hex'), + to: topubkey.toString('hex') + }; + return encoded; +}; + +AuthMessage.decode = function(key, encoded, opts) { + if (opts && opts.prevnonce && Buffer.isBuffer(opts.prevnonce) && opts.prevnonce.length == 8) { + var prevnonce = opts.prevnonce; + } else { + var prevnonce = new Buffer(8); + prevnonce.fill(0); //nonce is a big endian 8 byte number + } + + try { + var frompubkey = new Buffer(encoded.pubkey, 'hex'); + } catch (e) { + throw new Error('Error decoding public key: ' + e); + } + + try { + var sig = new Buffer(encoded.sig, 'hex'); + var encrypted = new Buffer(encoded.encrypted, 'hex'); + } catch (e) { + throw new Error('Error decoding data: ' + e); + } + + try { + var v = AuthMessage._verify(frompubkey, sig, encrypted); + } catch (e) { + throw new Error('Error verifying signature: ' + e); + } + + if (!v) { + throw new Error('Invalid signature'); + } + + try { + var decryptedhexbuf = AuthMessage._decrypt(key.private, encrypted); + var decrypted = new Buffer(decryptedhexbuf.toString(), 'hex'); //workaround for bug in bitcore/sjcl + } catch (e) { + throw new Error('Cannot decrypt data: ' + e); + } + + try { + var version1 = decrypted[0]; + var version2 = decrypted[1]; + var nonce = decrypted.slice(2, 10); + var payload = decrypted.slice(10); + } catch (e) { + throw new Error('Cannot parse decrypted data: ' + e); + } + + if (payload.length === 0) { + throw new Error('No data present'); + } + + if (version1 !== majorVersion) { + throw new Error('Invalid version number'); + } + + if (version2 !== minorVersion) { + //put special version2 handling code here, if ever needed + } + + if (!AuthMessage._noncegt(nonce, prevnonce) && prevnonce.toString('hex') !== '0000000000000000') { + throw new Error('Nonce not equal to zero and not greater than the previous nonce'); + } + + try { + payload = JSON.parse(payload); + } catch (e) { + if (e instanceof SyntaxError) { + // if we can't parse a JSON, just return what we found + } else { + throw e; + } + } + + var decoded = { + version1: version1, + version2: version2, + nonce: nonce, + payload: payload + }; + + return decoded; +}; + +//return true if nonce > prevnonce; false otherwise +AuthMessage._noncegt = function(nonce, prevnonce) { + var noncep1 = nonce.slice(0, 4).readUInt32BE(0); + var prevnoncep1 = prevnonce.slice(0, 4).readUInt32BE(0); + + if (noncep1 > prevnoncep1) + return true; + + if (noncep1 < prevnoncep1) + return false; + + var noncep2 = nonce.slice(4, 8).readUInt32BE(0); + var prevnoncep2 = prevnonce.slice(4, 8).readUInt32BE(0); + + if (noncep2 > prevnoncep2) + return true; + + return false; +}; + +AuthMessage._encrypt = function(topubkey, payload, r, iv) { + var encrypted = ECIES.encrypt(topubkey, payload, r, iv); + return encrypted; +}; + +AuthMessage._decrypt = function(privkey, encrypted) { + var decrypted = ECIES.decrypt(privkey, encrypted); + return decrypted; +}; + +AuthMessage._sign = function(key, payload) { + var sig = Message.sign(payload, key); + return sig; +}; + +AuthMessage._verify = function(pubkey, signature, payload) { + var v = Message.verifyWithPubKey(pubkey, payload, signature); + return v; +}; + +module.exports = AuthMessage; + +}).call(this,require("buffer").Buffer) +},{"./ECIES":"0Qraa1","./Key":"ALJ4PS","./Message":"CBDCgz","buffer":108,"preconditions":178}],"./lib/AuthMessage":[function(require,module,exports){ module.exports=require('cBnJMk'); },{}],"Rpcuro":[function(require,module,exports){ -"use strict";var URL=require("url"),Address=require("./Address"),BIP21=function(t){if(this.data={},this.address=void 0,"string"==typeof t)this.parse(t);else if("object"==typeof t)this.fromObj(t);else if("undefined"!=typeof t)throw new Error("Invalid argument")};BIP21.prototype.fromObj=function(t){for(var r in t)this.data[r]=t[r];t.address&&(delete this.data.address,this.setAddress(t.address))},BIP21.prototype.parse=function(t){var r=URL.parse(t,!0);if("bitcoin:"!=r.protocol)throw new Error("Invalid protocol");var e=/[^:]*:\/?\/?([^?]*)/.exec(t);this.setAddress(e&&e[1]);for(var s in r.query){var d=r.query[s];"amount"===s&&(d=Number(d)),"r"===s&&(this.data.merchant=d),this.data[s]=d}},BIP21.prototype.isValid=function(t){var r=t||[],e=!0;"undefined"!=typeof this.data.amount&&(e&=!isNaN(this.data.amount)),this.address&&(e&="object"==typeof this.address&&this.address.isValid()),e&=!(!this.address&&!this.data.r);for(var s in this.data)0==s.indexOf("req-")&&(e&=-1!=r.indexOf(s));return!!e},BIP21.prototype.setAddress=function(t){return t&&(this.address=Address.validate(t)?new Address(t):t),this},BIP21.prototype.getURI=function(){return URL.format({protocol:"bitcoin:",host:this.address,query:this.data})},module.exports=BIP21; -},{"./Address":"G+CcXD","url":128}],"./lib/BIP21":[function(require,module,exports){ +'use strict'; + +// BIP21 +// ======= +// Helper for parsing and building bitcoin: URIs +// +// Examples: +// ======= +// var uriString = 'bitcoin:1DP69gMMvSuYhbnxsi4EJEFufUAbDrEQfj?message=Hey%20there&amount=1.212'; +// +// var uri = new BIP21(uriString); +// uri.isValid() // true +// uri.address // bitcore.Address object +// uri.data.message // 'Hey there' +// uri.data.amount // 1.212 +// +// uriString = new BIP21({ +// address: '1DP69gMMvSuYhbnxsi4EJEFufUAbDrEQfj', +// message: 'Hey there', +// amount: 1.212 +// }).getURI(); + + +var URL = require('url'); +var Address = require('./Address'); + +var BIP21 = function(arg) { + this.data = {}; + this.address = undefined; + + if (typeof(arg) == 'string') { + this.parse(arg); + } else if (typeof(arg) == 'object') { + this.fromObj(arg); + } else if (typeof(arg) != 'undefined') { + throw new Error('Invalid argument'); + } +} + +BIP21.prototype.fromObj = function(obj) { + for (var key in obj) { + this.data[key] = obj[key]; + } + + if (obj.address) { + delete this.data.address; + this.setAddress(obj.address); + } +} + +BIP21.prototype.parse = function(uri) { + var info = URL.parse(uri, true); + + if (info.protocol != 'bitcoin:') { + throw new Error('Invalid protocol'); + } + + // workaround to host insensitiveness + var group = /[^:]*:\/?\/?([^?]*)/.exec(uri); + this.setAddress(group && group[1]); + + for (var arg in info.query) { + var val = info.query[arg]; + if (arg === 'amount') val = Number(val); + if (arg === 'r') this.data.merchant = val; + this.data[arg] = val; + } +} + +BIP21.prototype.isValid = function(known) { + var knownArguments = known || []; + var valid = true; + + if (typeof(this.data.amount) != 'undefined') { + valid &= !isNaN(this.data.amount); + } + + if (this.address) { + valid &= typeof(this.address) == 'object' && this.address.isValid(); + } + + // Require address or PayPro info + valid &= !!(this.address || this.data.r); + + // Check required arguments + for (var key in this.data) { + if (key.indexOf('req-') == 0) { + valid &= knownArguments.indexOf(key) != -1; + } + } + + return !!valid; +} + +BIP21.prototype.setAddress = function(addr) { + if (addr) { + this.address = Address.validate(addr) ? new Address(addr) : addr; + } + + return this; +} + +BIP21.prototype.getURI = function() { + return URL.format({ + protocol: 'bitcoin:', + host: this.address, + query: this.data + }); +} + +module.exports = BIP21; + +},{"./Address":"G+CcXD","url":139}],"./lib/BIP21":[function(require,module,exports){ module.exports=require('Rpcuro'); },{}],"./lib/BIP39":[function(require,module,exports){ module.exports=require('82LilS'); },{}],"82LilS":[function(require,module,exports){ -(function(e){var n=require("../util"),r=require("./sjcl"),t=require("./SecureRandom"),i=function(e){var n=new r.misc.hmac(e,r.hash.sha512);this.encrypt=function(){return n.encrypt.apply(n,arguments)}},o=function(e,n,t){var o=r.misc.pbkdf2(e,n,t,512,i);return r.codec.hex.fromBits(o)},l=function(){};l.mnemonic=function(e,n){if(n||(n=128),n%32!=0)throw new Error("bits must be multiple of 32");var r=t.getRandomBuffer(n/8);return l.entropy2mnemonic(e,r)},l.entropy2mnemonic=function(e,r){for(var t=n.sha256(r),i="",o=8*r.length,l=0;lc)return!1;o+=("00000000000"+c.toString(2)).slice(-11)}if(o.length%11!=0)throw new Error("internal error - entropy not an even multiple of 11 bits - "+o.length);for(var u=o.length/33,s=o.slice(-u),a=o.slice(0,o.length-u),f=new e(a.length/8),l=0;l0){var i=new e(t);return i.fill(0),t==r.length?i:(n=n.toBuffer(),e.concat([i,n],t+n.length))}return n.toBuffer()}},s={encode:function(r){var t=new e(r.length+4),o=n(r);return r.copy(t),o.copy(t,r.length),l.encode(t)},decode:function(e){var r=l.decode(e);if(r.length<4)throw new Error("invalid input: too short");var t=r.slice(0,-4),o=r.slice(-4),i=n(t),c=i.slice(0,4);if(o.toString("hex")!==c.toString("hex"))throw new Error("checksum mismatch");return t}};exports.setBuffer=function(e){i=e},exports.base58=l,exports.base58Check=s,exports.encode=l.encode,exports.decode=l.decode}).call(this,require("buffer").Buffer); -},{"bignum":63,"buffer":97,"crypto":101}],"./lib/Block":[function(require,module,exports){ +(function (Buffer){ +var crypto = require('crypto'); +var bignum = require('bignum'); + +var globalBuffer = new Buffer(1024); +var zerobuf = new Buffer(0); +var ALPHABET = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'; +var ALPHABET_ZERO = ALPHABET[0]; +var ALPHABET_BUF = new Buffer(ALPHABET, 'ascii'); +var ALPHABET_INV = {}; +for (var i = 0; i < ALPHABET.length; i++) { + ALPHABET_INV[ALPHABET[i]] = i; +}; + +// Vanilla Base58 Encoding +var base58 = { + encode: function(buf) { + var str; + var x = bignum.fromBuffer(buf); + var r; + + if (buf.length < 512) { + str = globalBuffer; + } else { + str = new Buffer(buf.length << 1); + } + var i = str.length - 1; + while (x.gt(0)) { + r = x.mod(58); + x = x.div(58); + str[i] = ALPHABET_BUF[r.toNumber()]; + i--; + } + + // deal with leading zeros + var j = 0; + while (buf[j] == 0) { + str[i] = ALPHABET_BUF[0]; + j++; + i--; + } + + return str.slice(i + 1, str.length).toString('ascii'); + }, + + decode: function(str) { + if (str.length == 0) return zerobuf; + var answer = bignum(0); + for (var i = 0; i < str.length; i++) { + answer = answer.mul(58); + answer = answer.add(ALPHABET_INV[str[i]]); + }; + var i = 0; + while (i < str.length && str[i] == ALPHABET_ZERO) { + i++; + } + if (i > 0) { + var zb = new Buffer(i); + zb.fill(0); + if (i == str.length) return zb; + answer = answer.toBuffer(); + return Buffer.concat([zb, answer], i + answer.length); + } else { + return answer.toBuffer(); + } + }, +}; + +// Base58Check Encoding +function sha256(data) { + return new Buffer(crypto.createHash('sha256').update(data).digest('binary'), 'binary'); +}; + +function doubleSHA256(data) { + return sha256(sha256(data)); +}; + +var base58Check = { + encode: function(buf) { + var checkedBuf = new Buffer(buf.length + 4); + var hash = doubleSHA256(buf); + buf.copy(checkedBuf); + hash.copy(checkedBuf, buf.length); + return base58.encode(checkedBuf); + }, + + decode: function(s) { + var buf = base58.decode(s); + if (buf.length < 4) { + throw new Error("invalid input: too short"); + } + + var data = buf.slice(0, -4); + var csum = buf.slice(-4); + + var hash = doubleSHA256(data); + var hash4 = hash.slice(0, 4); + + if (csum.toString('hex') !== hash4.toString('hex')) { + throw new Error("checksum mismatch"); + } + + return data; + }, +}; + +// if you frequently do base58 encodings with data larger +// than 512 bytes, you can use this method to expand the +// size of the reusable buffer +exports.setBuffer = function(buf) { + globalBuffer = buf; +}; + +exports.base58 = base58; +exports.base58Check = base58Check; +exports.encode = base58.encode; +exports.decode = base58.decode; + +}).call(this,require("buffer").Buffer) +},{"bignum":63,"buffer":108,"crypto":112}],"./lib/Block":[function(require,module,exports){ module.exports=require('pJEQEB'); },{}],"pJEQEB":[function(require,module,exports){ -(function(t){function e(t){"object"!=typeof t&&(t={}),this.hash=t.hash||null,this.prev_hash=t.prev_hash||r.NULL_HASH,this.merkle_root=t.merkle_root||r.NULL_HASH,this.timestamp=t.timestamp||0,this.bits=t.bits||0,this.nonce=t.nonce||0,this.version=t.version||0,this.height=t.height||0,this.size=t.size||0,this.active=t.active||!1,this.chainWork=t.chainWork||r.EMPTY_BUFFER,this.txs=t.txs||[]}var r=require("../util"),i=require("./Script"),o=require("bignum"),s=(require("binary"),require("step"),require("buffertools")),h=require("./Transaction"),n=h.In,a=h.Out,c=h.COINBASE_OP,u=require("../util/error").VerificationError,l={maxTimeOffset:7200,largestHash:new o("10000000000000000000000000000000000000000000000000000000000000000",16)};e.prototype.getHeader=function(){var e=new t(80),r=0;return e.writeUInt32LE(this.version,r),r+=4,this.prev_hash.copy(e,r),r+=32,this.merkle_root.copy(e,r),r+=32,e.writeUInt32LE(this.timestamp,r),r+=4,e.writeUInt32LE(this.bits,r),r+=4,e.writeUInt32LE(this.nonce,r),r+=4,e},e.prototype.parse=function(t,e){if(this.version=t.word32le(),this.prev_hash=t.buffer(32),this.merkle_root=t.buffer(32),this.timestamp=t.word32le(),this.bits=t.word32le(),this.nonce=t.word32le(),this.txs=[],this.size=0,!e)for(var r=t.varInt(),i=0;r>i;i++){var o=new h;o.parse(t),this.txs.push(o)}},e.prototype.calcHash=function(){var t=this.getHeader();return r.twoSha256(t)},e.prototype.checkHash=function(){return this.hash&&this.hash.length?0==s.compare(this.calcHash(),this.hash):!1},e.prototype.getHash=function(){return this.hash&&this.hash.length||(this.hash=this.calcHash()),this.hash},e.prototype.checkProofOfWork=function(){var t=r.decodeDiffBits(this.bits),e=s.reverse(this.hash);if(s.compare(e,t)>0)throw new u("Difficulty target not met");return!0},e.prototype.getWork=function(){var t=r.decodeDiffBits(this.bits,!0);return l.largestHash.div(t.add(1))},e.prototype.checkTimestamp=function(){var t=(new Date).getTime()/1e3;if(this.timestamp>t+l.maxTimeOffset)throw new u("Timestamp too far into the future");return!0},e.prototype.checkTransactions=function(t){if(!Array.isArray(t)||t.length<=0)throw new u("No transactions");if(!t[0].isCoinBase())throw new u("First tx must be coinbase");for(var e=1;e1;s=Math.floor((s+1)/2)){for(var n=0;s>n;n+=2){var a=Math.min(n+1,s-1),c=i[o+n],u=i[o+a];i.push(r.twoSha256(t.concat([c,u])))}o+=s}return i},e.prototype.calcMerkleRoot=function(t){var e=this.getMerkleTree(t);return e[e.length-1]},e.prototype.checkMerkleRoot=function(e){if(!this.merkle_root||!this.merkle_root.length)throw new u("No merkle root");if(0!==s.compare(this.calcMerkleRoot(e),new t(this.merkle_root)))throw new u("Merkle root incorrect");return!0},e.prototype.checkBlock=function(t){if(!this.checkHash())throw new u("Block hash invalid");if(this.checkProofOfWork(),this.checkTimestamp(),t&&(this.checkTransactions(t),!this.checkMerkleRoot(t)))throw new u("Merkle hash invalid");return!0},e.getBlockValue=function(t){var e=50*r.COIN;return e/=Math.pow(2,Math.floor(t/21e4)),e=Math.floor(e),e=new o(e)},e.prototype.getBlockValue=function(){return e.getBlockValue(this.height)},e.prototype.toString=function(){return""},e.prototype.createCoinbaseTx=function(t){var e=new h;return e.ins.push(new n({s:r.EMPTY_BUFFER,q:4294967295,o:c})),e.outs.push(new a({v:r.bigIntToValue(this.getBlockValue()),s:i.createPubKeyOut(t).getBuffer()})),e},e.prototype.solve=function(t,e){var i=this.getHeader(),o=r.decodeDiffBits(this.bits);t.solve(i,o,e)},e.prototype.getStandardizedObject=function(t){var e={hash:r.formatHashFull(this.getHash()),version:this.version,prev_block:r.formatHashFull(this.prev_hash),mrkl_root:r.formatHashFull(this.merkle_root),time:this.timestamp,bits:this.bits,nonce:this.nonce,height:this.height};if(t){var i=this.getMerkleTree(t).map(function(t){return r.formatHashFull(t)});e.mrkl_root=i[i.length-1],e.n_tx=t.length;var o=80;o+=r.getVarIntSize(t.length),t=t.map(function(t){return t=t.getStandardizedObject(),o+=t.size,t}),e.size=o,e.tx=t,e.mrkl_tree=i}else e.size=this.size;return e},module.exports=e}).call(this,require("buffer").Buffer); -},{"../util":193,"../util/error":192,"./Script":"hQ0t76","./Transaction":"LJhYtm","bignum":63,"binary":85,"buffer":97,"buffertools":"fugeBw","step":180}],"./lib/Bloom":[function(require,module,exports){ +(function (Buffer){ +var util = require('../util'); +var Script = require('./Script'); +var Bignum = require('bignum'); +var Binary = require('binary'); +var Step = require('step'); +var buffertools = require('buffertools'); +var Transaction = require('./Transaction'); +var TransactionIn = Transaction.In; +var TransactionOut = Transaction.Out; +var COINBASE_OP = Transaction.COINBASE_OP; +var VerificationError = require('../util/error').VerificationError; +var BlockRules = { + maxTimeOffset: 2 * 60 * 60, // How far block timestamps can be into the future + //largestHash: (new Bignum(2)).pow(256) + //largestHash: new Bignum('115792089237316195423570985008687907853269984665640564039457584007913129639936') // = 2^256 + largestHash: new Bignum('10000000000000000000000000000000000000000000000000000000000000000', 16) +}; + +function Block(data) { + if ("object" !== typeof data) { + data = {}; + } + this.hash = data.hash || null; + this.prev_hash = data.prev_hash || util.NULL_HASH; + this.merkle_root = data.merkle_root || util.NULL_HASH; + this.timestamp = data.timestamp || 0; + this.bits = data.bits || 0; + this.nonce = data.nonce || 0; + this.version = data.version || 0; + this.height = data.height || 0; + this.size = data.size || 0; + this.active = data.active || false; + this.chainWork = data.chainWork || util.EMPTY_BUFFER; + this.txs = data.txs || []; +} + +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; + return buf; +}; + +Block.prototype.parse = function parse(parser, headerOnly) { + this.version = parser.word32le(); + this.prev_hash = parser.buffer(32); + this.merkle_root = parser.buffer(32); + this.timestamp = parser.word32le(); + this.bits = parser.word32le(); + this.nonce = parser.word32le(); + + this.txs = []; + this.size = 0; + + if (headerOnly) + return; + + var txCount = parser.varInt(); + + for (var i = 0; i < txCount; i++) { + var tx = new Transaction(); + tx.parse(parser); + this.txs.push(tx); + } +}; + +Block.prototype.calcHash = function calcHash() { + var header = this.getHeader(); + + return util.twoSha256(header); +}; + +Block.prototype.checkHash = function checkHash() { + if (!this.hash || !this.hash.length) return false; + return buffertools.compare(this.calcHash(), this.hash) == 0; +}; + +Block.prototype.getHash = function getHash() { + if (!this.hash || !this.hash.length) this.hash = this.calcHash(); + + return this.hash; +}; + +Block.prototype.checkProofOfWork = function checkProofOfWork() { + var target = util.decodeDiffBits(this.bits); + + // TODO: Create a compare method in node-buffertools that uses the correct + // endian so we don't have to reverse both buffers before comparing. + var reverseHash = buffertools.reverse(this.hash); + if (buffertools.compare(reverseHash, target) > 0) { + throw new VerificationError('Difficulty target not met'); + } + + return true; +}; + +/** + * Returns the amount of work that went into this block. + * + * Work is defined as the average number of tries required to meet this + * block's difficulty target. For example a target that is greater than 5% + * of all possible hashes would mean that 20 "work" is required to meet it. + */ +Block.prototype.getWork = function getWork() { + var target = util.decodeDiffBits(this.bits, true); + return BlockRules.largestHash.div(target.add(1)); +}; + +Block.prototype.checkTimestamp = function checkTimestamp() { + var currentTime = new Date().getTime() / 1000; + if (this.timestamp > currentTime + BlockRules.maxTimeOffset) { + throw new VerificationError('Timestamp too far into the future'); + } + + return true; +}; + +Block.prototype.checkTransactions = function checkTransactions(txs) { + if (!Array.isArray(txs) || txs.length <= 0) { + throw new VerificationError('No transactions'); + } + if (!txs[0].isCoinBase()) { + throw new VerificationError('First tx must be coinbase'); + } + for (var i = 1; i < txs.length; i++) { + if (txs[i].isCoinBase()) { + throw new VerificationError('Tx index ' + i + ' must not be coinbase'); + } + } + + return true; +}; + +/** + * Build merkle tree. + * + * Ported from Java. Original code: BitcoinJ by Mike Hearn + * Copyright (c) 2011 Google Inc. + */ +Block.prototype.getMerkleTree = function getMerkleTree(txs) { + // The merkle hash is based on a tree of hashes calculated from the transactions: + // + // merkleHash + // /\ + // / \ + // A B + // / \ / \ + // tx1 tx2 tx3 tx4 + // + // Basically transactions are hashed, then the hashes of the transactions are hashed + // again and so on upwards into the tree. The point of this scheme is to allow for + // disk space savings later on. + // + // This function is a direct translation of CBlock::BuildMerkleTree(). + + if (txs.length == 0) { + return [util.NULL_HASH.slice(0)]; + } + + // Start by adding all the hashes of the transactions as leaves of the tree. + var tree = txs.map(function(tx) { + return tx instanceof Transaction ? tx.getHash() : tx; + }); + + var j = 0; + // Now step through each level ... + for (var size = txs.length; size > 1; size = Math.floor((size + 1) / 2)) { + // and for each leaf on that level .. + for (var i = 0; i < size; i += 2) { + var i2 = Math.min(i + 1, size - 1); + var a = tree[j + i]; + var b = tree[j + i2]; + tree.push(util.twoSha256(Buffer.concat([a, b]))); + } + j += size; + } + + return tree; +}; + +Block.prototype.calcMerkleRoot = function calcMerkleRoot(txs) { + var tree = this.getMerkleTree(txs); + return tree[tree.length - 1]; +}; + +Block.prototype.checkMerkleRoot = function checkMerkleRoot(txs) { + if (!this.merkle_root || !this.merkle_root.length) { + throw new VerificationError('No merkle root'); + } + + if (buffertools.compare(this.calcMerkleRoot(txs), new Buffer(this.merkle_root)) !== 0) { + throw new VerificationError('Merkle root incorrect'); + } + + return true; +}; + +Block.prototype.checkBlock = function checkBlock(txs) { + if (!this.checkHash()) { + throw new VerificationError("Block hash invalid"); + } + this.checkProofOfWork(); + this.checkTimestamp(); + + if (txs) { + this.checkTransactions(txs); + if (!this.checkMerkleRoot(txs)) { + throw new VerificationError("Merkle hash invalid"); + } + } + return true; +}; + +Block.getBlockValue = function getBlockValue(height) { + var subsidy = 50 * util.COIN; + subsidy = subsidy / (Math.pow(2, Math.floor(height / 210000))); + subsidy = Math.floor(subsidy); + subsidy = new Bignum(subsidy); + return subsidy; +}; + +Block.prototype.getBlockValue = function getBlockValue() { + return Block.getBlockValue(this.height); +}; + +Block.prototype.toString = function toString() { + return ""; +}; + + +Block.prototype.createCoinbaseTx = + function createCoinbaseTx(beneficiary) { + var tx = new Transaction(); + tx.ins.push(new TransactionIn({ + s: util.EMPTY_BUFFER, + q: 0xffffffff, + o: COINBASE_OP + })); + tx.outs.push(new TransactionOut({ + v: util.bigIntToValue(this.getBlockValue()), + s: Script.createPubKeyOut(beneficiary).getBuffer() + })); + return tx; +}; + +Block.prototype.solve = function solve(miner, callback) { + var header = this.getHeader(); + var target = util.decodeDiffBits(this.bits); + miner.solve(header, target, callback); +}; + +/** + * Returns an object with the same field names as jgarzik's getblock patch. + */ +Block.prototype.getStandardizedObject = + function getStandardizedObject(txs) { + var block = { + hash: util.formatHashFull(this.getHash()), + version: this.version, + prev_block: util.formatHashFull(this.prev_hash), + mrkl_root: util.formatHashFull(this.merkle_root), + time: this.timestamp, + bits: this.bits, + nonce: this.nonce, + height: this.height + }; + + + if (txs) { + var mrkl_tree = this.getMerkleTree(txs).map(function(buffer) { + return util.formatHashFull(buffer); + }); + block.mrkl_root = mrkl_tree[mrkl_tree.length - 1]; + + block.n_tx = txs.length; + var totalSize = 80; // Block header + totalSize += util.getVarIntSize(txs.length); // txn_count + txs = txs.map(function(tx) { + tx = tx.getStandardizedObject(); + totalSize += tx.size; + return tx; + }); + block.size = totalSize; + block.tx = txs; + + block.mrkl_tree = mrkl_tree; + } else { + block.size = this.size; + } + return block; +}; + +module.exports = Block; + +}).call(this,require("buffer").Buffer) +},{"../util":206,"../util/error":205,"./Script":"hQ0t76","./Transaction":"LJhYtm","bignum":63,"binary":98,"buffer":108,"buffertools":"fugeBw","step":193}],"KifRG4":[function(require,module,exports){ +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); +}; + + +module.exports = Bloom; + +},{}],"./lib/Bloom":[function(require,module,exports){ module.exports=require('KifRG4'); -},{}],"KifRG4":[function(require,module,exports){ -function Bloom(){this.data="",this.hashFuncs=0}function ROTL32(t,n){return t<>32-n}function getBlockU32(t,n){var o=4*t,s=n[o+0]<<0|n[o+1]<<8|n[o+2]<<16|n[o+3]<<24;return s}function toInt(t){return~~t}function min(t,n){return n>t?t:n}var MAX_BLOOM_FILTER_SIZE=36e3,MAX_HASH_FUNCS=50,LN2SQUARED=.48045301391820144,LN2=.6931471805599453,bit_mask=[1,2,4,8,16,32,64,128];Bloom.prototype.hash=function(t,n){for(var o=t*(4294967295/(this.hashFuncs-1)),s=3432918353,a=461845907,h=n.length/4,i=-h;i;i++){var r=getBlockU32(i);r*=s,r=ROTLF32(r,15),r*=a,o^=r,o=ROTFL(o,13),o=5*o+3864292196}var e=n.slice(4*h),r=0;switch(3&n.length){case 3:r^=e[2]<<16;case 2:r^=e[1]<<8;case 1:r^=e[0],r*=s,r=ROTL32(r,15),r*=a,o^=r}return o^=n.length,o^=o>>16,o*=2246822507,o^=o>>13,o*=3266489909,o^=o>>16,o%(8*this.data.length)},Bloom.prototype.insert=function(t){for(var n=0;n>3]|=bit_mask[7&o]}},Bloom.prototype.contains=function(t){for(var n=0;n>3]&bit_mask[7&o]))return!1}return!0},Bloom.prototype.sizeOk=function(){return this.data.length<=MAX_BLOOM_FILTER_SIZE&&this.hashFuncs<=MAX_HASH_FUNCS},Bloom.prototype.init=function(t,n){var o=min(toInt(-1/LN2SQUARED*t*Math.log(n)),8*MAX_BLOOM_FILTER_SIZE)/8;this.data[o]=0,this.hashFuncs=min(toInt(8*this.data.length/t*LN2),MAX_HASH_FUNCS)},module.exports=Bloom; },{}],"./lib/Connection":[function(require,module,exports){ module.exports=require('DB/p3X'); },{}],"DB/p3X":[function(require,module,exports){ -(function(e){function t(e,t,r){if(this.config=r||a,this.network=h[this.config.network]||h.livenet,this.socket=e,this.peer=t,this.config.proxy){var s=require("socks5-client");this.socket=new s(this.config.proxy.host,this.config.proxy.port)}this.active=!1,this.recvVer=0,this.sendVer=0,this.bestHeight=0,this.inbound=!!this.socket.server,this.getaddr=!1,this.buffers=new o,(new Date).getTime()>1329696e6&&(this.recvVer=209,this.sendVer=209),this.setupHandlers()}var r=require("../util/log"),s=1e7,n=7e4,i=require("bufferput"),o=require("buffers");require("../patches/Buffers.monkey").patch(o);var a=require("../config"),h=require("../networks"),c=require("./Block"),d=require("./Transaction"),f=require("../util"),u=require("../util/BinaryParser"),p=require("buffertools"),g=f.twoSha256,l=require("./SecureRandom"),v=l.getPseudoRandomBuffer(8),b=require("util"),k=require("events").EventEmitter,m=6e4;b.inherits(t,k),t.prototype.open=function(e){return"function"==typeof e&&this.once("connect",e),this.socket.connect(this.peer.port,this.peer.host),this},t.prototype.setupHandlers=function(){this.socket.addListener("connect",this.handleConnect.bind(this)),this.socket.addListener("error",this.handleError.bind(this)),this.socket.addListener("end",this.handleDisconnect.bind(this)),this.socket.addListener("data",function(e){var t=35;r.debug("["+this.peer+"] Recieved "+e.length+" bytes of data:"),r.debug("... "+p.toHex(e.slice(0,t>e.length?e.length:t))+(e.length>t?"...":""))}.bind(this)),this.socket.addListener("data",this.handleData.bind(this))},t.prototype.handleConnect=function(){this.inbound||this.sendVersion(),this.emit("connect",{conn:this,socket:this.socket,peer:this.peer})},t.prototype.handleError=function(e){110==e.errno||"ETIMEDOUT"==e.errno?r.info("connection timed out for "+this.peer):111==e.errno||"ECONNREFUSED"==e.errno?r.info("connection refused for "+this.peer):r.warn("connection with "+this.peer+" "+e.toString()),this.emit("error",{conn:this,socket:this.socket,peer:this.peer,err:e})},t.prototype.handleDisconnect=function(){this.emit("disconnect",{conn:this,socket:this.socket,peer:this.peer})},t.prototype.handleMessage=function(t){if(t){try{switch(t.command){case"version":if(0===p.compare(v,t.nonce))return void this.socket.end();this.inbound&&this.sendVersion(),t.version>=209&&this.sendMessage("verack",new e([])),this.sendVer=Math.min(t.version,n),t.version<209?this.recvVer=Math.min(t.version,n):this.once("verack",function(){this.recvVer=t.version}.bind(this)),this.bestHeight=t.start_height;break;case"verack":this.recvVer=Math.min(t.version,n),this.active=!0;break;case"ping":"object"==typeof t.nonce&&this.sendPong(t.nonce)}}catch(s){return void r.err('Error while handling "'+t.command+'" message from '+this.peer+":\n"+(s.stack?s.stack:s.toString()))}this.emit(t.command,{conn:this,socket:this.socket,peer:this.peer,message:t})}},t.prototype.sendPong=function(e){this.sendMessage("pong",e)},t.prototype.sendVersion=function(){var t="/BitcoinX:0.1/",r=new i;r.word32le(n),r.word64le(1),r.word64le(Math.round((new Date).getTime()/1e3)),r.pad(26),r.pad(26),r.put(v),r.varint(t.length),r.put(new e(t,"ascii")),r.word32le(0),this.sendMessage("version",r.buffer())},t.prototype.sendGetBlocks=function(t,r,s){r=r||f.NULL_HASH;var n=new i;n.word32le(this.sendVer),n.varint(t.length);for(var o=0;o12)throw"Command name too long";var a;a=this.sendVer>=209?g(s).slice(0,4):new e([]);var h=new i;h.put(n),h.put(o),h.pad(12-o.length),h.word32le(s.length),h.put(a),h.put(s);var c=h.buffer();r.debug("["+this.peer+"] Sending message "+t+" ("+s.length+" bytes)"),this.socket.write(c)}catch(d){r.err("Error while sending message to peer "+this.peer+": "+(d.stack?d.stack:d.toString()))}},t.prototype.handleData=function(e){return this.buffers.push(e),this.buffers.length>s?(r.err("Peer "+this.peer+" exceeded maxreceivebuffer, disconnecting."+(err.stack?err.stack:err.toString())),void this.socket.destroy()):void this.processData()},t.prototype.processData=function(){if(!(this.buffers.length<20)){for(var e=this.network.magic,t=0;;){if(this.buffers.get(t)===e[0]&&this.buffers.get(t+1)===e[1]&&this.buffers.get(t+2)===e[2]&&this.buffers.get(t+3)===e[3]){0!==t&&(r.debug("["+this.peer+"] Received "+t+" bytes of inter-message garbage: "),r.debug("... "+this.buffers.slice(0,t)),this.buffers.skip(t));break}if(t>this.buffers.length-4)return void this.buffers.skip(t);t++}var s=this.buffers.get(16)+(this.buffers.get(17)<<8)+(this.buffers.get(18)<<16)+(this.buffers.get(19)<<24),n=this.recvVer>=209?24:20,i=n+s;if(!(this.buffers.length=209?this.buffers.slice(20,24):null;if(r.debug("["+this.peer+"] Received message "+o+" ("+s+" bytes)"),null!==h){var c=g(a).slice(0,4);if(0!==p.compare(c,h))return void r.err("["+this.peer+"] Checksum failed",{cmd:o,expected:c.toString("hex"),actual:h.toString("hex")})}var d;try{d=this.parseMessage(o,a)}catch(f){r.err("Error while parsing message "+o+" from "+this.peer+":\n"+(f.stack?f.stack:f.toString()))}d&&this.handleMessage(d),this.buffers.skip(i),this.processData()}}},t.prototype.parseMessage=function(e,t){var s,n=new u(t),i={command:e};switch(e){case"version":i.version=n.word32le(),i.services=n.word64le(),i.timestamp=n.word64le(),i.addr_me=n.buffer(26),i.addr_you=n.buffer(26),i.nonce=n.buffer(8),i.subversion=n.varStr(),i.start_height=n.word32le();break;case"inv":case"getdata":for(i.count=n.varInt(),i.invs=[],s=0;ss;s++)i.starts.push(n.buffer(32));i.stop=n.buffer(32);break;case"addr":var p=n.varInt();for(p>1e3&&(p=1e3),i.addrs=[],s=0;p>s;s++)i.addrs.push({time:n.word32le(),services:n.word64le(),ip:n.buffer(16),port:n.word16be()});break;case"alert":i.payload=n.varStr(),i.signature=n.varStr();break;case"ping":this.recvVer>m&&(i.nonce=n.buffer(8));break;case"getaddr":case"verack":case"reject":break;default:return r.err("Connection.parseMessage(): Command not implemented",{cmd:e}),null}return i},module.exports=t}).call(this,require("buffer").Buffer); -},{"../config":"4itQ50","../networks":"ULNIu2","../patches/Buffers.monkey":"kytKTK","../util":193,"../util/BinaryParser":"b3ZSD7","../util/log":"AdF7pF","./Block":"pJEQEB","./SecureRandom":"p4SiC2","./Transaction":"LJhYtm","buffer":97,"bufferput":"aXRuS6","buffers":"OBo3aV","buffertools":"fugeBw","events":"T9Wsc/","socks5-client":174,"util":130}],"ez/meX":[function(require,module,exports){ -exports.intFromCompact=function(r){var t=(r>>>24&255)>>>0,n=(16777215&r)<<8*(t-3)>>>0;return n}; +(function (Buffer){ +var log = require('../util/log'); + +var MAX_RECEIVE_BUFFER = 10000000; +var PROTOCOL_VERSION = 70000; + +var Put = require('bufferput'); +var Buffers = require('buffers'); +require('../patches/Buffers.monkey').patch(Buffers); + +var bitcoreDefaults = require('../config'); +var networks = require('../networks'); +var Block = require('./Block'); +var Transaction = require('./Transaction'); +var util = require('../util'); +var Parser = require('../util/BinaryParser'); +var buffertools = require('buffertools'); +var doubleSha256 = util.twoSha256; +var SecureRandom = require('./SecureRandom'); +var nonce = SecureRandom.getPseudoRandomBuffer(8); +var nodeUtil = require('util'); +var EventEmitter = require('events').EventEmitter; + +var BIP0031_VERSION = 60000; + +function Connection(socket, peer, opts) { + this.config = opts || bitcoreDefaults; + + this.network = networks[this.config.network] || networks.livenet; + this.socket = socket; + this.peer = peer; + + // check for socks5 proxy options and construct a proxied socket + if (this.config.proxy) { + var Socks5Client = require('socks5-client'); + this.socket = new Socks5Client(this.config.proxy.host, this.config.proxy.port); + } + + // A connection is considered "active" once we have received verack + this.active = false; + // The version incoming packages are interpreted as + this.recvVer = 0; + // The version outgoing packages are sent as + this.sendVer = 0; + // The (claimed) height of the remote peer's block chain + this.bestHeight = 0; + // Is this an inbound connection? + this.inbound = !!this.socket.server; + // Have we sent a getaddr on this connection? + this.getaddr = false; + + // Receive buffer + this.buffers = new Buffers(); + + // Starting 20 Feb 2012, Version 0.2 is obsolete + // This is the same behavior as the official client + if (new Date().getTime() > 1329696000000) { + this.recvVer = 209; + this.sendVer = 209; + } + + this.setupHandlers(); +} +nodeUtil.inherits(Connection, EventEmitter); +Connection.prototype.open = function(callback) { + if (typeof callback === 'function') this.once('connect', callback); + this.socket.connect(this.peer.port, this.peer.host); + return this; +}; + +Connection.prototype.setupHandlers = function() { + this.socket.addListener('connect', this.handleConnect.bind(this)); + this.socket.addListener('error', this.handleError.bind(this)); + this.socket.addListener('end', this.handleDisconnect.bind(this)); + this.socket.addListener('data', (function(data) { + var dumpLen = 35; + log.debug('[' + this.peer + '] ' + + 'Recieved ' + data.length + ' bytes of data:'); + log.debug('... ' + buffertools.toHex(data.slice(0, dumpLen > data.length ? + data.length : dumpLen)) + + (data.length > dumpLen ? '...' : '')); + }).bind(this)); + this.socket.addListener('data', this.handleData.bind(this)); +}; + +Connection.prototype.handleConnect = function() { + if (!this.inbound) { + this.sendVersion(); + } + this.emit('connect', { + conn: this, + socket: this.socket, + peer: this.peer + }); +}; + +Connection.prototype.handleError = function(err) { + if (err.errno == 110 || err.errno == 'ETIMEDOUT') { + log.info('connection timed out for ' + this.peer); + } else if (err.errno == 111 || err.errno == 'ECONNREFUSED') { + log.info('connection refused for ' + this.peer); + } else { + log.warn('connection with ' + this.peer + ' ' + err.toString()); + } + this.emit('error', { + conn: this, + socket: this.socket, + peer: this.peer, + err: err + }); +}; + +Connection.prototype.handleDisconnect = function() { + this.emit('disconnect', { + conn: this, + socket: this.socket, + peer: this.peer + }); +}; + +Connection.prototype.handleMessage = function(message) { + if (!message) { + // Parser was unable to make sense of the message, drop it + return; + } + + try { + switch (message.command) { + case 'version': + // Did we connect to ourself? + if (buffertools.compare(nonce, message.nonce) === 0) { + this.socket.end(); + return; + } + + if (this.inbound) { + this.sendVersion(); + } + + if (message.version >= 209) { + this.sendMessage('verack', new Buffer([])); + } + this.sendVer = Math.min(message.version, PROTOCOL_VERSION); + if (message.version < 209) { + this.recvVer = Math.min(message.version, PROTOCOL_VERSION); + } else { + // We won't start expecting a checksum until after we've received + // the 'verack' message. + this.once('verack', (function() { + this.recvVer = message.version; + }).bind(this)); + } + this.bestHeight = message.start_height; + break; + + case 'verack': + this.recvVer = Math.min(message.version, PROTOCOL_VERSION); + this.active = true; + break; + + case 'ping': + if ('object' === typeof message.nonce) { + this.sendPong(message.nonce); + } + break; + } + } catch (e) { + log.err('Error while handling "' + message.command + '" message from ' + + this.peer + ':\n' + + (e.stack ? e.stack : e.toString())); + return; + } + this.emit(message.command, { + conn: this, + socket: this.socket, + peer: this.peer, + message: message + }); +}; + +Connection.prototype.sendPong = function(nonce) { + this.sendMessage('pong', nonce); +}; + +Connection.prototype.sendVersion = function() { + var subversion = '/BitcoinX:0.1/'; + + var put = new Put(); + put.word32le(PROTOCOL_VERSION); // version + put.word64le(1); // services + put.word64le(Math.round(new Date().getTime() / 1000)); // timestamp + put.pad(26); // addr_me + put.pad(26); // addr_you + put.put(nonce); + put.varint(subversion.length); + put.put(new Buffer(subversion, 'ascii')); + put.word32le(0); + + this.sendMessage('version', put.buffer()); +}; + +Connection.prototype.sendGetBlocks = function(starts, stop, wantHeaders) { + // Default value for stop is 0 to get as many blocks as possible (500) + stop = stop || util.NULL_HASH; + + var put = new Put(); + + // https://en.bitcoin.it/wiki/Protocol_specification#getblocks + put.word32le(this.sendVer); + put.varint(starts.length); + + for (var i = 0; i < starts.length; i++) { + if (starts[i].length != 32) { + throw new Error('Invalid hash length'); + } + + put.put(starts[i]); + } + + var stopBuffer = new Buffer(stop, 'binary'); + if (stopBuffer.length != 32) { + throw new Error('Invalid hash length'); + } + + put.put(stopBuffer); + + var command = 'getblocks'; + if (wantHeaders) + command = 'getheaders'; + this.sendMessage(command, put.buffer()); +}; + +Connection.prototype.sendGetHeaders = function(starts, stop) { + this.sendGetBlocks(starts, stop, true); +}; + +Connection.prototype.sendGetData = function(invs) { + var put = new Put(); + put.varint(invs.length); + for (var i = 0; i < invs.length; i++) { + put.word32le(invs[i].type); + put.put(invs[i].hash); + } + this.sendMessage('getdata', put.buffer()); +}; + +Connection.prototype.sendGetAddr = function(invs) { + var put = new Put(); + this.sendMessage('getaddr', put.buffer()); +}; + +Connection.prototype.sendInv = function(data) { + if (!Array.isArray(data)) data = [data]; + var put = new Put(); + put.varint(data.length); + data.forEach(function(value) { + if (value instanceof Block) { + // Block + put.word32le(2); // MSG_BLOCK + } else { + // Transaction + put.word32le(1); // MSG_TX + } + put.put(value.getHash()); + }); + this.sendMessage('inv', put.buffer()); +}; + +Connection.prototype.sendHeaders = function(headers) { + var put = new Put(); + put.varint(headers.length); + headers.forEach(function(header) { + put.put(header); + + // Indicate 0 transactions + put.word8(0); + }); + this.sendMessage('headers', put.buffer()); +}; + +Connection.prototype.sendTx = function(tx) { + this.sendMessage('tx', tx.serialize()); +}; + +Connection.prototype.sendBlock = function(block, txs) { + var put = new Put(); + + // Block header + put.put(block.getHeader()); + + // List of transactions + put.varint(txs.length); + txs.forEach(function(tx) { + put.put(tx.serialize()); + }); + + this.sendMessage('block', put.buffer()); +}; + +Connection.prototype.sendMessage = function(command, payload) { + try { + var magic = this.network.magic; + var commandBuf = new Buffer(command, 'ascii'); + if (commandBuf.length > 12) throw 'Command name too long'; + + var checksum; + if (this.sendVer >= 209) { + checksum = doubleSha256(payload).slice(0, 4); + } else { + checksum = new Buffer([]); + } + + var message = new Put(); // -- HEADER -- + message.put(magic); // magic bytes + message.put(commandBuf); // command name + message.pad(12 - commandBuf.length); // zero-padded + message.word32le(payload.length); // payload length + message.put(checksum); // checksum + // -- BODY -- + message.put(payload); // payload data + + var buffer = message.buffer(); + + log.debug('[' + this.peer + '] ' + + 'Sending message ' + command + ' (' + payload.length + ' bytes)'); + + this.socket.write(buffer); + } catch (err) { + // TODO: We should catch this error one level higher in order to better + // determine how to react to it. For now though, ignoring it will do. + log.err('Error while sending message to peer ' + this.peer + ': ' + + (err.stack ? err.stack : err.toString())); + } +}; + +Connection.prototype.handleData = function(data) { + this.buffers.push(data); + + if (this.buffers.length > MAX_RECEIVE_BUFFER) { + log.err('Peer ' + this.peer + ' exceeded maxreceivebuffer, disconnecting.' + + (err.stack ? err.stack : err.toString())); + this.socket.destroy(); + return; + } + + this.processData(); +}; + +Connection.prototype.processData = function() { + // If there are less than 20 bytes there can't be a message yet. + if (this.buffers.length < 20) return; + + var magic = this.network.magic; + var i = 0; + for (;;) { + if (this.buffers.get(i) === magic[0] && + this.buffers.get(i + 1) === magic[1] && + this.buffers.get(i + 2) === magic[2] && + this.buffers.get(i + 3) === magic[3]) { + if (i !== 0) { + log.debug('[' + this.peer + '] ' + + 'Received ' + i + + ' bytes of inter-message garbage: '); + log.debug('... ' + this.buffers.slice(0, i)); + + this.buffers.skip(i); + } + break; + } + + if (i > (this.buffers.length - 4)) { + this.buffers.skip(i); + return; + } + i++; + } + + var payloadLen = (this.buffers.get(16)) + + (this.buffers.get(17) << 8) + + (this.buffers.get(18) << 16) + + (this.buffers.get(19) << 24); + + var startPos = (this.recvVer >= 209) ? 24 : 20; + var endPos = startPos + payloadLen; + + if (this.buffers.length < endPos) return; + + var command = this.buffers.slice(4, 16).toString('ascii').replace(/\0+$/, ''); + var payload = this.buffers.slice(startPos, endPos); + var checksum = (this.recvVer >= 209) ? this.buffers.slice(20, 24) : null; + + log.debug('[' + this.peer + '] ' + + 'Received message ' + command + + ' (' + payloadLen + ' bytes)'); + + if (checksum !== null) { + var checksumConfirm = doubleSha256(payload).slice(0, 4); + if (buffertools.compare(checksumConfirm, checksum) !== 0) { + log.err('[' + this.peer + '] ' + + 'Checksum failed', { + cmd: command, + expected: checksumConfirm.toString('hex'), + actual: checksum.toString('hex') + }); + return; + } + } + + var message; + try { + message = this.parseMessage(command, payload); + } catch (e) { + log.err('Error while parsing message ' + command + ' from ' + + this.peer + ':\n' + + (e.stack ? e.stack : e.toString())); + } + + if (message) { + this.handleMessage(message); + } + + this.buffers.skip(endPos); + this.processData(); +}; + +Connection.prototype.parseMessage = function(command, payload) { + var parser = new Parser(payload); + + var data = { + command: command + }; + + var i; + + switch (command) { + case 'version': // https://en.bitcoin.it/wiki/Protocol_specification#version + data.version = parser.word32le(); + data.services = parser.word64le(); + data.timestamp = parser.word64le(); + data.addr_me = parser.buffer(26); + data.addr_you = parser.buffer(26); + data.nonce = parser.buffer(8); + data.subversion = parser.varStr(); + data.start_height = parser.word32le(); + break; + + case 'inv': + case 'getdata': + data.count = parser.varInt(); + + data.invs = []; + for (i = 0; i < data.count; i++) { + data.invs.push({ + type: parser.word32le(), + hash: parser.buffer(32) + }); + } + break; + + case 'headers': + data.count = parser.varInt(); + + data.headers = []; + for (i = 0; i < data.count; i++) { + var header = new Block(); + header.parse(parser); + data.headers.push(header); + } + break; + + case 'block': + var block = new Block(); + block.parse(parser); + + data.block = block; + data.version = block.version; + data.prev_hash = block.prev_hash; + data.merkle_root = block.merkle_root; + data.timestamp = block.timestamp; + data.bits = block.bits; + data.nonce = block.nonce; + + data.txs = block.txs; + + data.size = payload.length; + break; + + case 'tx': + var tx = new Transaction(); + tx.parse(parser); + return { + command: command, + version: tx.version, + lock_time: tx.lock_time, + ins: tx.ins, + outs: tx.outs, + tx: tx, + }; + + case 'getblocks': + case 'getheaders': + // parse out the version + data.version = parser.word32le(); + + // TODO: Limit block locator size? + // reference implementation limits to 500 results + var startCount = parser.varInt(); + + data.starts = []; + for (i = 0; i < startCount; i++) { + data.starts.push(parser.buffer(32)); + } + data.stop = parser.buffer(32); + break; + + case 'addr': + var addrCount = parser.varInt(); + + // Enforce a maximum number of addresses per message + if (addrCount > 1000) { + addrCount = 1000; + } + + data.addrs = []; + for (i = 0; i < addrCount; i++) { + // TODO: Time actually depends on the version of the other peer (>=31402) + data.addrs.push({ + time: parser.word32le(), + services: parser.word64le(), + ip: parser.buffer(16), + port: parser.word16be() + }); + } + break; + + case 'alert': + data.payload = parser.varStr(); + data.signature = parser.varStr(); + break; + + case 'ping': + if (this.recvVer > BIP0031_VERSION) { + data.nonce = parser.buffer(8); + } + break; + + case 'getaddr': + case 'verack': + case 'reject': + // Empty message, nothing to parse + break; + + default: + log.err('Connection.parseMessage(): Command not implemented', { + cmd: command + }); + + // This tells the calling function not to issue an event + return null; + } + + return data; +}; + +module.exports = Connection; + +}).call(this,require("buffer").Buffer) +},{"../config":"4itQ50","../networks":"ULNIu2","../patches/Buffers.monkey":"kytKTK","../util":206,"../util/BinaryParser":"b3ZSD7","../util/log":"AdF7pF","./Block":"pJEQEB","./SecureRandom":"p4SiC2","./Transaction":"LJhYtm","buffer":108,"bufferput":"aXRuS6","buffers":"OBo3aV","buffertools":"fugeBw","events":"T9Wsc/","socks5-client":187,"util":141}],"ez/meX":[function(require,module,exports){ +exports.intFromCompact = function(c) { + var bytes = ((c >>> 24) & 0xff) >>> 0; + var v = ((c & 0xffffff) << (8 * (bytes - 3))) >>> 0; + return v; +} + },{}],"./lib/Deserialize":[function(require,module,exports){ module.exports=require('ez/meX'); },{}],"./lib/Electrum":[function(require,module,exports){ module.exports=require('hdzBvq'); },{}],"hdzBvq":[function(require,module,exports){ -(function(e){function r(r){this.mpk=new e(r,"hex")}var t=require("./Key"),u=require("./Point"),n=require("../util").twoSha256,i=(require("buffertools"),require("bignum"));r.prototype.getSequence=function(r,t){var u=r?1:0,o=e.concat([new e(t+":"+u+":","utf8"),this.mpk]);return i.fromBuffer(n(o))},r.prototype.generatePubKey=function(r,n){var o=i.fromBuffer(this.mpk.slice(0,32),{size:32}),f=i.fromBuffer(this.mpk.slice(32,64),{size:32}),c=new u(o,f),p=this.getSequence(n,r),s=new t;s.private=p.toBuffer(),s.regenerateSync(),s.compressed=!1;var a=u.fromUncompressedPubKey(s.public);pt=u.add(c,a);var m=pt.x.toBuffer({size:32}),b=pt.y.toBuffer({size:32}),h=new e([4]),l=new t;return l.compressed=!1,l.public=e.concat([h,m,b]),l.public},r.prototype.generateChangePubKey=function(e){return this.generatePubKey(e,!0)},module.exports=r}).call(this,require("buffer").Buffer); -},{"../util":193,"./Key":"ALJ4PS","./Point":"6tXgqr","bignum":63,"buffer":97,"buffertools":"fugeBw"}],"x1O6JW":[function(require,module,exports){ -(function(e){function i(e,i){if(e.lengthn;n++)t*=256,t+=e[n];return t}function t(e){return i(e,1)}function n(e){return i(e,4)}var r=require("./Base58").base58,s=require("../util"),h=require("./Key"),a=require("./Point"),c=require("./SecureRandom"),o=require("bignum"),d=require("../networks"),l=new o("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141",16),u=(new o("79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798",16),function(i){if("undefined"==typeof i||"mainnet"==i||"livenet"==i?(i="livenet",this.version=d.livenet.hkeyPrivateVersion):"testnet"==i&&(this.version=d.testnet.hkeyPrivateVersion),"livenet"==i||"testnet"==i)return this.depth=0,this.parentFingerprint=new e([0,0,0,0]),this.childIndex=new e([0,0,0,0]),this.chainCode=c.getRandomBuffer(32),this.eckey=h.generateSync(),this.hasPrivateKey=!0,this.pubKeyHash=s.sha256ripe160(this.eckey.public),this.buildExtendedPublicKey(),void this.buildExtendedPrivateKey();if("string"==typeof i){var t=r.decode(i);if(82!=t.length)throw new Error("Not enough data, expected 82 and received "+t.length);var n=t.slice(78,82);i=t.slice(0,78);var a=s.sha256(s.sha256(i));if(a[0]!=n[0]||a[1]!=n[1]||a[2]!=n[2]||a[3]!=n[3])throw new Error("Invalid checksum")}void 0!==i&&null!==i&&this.initFromBytes(i)});u.seed=function(i,t){if(t||(t="livenet"),e.isBuffer(i)||(i=new e(i,"hex")),i.length<16)return!1;if(i.length>64)return!1;var n=s.sha512hmac(i,new e("Bitcoin seed")),r=new u(null);return r.depth=0,r.parentFingerprint=new e([0,0,0,0]),r.childIndex=new e([0,0,0,0]),r.chainCode=n.slice(32,64),r.version=d[t].hkeyPrivateVersion,r.eckey=new h,r.eckey.private=n.slice(0,32),r.eckey.regenerateSync(),r.hasPrivateKey=!0,r.pubKeyHash=s.sha256ripe160(r.eckey.public),r.buildExtendedPublicKey(),r.buildExtendedPrivateKey(),r},u.prototype.initFromBytes=function(e){if(78!=e.length)throw new Error("not enough data");this.version=n(e.slice(0,4)),this.depth=t(e.slice(4,5)),this.parentFingerprint=e.slice(5,9),this.childIndex=n(e.slice(9,13)),this.chainCode=e.slice(13,45);var i=e.slice(45,78),r=this.version==d.livenet.hkeyPrivateVersion||this.version==d.testnet.hkeyPrivateVersion,a=this.version==d.livenet.hkeyPublicVersion||this.version==d.testnet.hkeyPublicVersion;if(r&&0==i[0])this.eckey=new h,this.eckey.private=i.slice(1,33),this.eckey.compressed=!0,this.eckey.regenerateSync(),this.pubKeyHash=s.sha256ripe160(this.eckey.public),this.hasPrivateKey=!0;else{if(!a||2!=i[0]&&3!=i[0])throw new Error("Invalid key");this.eckey=new h,this.eckey.public=i,this.pubKeyHash=s.sha256ripe160(this.eckey.public),this.hasPrivateKey=!1}this.buildExtendedPublicKey(),this.buildExtendedPrivateKey()},u.prototype.buildExtendedPublicKey=function(){this.extendedPublicKey=new e([]);var i=null;switch(this.version){case d.livenet.hkeyPublicVersion:case d.livenet.hkeyPrivateVersion:i=d.livenet.hkeyPublicVersion;break;case d.testnet.hkeyPublicVersion:case d.testnet.hkeyPrivateVersion:i=d.testnet.hkeyPublicVersion;break;default:throw new Error("Unknown version")}this.extendedPublicKey=e.concat([new e([i>>24]),new e([i>>16&255]),new e([i>>8&255]),new e([255&i]),new e([this.depth]),this.parentFingerprint,new e([this.childIndex>>>24]),new e([this.childIndex>>>16&255]),new e([this.childIndex>>>8&255]),new e([255&this.childIndex]),this.chainCode,this.eckey.public])},u.prototype.extendedPublicKeyString=function(i){if(void 0===i||"base58"===i){var t=s.sha256(s.sha256(this.extendedPublicKey)),n=t.slice(0,4),h=e.concat([this.extendedPublicKey,n]);return r.encode(h)}if("hex"===i)return this.extendedPublicKey.toString("hex");throw new Error("bad format")},u.prototype.buildExtendedPrivateKey=function(){if(this.hasPrivateKey){this.extendedPrivateKey=new e([]);var i=this.version;this.extendedPrivateKey=e.concat([new e([i>>24]),new e([i>>16&255]),new e([i>>8&255]),new e([255&i]),new e([this.depth]),this.parentFingerprint,new e([this.childIndex>>>24]),new e([this.childIndex>>>16&255]),new e([this.childIndex>>>8&255]),new e([255&this.childIndex]),this.chainCode,new e([0]),this.eckey.private])}},u.prototype.extendedPrivateKeyString=function(i){if(void 0===i||"base58"===i){var t=s.sha256(s.sha256(this.extendedPrivateKey)),n=t.slice(0,4),h=e.concat([this.extendedPrivateKey,n]);return r.encode(h)}if("hex"===i)return this.extendedPrivateKey.toString("hex");throw new Error("bad format")},u.prototype.derive=function(e){var i=e.split("/");if("m"==e||"M"==e||"m'"==e||"M'"==e)return this;var t=this;for(var n in i){var r=i[n];if(0!=n){var s=r.length>1&&"'"==r[r.length-1],h=2147483647&parseInt(s?r.slice(0,r.length-1):r);s&&(h+=2147483648),t=t.deriveChild(h)}else if("m"!=r)throw new Error("invalid path")}return t},u.prototype.deriveChild=function(i){var t=[];t.push(i>>24&255),t.push(i>>16&255),t.push(i>>8&255),t.push(255&i),t=new e(t);var n=0!=(2147483648&i),r=this.version==d.livenet.hkeyPrivateVersion||this.version==d.testnet.hkeyPrivateVersion;if(n&&(!this.hasPrivateKey||!r))throw new Error("Cannot do private key derivation without private key");var c=null;if(this.hasPrivateKey){var y=null;y=e.concat(n?[new e([0]),this.eckey.private,t]:[this.eckey.public,t]);var v=s.sha512hmac(y,this.chainCode),p=o.fromBuffer(v.slice(0,32),{size:32}),w=v.slice(32,64),b=o.fromBuffer(this.eckey.private,{size:32}),f=p.add(b).mod(l);c=new u(null),c.chainCode=w,c.eckey=new h,c.eckey.private=f.toBuffer({size:32}),c.eckey.regenerateSync(),c.hasPrivateKey=!0}else{var y=e.concat([this.eckey.public,t]),v=s.sha512hmac(y,this.chainCode),p=v.slice(0,32),w=v.slice(32,64),k=new h;k.private=p,k.regenerateSync(),k.compressed=!1;var P=a.fromUncompressedPubKey(k.public),F=new h;F.public=this.eckey.public,F.compressed=!1;var x=a.fromUncompressedPubKey(F.public),K=a.add(P,x).toUncompressedPubKey();c=new u(null),c.chainCode=new e(w);var g=new h;g.public=K,g.compressed=!0,c.eckey=g,c.hasPrivateKey=!1}return c.childIndex=i,c.parentFingerprint=this.pubKeyHash.slice(0,4),c.version=this.version,c.depth=this.depth+1,c.eckey.compressed=!0,c.pubKeyHash=s.sha256ripe160(c.eckey.public),c.buildExtendedPublicKey(),c.buildExtendedPrivateKey(),c},module.exports=u}).call(this,require("buffer").Buffer); -},{"../networks":"ULNIu2","../util":193,"./Base58":"6VqyzY","./Key":"ALJ4PS","./Point":"6tXgqr","./SecureRandom":"p4SiC2","bignum":63,"buffer":97}],"./lib/HierarchicalKey":[function(require,module,exports){ +(function (Buffer){ +var Key = require('./Key'), + Point = require('./Point'), + twoSha256 = require('../util').twoSha256, + buffertools = require('buffertools'), + bignum = require('bignum'); + +/** + * Pre-BIP32 Electrum public key derivation (electrum <2.0) + * + * For now, this class can only understands master public keys. + * It doesn't support derivation from a private master key (TODO). + * + * @example examples/ElectrumMPK.js + */ +function Electrum(master_public_key) { + this.mpk = new Buffer(master_public_key, 'hex'); +} + +Electrum.prototype.getSequence = function(for_change, n) { + var mode = for_change ? 1 : 0; + var buf = Buffer.concat([new Buffer(n + ':' + mode + ':', 'utf8'), this.mpk]); + return bignum.fromBuffer(twoSha256(buf)); +}; + +Electrum.prototype.generatePubKey = function(n, for_change) { + var x = bignum.fromBuffer(this.mpk.slice(0, 32), { + size: 32 + }); + var y = bignum.fromBuffer(this.mpk.slice(32, 64), { + size: 32 + }); + var mpk_pt = new Point(x, y); + + var sequence = this.getSequence(for_change, n); + var sequence_key = new Key(); + sequence_key.private = sequence.toBuffer(); + sequence_key.regenerateSync(); + sequence_key.compressed = false; + + var sequence_pt = Point.fromUncompressedPubKey(sequence_key.public); + + pt = Point.add(mpk_pt, sequence_pt); + + var xbuf = pt.x.toBuffer({ + size: 32 + }); + var ybuf = pt.y.toBuffer({ + size: 32 + }); + var prefix = new Buffer([0x04]); + + var key = new Key(); + key.compressed = false; + key.public = Buffer.concat([prefix, xbuf, ybuf]); + + return key.public; +}; + +Electrum.prototype.generateChangePubKey = function(sequence) { + return this.generatePubKey(sequence, true); +}; + +module.exports = Electrum; + +}).call(this,require("buffer").Buffer) +},{"../util":206,"./Key":"ALJ4PS","./Point":"6tXgqr","bignum":63,"buffer":108,"buffertools":"fugeBw"}],"./lib/HierarchicalKey":[function(require,module,exports){ module.exports=require('x1O6JW'); -},{}],"CBDCgz":[function(require,module,exports){ -(function(e){"use strict";var r=require("../util"),i=require("./Key"),n=require("bignum"),r=require("../util"),t=function(){};t.sign=function(e,r){var i=t.magicHash(e),n=r.signSync(i);return n},t.verifyWithPubKey=function(e,r,n){var u=t.magicHash(r),a=new i;return 65==e.length&&(a.compressed=!1),a.public=e,a.verifySignatureSync(u,n)},t.signMessage=function(e,r){var u=t.magicHash(e),a=n.fromBuffer(r.private),s=i.signCompressed(u,a);return s},t.verifyMessage=function(e,r,n){var u=t.magicHash(r);return i.verifyCompressed(u,n,e)},t.magicBytes=new e("Bitcoin Signed Message:\n"),t.magicHash=function(i){var n=t.magicBytes,u=r.varIntBuf(n.length),a=new e(i),s=r.varIntBuf(a.length),c=e.concat([u,n,s,a]),f=r.twoSha256(c);return f},module.exports=t}).call(this,require("buffer").Buffer); -},{"../util":193,"./Key":"ALJ4PS","bignum":63,"buffer":97}],"./lib/Message":[function(require,module,exports){ +},{}],"x1O6JW":[function(require,module,exports){ +(function (Buffer){ +var base58 = require('./Base58').base58; +var coinUtil = require('../util'); +var Key = require('./Key'); +var Point = require('./Point'); +var SecureRandom = require('./SecureRandom'); +var bignum = require('bignum'); +var networks = require('../networks'); + +var secp256k1_n = new bignum('FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141', 16); +var secp256k1_Gx = new bignum('79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798', 16); + +/* +random new HierarchicalKey: new HierarchicalKey(); +from extended public or private key: new HierarchicalKey(str); +new blank HierarchicalKey: new HierarchicalKey(null); +*/ +var HierarchicalKey = function(bytes) { + if (typeof bytes == 'undefined' || bytes == 'mainnet' || bytes == 'livenet') { + bytes = 'livenet'; + this.version = networks['livenet'].hkeyPrivateVersion; + } else if (bytes == 'testnet') { + this.version = networks['testnet'].hkeyPrivateVersion; + } + if (bytes == 'livenet' || bytes == 'testnet') { + this.depth = 0x00; + this.parentFingerprint = new Buffer([0, 0, 0, 0]); + this.childIndex = new Buffer([0, 0, 0, 0]); + this.chainCode = SecureRandom.getRandomBuffer(32); + this.eckey = Key.generateSync(); + this.hasPrivateKey = true; + this.pubKeyHash = coinUtil.sha256ripe160(this.eckey.public); + this.buildExtendedPublicKey(); + this.buildExtendedPrivateKey(); + return; + } + + // decode base58 + if (typeof bytes === 'string') { + var decoded = base58.decode(bytes); + if (decoded.length != 82) + throw new Error('Not enough data, expected 82 and received ' + decoded.length); + var checksum = decoded.slice(78, 82); + bytes = decoded.slice(0, 78); + + var hash = coinUtil.sha256(coinUtil.sha256(bytes)); + + if (hash[0] != checksum[0] || hash[1] != checksum[1] || hash[2] != checksum[2] || hash[3] != checksum[3]) { + throw new Error('Invalid checksum'); + } + } + + if (bytes !== undefined && bytes !== null) + this.initFromBytes(bytes); +} + +HierarchicalKey.seed = function(bytes, network) { + if (!network) + network = 'livenet'; + + if (!Buffer.isBuffer(bytes)) + bytes = new Buffer(bytes, 'hex'); //if not buffer, assume hex + if (bytes.length < 128 / 8) + return false; //need more entropy + if (bytes.length > 512 / 8) + return false; + var hash = coinUtil.sha512hmac(bytes, new Buffer('Bitcoin seed')); + + var hkey = new HierarchicalKey(null); + hkey.depth = 0x00; + hkey.parentFingerprint = new Buffer([0, 0, 0, 0]); + hkey.childIndex = new Buffer([0, 0, 0, 0]); + hkey.chainCode = hash.slice(32, 64); + hkey.version = networks[network].hkeyPrivateVersion; + hkey.eckey = new Key(); + hkey.eckey.private = hash.slice(0, 32); + hkey.eckey.regenerateSync(); + hkey.hasPrivateKey = true; + hkey.pubKeyHash = coinUtil.sha256ripe160(hkey.eckey.public); + + hkey.buildExtendedPublicKey(); + hkey.buildExtendedPrivateKey(); + + return hkey; +}; + +HierarchicalKey.prototype.initFromBytes = function(bytes) { + // Both pub and private extended keys are 78 bytes + if (bytes.length != 78) throw new Error('not enough data'); + + this.version = u32(bytes.slice(0, 4)); + this.depth = u8(bytes.slice(4, 5)); + this.parentFingerprint = bytes.slice(5, 9); + this.childIndex = u32(bytes.slice(9, 13)); + this.chainCode = bytes.slice(13, 45); + + var keyBytes = bytes.slice(45, 78); + + var isPrivate = + (this.version == networks['livenet'].hkeyPrivateVersion || + this.version == networks['testnet'].hkeyPrivateVersion); + + var isPublic = + (this.version == networks['livenet'].hkeyPublicVersion || + this.version == networks['testnet'].hkeyPublicVersion); + + if (isPrivate && keyBytes[0] == 0) { + this.eckey = new Key(); + this.eckey.private = keyBytes.slice(1, 33); + this.eckey.compressed = true; + this.eckey.regenerateSync(); + this.pubKeyHash = coinUtil.sha256ripe160(this.eckey.public); + this.hasPrivateKey = true; + } else if (isPublic && (keyBytes[0] == 0x02 || keyBytes[0] == 0x03)) { + this.eckey = new Key(); + this.eckey.public = keyBytes; + this.pubKeyHash = coinUtil.sha256ripe160(this.eckey.public); + this.hasPrivateKey = false; + } else { + throw new Error('Invalid key'); + } + + this.buildExtendedPublicKey(); + this.buildExtendedPrivateKey(); +} + +HierarchicalKey.prototype.buildExtendedPublicKey = function() { + this.extendedPublicKey = new Buffer([]); + + var v = null; + switch (this.version) { + case networks['livenet'].hkeyPublicVersion: + case networks['livenet'].hkeyPrivateVersion: + v = networks['livenet'].hkeyPublicVersion; + break; + case networks['testnet'].hkeyPublicVersion: + case networks['testnet'].hkeyPrivateVersion: + v = networks['testnet'].hkeyPublicVersion; + break; + default: + throw new Error('Unknown version'); + } + + // Version + this.extendedPublicKey = Buffer.concat([ + new Buffer([v >> 24]), + new Buffer([(v >> 16) & 0xff]), + new Buffer([(v >> 8) & 0xff]), + new Buffer([v & 0xff]), + new Buffer([this.depth]), + this.parentFingerprint, + new Buffer([this.childIndex >>> 24]), + new Buffer([(this.childIndex >>> 16) & 0xff]), + new Buffer([(this.childIndex >>> 8) & 0xff]), + new Buffer([this.childIndex & 0xff]), + this.chainCode, + this.eckey.public + ]); +} + +HierarchicalKey.prototype.extendedPublicKeyString = function(format) { + if (format === undefined || format === 'base58') { + var hash = coinUtil.sha256(coinUtil.sha256(this.extendedPublicKey)); + var checksum = hash.slice(0, 4); + var data = Buffer.concat([this.extendedPublicKey, checksum]); + return base58.encode(data); + } else if (format === 'hex') { + return this.extendedPublicKey.toString('hex');; + } else { + throw new Error('bad format'); + } +} + +HierarchicalKey.prototype.buildExtendedPrivateKey = function() { + if (!this.hasPrivateKey) return; + this.extendedPrivateKey = new Buffer([]); + + var v = this.version; + + this.extendedPrivateKey = Buffer.concat([ + new Buffer([v >> 24]), + new Buffer([(v >> 16) & 0xff]), + new Buffer([(v >> 8) & 0xff]), + new Buffer([v & 0xff]), + new Buffer([this.depth]), + this.parentFingerprint, + new Buffer([this.childIndex >>> 24]), + new Buffer([(this.childIndex >>> 16) & 0xff]), + new Buffer([(this.childIndex >>> 8) & 0xff]), + new Buffer([this.childIndex & 0xff]), + this.chainCode, + new Buffer([0]), + this.eckey.private + ]); +} + +HierarchicalKey.prototype.extendedPrivateKeyString = function(format) { + if (format === undefined || format === 'base58') { + var hash = coinUtil.sha256(coinUtil.sha256(this.extendedPrivateKey)); + var checksum = hash.slice(0, 4); + var data = Buffer.concat([this.extendedPrivateKey, checksum]); + return base58.encode(data); + } else if (format === 'hex') { + return this.extendedPrivateKey.toString('hex'); + } else { + throw new Error('bad format'); + } +} + + +HierarchicalKey.prototype.derive = function(path) { + var e = path.split('/'); + + // Special cases: + if (path == 'm' || path == 'M' || path == 'm\'' || path == 'M\'') + return this; + + var hkey = this; + for (var i in e) { + var c = e[i]; + + if (i == 0) { + if (c != 'm') throw new Error('invalid path'); + continue; + } + + var usePrivate = (c.length > 1) && (c[c.length - 1] == '\''); + var childIndex = parseInt(usePrivate ? c.slice(0, c.length - 1) : c) & 0x7fffffff; + + if (usePrivate) + childIndex += 0x80000000; + + hkey = hkey.deriveChild(childIndex); + } + + return hkey; +} + +HierarchicalKey.prototype.deriveChild = function(i) { + var ib = []; + ib.push((i >> 24) & 0xff); + ib.push((i >> 16) & 0xff); + ib.push((i >> 8) & 0xff); + ib.push(i & 0xff); + ib = new Buffer(ib); + + var usePrivate = (i & 0x80000000) != 0; + + var isPrivate = + (this.version == networks['livenet'].hkeyPrivateVersion || + this.version == networks['testnet'].hkeyPrivateVersion); + + if (usePrivate && (!this.hasPrivateKey || !isPrivate)) + throw new Error('Cannot do private key derivation without private key'); + + var ret = null; + if (this.hasPrivateKey) { + var data = null; + + if (usePrivate) { + data = Buffer.concat([new Buffer([0]), this.eckey.private, ib]); + } else { + data = Buffer.concat([this.eckey.public, ib]); + } + + var hash = coinUtil.sha512hmac(data, this.chainCode); + var il = bignum.fromBuffer(hash.slice(0, 32), { + size: 32 + }); + var ir = hash.slice(32, 64); + + // ki = IL + kpar (mod n). + var priv = bignum.fromBuffer(this.eckey.private, { + size: 32 + }); + var k = il.add(priv).mod(secp256k1_n); + + ret = new HierarchicalKey(null); + ret.chainCode = ir; + + ret.eckey = new Key(); + ret.eckey.private = k.toBuffer({ + size: 32 + }); + ret.eckey.regenerateSync(); + ret.hasPrivateKey = true; + + } else { + var data = Buffer.concat([this.eckey.public, ib]); + var hash = coinUtil.sha512hmac(data, this.chainCode); + var il = hash.slice(0, 32); + var ir = hash.slice(32, 64); + + // Ki = (IL + kpar)*G = IL*G + Kpar + var ilGkey = new Key(); + ilGkey.private = il; + ilGkey.regenerateSync(); + ilGkey.compressed = false; + var ilG = Point.fromUncompressedPubKey(ilGkey.public); + var oldkey = new Key(); + oldkey.public = this.eckey.public; + oldkey.compressed = false; + var Kpar = Point.fromUncompressedPubKey(oldkey.public); + var newpub = Point.add(ilG, Kpar).toUncompressedPubKey(); + + ret = new HierarchicalKey(null); + ret.chainCode = new Buffer(ir); + + var eckey = new Key(); + eckey.public = newpub; + eckey.compressed = true; + ret.eckey = eckey; + ret.hasPrivateKey = false; + } + + ret.childIndex = i; + ret.parentFingerprint = this.pubKeyHash.slice(0, 4); + ret.version = this.version; + ret.depth = this.depth + 1; + + ret.eckey.compressed = true; + ret.pubKeyHash = coinUtil.sha256ripe160(ret.eckey.public); + + ret.buildExtendedPublicKey(); + ret.buildExtendedPrivateKey(); + + return ret; +} + + +function uint(f, size) { + if (f.length < size) + throw new Error('not enough data'); + var n = 0; + for (var i = 0; i < size; i++) { + n *= 256; + n += f[i]; + } + return n; +} + +function u8(f) { + return uint(f, 1); +} + +function u16(f) { + return uint(f, 2); +} + +function u32(f) { + return uint(f, 4); +} + +function u64(f) { + return uint(f, 8); +} + +module.exports = HierarchicalKey; + +}).call(this,require("buffer").Buffer) +},{"../networks":"ULNIu2","../util":206,"./Base58":"6VqyzY","./Key":"ALJ4PS","./Point":"6tXgqr","./SecureRandom":"p4SiC2","bignum":63,"buffer":108}],"CBDCgz":[function(require,module,exports){ +(function (Buffer){ +'use strict'; +var coinUtil = require('../util'); +var Key = require('./Key'); +var bignum = require('bignum'); +var coinUtil = require('../util'); + +var Message = function() {}; + +//creates DER format signatures. +//probably not what you want. +Message.sign = function(str, key) { + var hash = Message.magicHash(str); + var sig = key.signSync(hash); + return sig; +}; + +//verifies compressed signatures +Message.verifyWithPubKey = function(pubkey, message, sig) { + var hash = Message.magicHash(message); + var key = new Key(); + if (pubkey.length == 65) + key.compressed = false; + key.public = pubkey; + + return key.verifySignatureSync(hash, sig); +}; + +//creates compressed format signatures. +//you probably want this, not .sign +Message.signMessage = function(str, key) { + var hash = Message.magicHash(str); + var privnum = bignum.fromBuffer(key.private); + var sig = Key.signCompressed(hash, privnum); + return sig; +}; + +//verifies compressed signatures +Message.verifyMessage = function(pubkeyhash, message, sig) { + var hash = Message.magicHash(message); + + return Key.verifyCompressed(hash, sig, pubkeyhash); +}; + +//TODO: Message.verify ... with address, not pubkey + +Message.magicBytes = new Buffer('Bitcoin Signed Message:\n'); + +Message.magicHash = function(str) { + var magicBytes = Message.magicBytes; + var prefix1 = coinUtil.varIntBuf(magicBytes.length); + var message = new Buffer(str); + var prefix2 = coinUtil.varIntBuf(message.length); + + var buf = Buffer.concat([prefix1, magicBytes, prefix2, message]); + + var hash = coinUtil.twoSha256(buf); + + return hash; +}; + +module.exports = Message; + +}).call(this,require("buffer").Buffer) +},{"../util":206,"./Key":"ALJ4PS","bignum":63,"buffer":108}],"./lib/Message":[function(require,module,exports){ module.exports=require('CBDCgz'); -},{}],"qYkfjX":[function(require,module,exports){ -var log=require("../util/log"),networks=require("../networks"),Address=require("./Address"),Peer=require("./Peer"),PeerManager=require("./PeerManager"),util=require("util"),EventEmitter=require("events").EventEmitter,preconditions=require("preconditions").singleton(),NetworkMonitor=function(e){preconditions.checkArgument(e),this.peerman=e,this.networkName=e.config.network,this.init()};util.inherits(NetworkMonitor,EventEmitter),NetworkMonitor.create=function(e){this.config=e;var t=new PeerManager({network:e.networkName});return t.addPeer(new Peer(e.host,e.port)),new NetworkMonitor(t)},NetworkMonitor.prototype.init=function(){var e=this,t=function(e){var t=e.message.invs;e.conn.sendGetData(t)},n=function(t){e.emit("block",t.message)},o=function(t){var n=t.message.tx;e.emit("tx",n);for(var o=n.getSendingAddresses(e.networkName),r=0;r=31402||this.peers.length<1e3)&&(e.conn.sendGetAddr(),e.conn.getaddr=!0)},PeerManager.prototype.handleReady=function(e){log.info("connected to "+e.conn.peer.host+":"+e.conn.peer.port),this.emit("connect",{pm:this,conn:e.conn,socket:e.socket,peer:e.peer}),0==this.isConnected&&(this.emit("netConnected",e),this.isConnected=!0)},PeerManager.prototype.handleAddr=function(e){if(this.peerDiscovery){var n=GetAdjustedTime();e.message.addrs.forEach(function(e){try{(e.time<=1e8||e.time>n+600)&&(e.time=n-432e3);var t=new Peer(e.ip,e.port,e.services);t.lastSeen=e.time,this.peers.push(t)}catch(r){log.warn("Invalid addr received: "+r.message)}}.bind(this)),e.message.addrs.length<1e3&&(e.conn.getaddr=!1)}},PeerManager.prototype.handleGetAddr=function(){},PeerManager.prototype.handleError=function(e){log.err("unkown error with peer "+e.peer+" (disconnecting): "+e.err),this.handleDisconnect.apply(this,[].slice.call(arguments))},PeerManager.prototype.handleDisconnect=function(e){log.info("disconnected from peer "+e.peer);var n=this.connections.indexOf(e.conn);-1!=n&&this.connections.splice(n,1),this.removePeer(e.peer),this.pool.length&&(log.info("replacing peer using the pool of "+this.pool.length+" seeds"),this.addPeer(this.pool.pop())),this.connections.length||(this.emit("netDisconnected"),this.isConnected=!1)},PeerManager.prototype.getActiveConnection=function(){var e=this.connections.filter(function(e){return e.active});if(e.length){var n=Math.floor(Math.random()*e.length),t=e[n];return t.socket.writable?t:(e.splice(n,1),this.getActiveConnection())}return null},PeerManager.prototype.getActiveConnections=function(){return this.connections.slice(0)},PeerManager.prototype.discover=function(e,n){var t=this,r=networks[t.config.network].dnsSeeds;t.limit=e.limit||12;var i=r.map(function(e){return function(n){return~t.seeds.resolved.indexOf(e)?n(null,t.seeds.results[e]):~t.seeds.failed.indexOf(e)?n(null,[]):(log.info("resolving dns seed "+e),void dns.resolve(e,function(r,i){return r?(log.err("failed to resolve dns seed "+e,r),t.seeds.failed.push(e),n(null,[])):(log.info("found "+i.length+" peers from "+e),t.seeds.resolved.push(e),i=i.map(function(e){return new Peer(e,networks[t.config.network].defaultClientPort)}),i.forEach(function(e){t.peers.length33&&!this.compressed()||34==this.data.length&&1!=this.data[33]||this.data.length>34)throw new Error("invalid data length")}),"undefined"==typeof this.network())throw new Error("invalid network")},i.prototype.payload=function(t){if(t)return this.doAsBinary(function(){t.copy(this.data,1)}),t;var i=this.as("binary");return 34==i.length?i.slice(1,33):33==i.length?i.slice(1):void 0},i.prototype.compressed=function(i){if(void 0===i){var e=34,r=this.as("binary");if(r.length==e&&1==r[e-1])return!0;if(r.length==e-1)return!1;throw new Error("invalid private key")}this.doAsBinary(function(){var e=34;if(i){var r=new t(e);this.data.copy(r),this.data=r,this.data[e-1]=1}else this.data=this.data.slice(0,e-1)})},i.prototype.network=function(){var t,i=this.version(),e=n.livenet,r=n.testnet;return i===e.privKeyVersion?t=e:i===r.privKeyVersion&&(t=r),t},module.exports=i}).call(this,require("buffer").Buffer); -},{"../networks":"ULNIu2","../util/EncodedData":"eLfUFE","../util/VersionedData":"QLzNQg","buffer":97,"util":130}],"./lib/PrivateKey":[function(require,module,exports){ +var log = require('../util/log'); +var bitcoreDefaults = require('../config'); +var Connection = require('./Connection'); +var Peer = require('./Peer'); +var async = require('async'); +var dns = require('dns'); +var networks = require('../networks'); +var util = require('util'); + +GetAdjustedTime = function() { + // TODO: Implement actual adjustment + return Math.floor(new Date().getTime() / 1000); +}; + +function PeerManager(config) { + // extend defaults with config + this.config = config || {}; + for (var i in bitcoreDefaults) + if (bitcoreDefaults.hasOwnProperty(i) && this.config[i] === undefined) + this.config[i] = bitcoreDefaults[i]; + + this.active = false; + this.timer = null; + + this.peers = []; + this.pool = []; + this.connections = []; + this.isConnected = false; + this.peerDiscovery = false; + + // Move these to the Node's settings object + this.interval = 5000; + this.minConnections = 8; + this.minKnownPeers = 10; + + // keep track of tried seeds and results + this.seeds = { + resolved: [], + failed: [] + }; +} + +var EventEmitter = require('events').EventEmitter; +util.inherits(PeerManager, EventEmitter); +PeerManager.Connection = Connection; + +PeerManager.prototype.start = function() { + this.active = true; + if (!this.timer) { + this.timer = setInterval(this.checkStatus.bind(this), this.interval); + } +}; + +PeerManager.prototype.stop = function() { + this.active = false; + if (this.timer) { + clearInterval(this.timer); + this.timer = null; + } + for (var i = 0; i < this.connections.length; i++) { + this.connections[i].socket.end(); + }; +}; + +PeerManager.prototype.addPeer = function(peer, port) { + if (peer instanceof Peer) { + this.peers.push(peer); + } else if ("string" == typeof peer) { + this.addPeer(new Peer(peer, port)); + } else { + log.err('Node.addPeer(): Invalid value provided for peer', { + val: peer + }); + throw 'Node.addPeer(): Invalid value provided for peer.'; + } +}; + +PeerManager.prototype.removePeer = function(peer) { + var index = this.peers.indexOf(peer); + var exists = !!~index; + if (exists) this.peers.splice(index, 1); + return exists; +}; + +PeerManager.prototype.checkStatus = function checkStatus() { + // Make sure we are connected to all forcePeers + if (this.peers.length) { + var peerIndex = {}; + this.peers.forEach(function(peer) { + peerIndex[peer.toString()] = peer; + }); + + // Ignore the ones we're already connected to + this.connections.forEach(function(conn) { + var peerName = conn.peer.toString(); + if ("undefined" !== peerIndex[peerName]) { + delete peerIndex[peerName]; + } + }); + + // for debug purposes, print how many of our peers are actually connected + var connected = 0 + this.peers.forEach(function(p) { + if (p.connection && !p.connection._connecting) connected++ + }); + log.debug(connected + ' of ' + this.peers.length + ' peers connected'); + + Object.keys(peerIndex).forEach(function(i) { + this.connectTo(peerIndex[i]); + }.bind(this)); + } +}; + +PeerManager.prototype.connectTo = function(peer) { + log.info('connecting to ' + peer); + try { + return this.addConnection(peer.createConnection(), peer); + } catch (e) { + log.err('creating connection', e); + return null; + } +}; + +PeerManager.prototype.addConnection = function(socketConn, peer) { + var conn = new Connection(socketConn, peer, this.config); + this.connections.push(conn); + this.emit('connection', conn); + + conn.addListener('version', this.handleVersion.bind(this)); + conn.addListener('verack', this.handleReady.bind(this)); + conn.addListener('addr', this.handleAddr.bind(this)); + conn.addListener('getaddr', this.handleGetAddr.bind(this)); + conn.addListener('error', this.handleError.bind(this)); + conn.addListener('disconnect', this.handleDisconnect.bind(this)); + + return conn; +}; + +PeerManager.prototype.handleVersion = function(e) { + e.peer.version = e.message.version; + e.peer.start_height = e.message.start_height; + + if (!e.conn.inbound) { + // TODO: Advertise our address (if listening) + } + // Get recent addresses + if (this.peerDiscovery && + (e.message.version >= 31402 || this.peers.length < 1000)) { + e.conn.sendGetAddr(); + e.conn.getaddr = true; + } +}; + +PeerManager.prototype.handleReady = function(e) { + log.info('connected to ' + e.conn.peer.host + ':' + e.conn.peer.port); + this.emit('connect', { + pm: this, + conn: e.conn, + socket: e.socket, + peer: e.peer + }); + + if (this.isConnected == false) { + this.emit('netConnected', e); + this.isConnected = true; + } +}; + +PeerManager.prototype.handleAddr = function(e) { + if (!this.peerDiscovery) return; + + var now = GetAdjustedTime(); + e.message.addrs.forEach(function(addr) { + try { + // In case of an invalid time, assume "5 days ago" + if (addr.time <= 100000000 || addr.time > (now + 10 * 60)) { + addr.time = now - 5 * 24 * 60 * 60; + } + var peer = new Peer(addr.ip, addr.port, addr.services); + peer.lastSeen = addr.time; + + // TODO: Handle duplicate peers + this.peers.push(peer); + + // TODO: Handle addr relay + } catch (e) { + log.warn("Invalid addr received: " + e.message); + } + }.bind(this)); + if (e.message.addrs.length < 1000) { + e.conn.getaddr = false; + } +}; + +PeerManager.prototype.handleGetAddr = function(e) { + // TODO: Reply with addr message. +}; + +PeerManager.prototype.handleError = function(e) { + log.err('unkown error with peer ' + e.peer + ' (disconnecting): ' + e.err); + this.handleDisconnect.apply(this, [].slice.call(arguments)); +}; + +PeerManager.prototype.handleDisconnect = function(e) { + log.info('disconnected from peer ' + e.peer); + var i = this.connections.indexOf(e.conn); + if (i != -1) this.connections.splice(i, 1); + + this.removePeer(e.peer); + if (this.pool.length) { + log.info('replacing peer using the pool of ' + this.pool.length + ' seeds'); + this.addPeer(this.pool.pop()); + } + + if (!this.connections.length) { + this.emit('netDisconnected'); + this.isConnected = false; + } +}; + +PeerManager.prototype.getActiveConnection = function() { + var activeConnections = this.connections.filter(function(conn) { + return conn.active; + }); + + if (activeConnections.length) { + var randomIndex = Math.floor(Math.random() * activeConnections.length); + var candidate = activeConnections[randomIndex]; + if (candidate.socket.writable) { + return candidate; + } else { + // Socket is not writable, remove it from active connections + activeConnections.splice(randomIndex, 1); + + // Then try again + // TODO: This causes an infinite recursion when all connections are dead, + // although it shouldn't. + return this.getActiveConnection(); + } + } else { + return null; + } +}; + +PeerManager.prototype.getActiveConnections = function() { + return this.connections.slice(0); +}; + +PeerManager.prototype.discover = function(options, callback) { + var self = this; + var seeds = networks[self.config.network].dnsSeeds; + + self.limit = options.limit || 12; + + var dnsExecutor = seeds.map(function(seed) { + return function(done) { + // have we already resolved this seed? + if (~self.seeds.resolved.indexOf(seed)) { + // if so, just pass back cached peer list + return done(null, self.seeds.results[seed]); + } + + // has this seed failed to resolve? + if (~self.seeds.failed.indexOf(seed)) { + // if so, pass back empty results + return done(null, []); + } + + log.info('resolving dns seed ' + seed); + + dns.resolve(seed, function(err, peers) { + if (err) { + log.err('failed to resolve dns seed ' + seed, err); + self.seeds.failed.push(seed); + return done(null, []); + } + + log.info('found ' + peers.length + ' peers from ' + seed); + self.seeds.resolved.push(seed); + + // transform that list into a list of Peer instances + peers = peers.map(function(ip) { + return new Peer(ip, networks[self.config.network].defaultClientPort); + }); + + peers.forEach(function(p) { + if (self.peers.length < self.limit) self.addPeer(p); + else self.pool.push(p); + }); + + self.emit('peers', peers); + + return done(null, peers); + }); + + }; + }); + + // try resolving all seeds + async.parallel(dnsExecutor, function(err, results) { + var peers = []; + + // consolidate all resolved peers into one list + results.forEach(function(peerlist) { + peers = peers.concat(peerlist); + }); + + if (typeof callback === 'function') callback(null, peers); + }); + + return self; +}; + +module.exports = PeerManager; + +},{"../config":"4itQ50","../networks":"ULNIu2","../util/log":"AdF7pF","./Connection":"DB/p3X","./Peer":"oolY81","async":97,"dns":106,"events":"T9Wsc/","util":141}],"izTl9z":[function(require,module,exports){ +(function (Buffer){ + +var VersionedData = require('../util/VersionedData'); +var EncodedData = require('../util/EncodedData'); +var networks = require('../networks'); +var util = require('util'); + +//compressed is true if public key is compressed; false otherwise +function PrivateKey(version, buf, compressed) { + PrivateKey.super_.call(this, version, buf); + if (compressed !== undefined) + this.compressed(compressed); +}; +util.inherits(PrivateKey, VersionedData); +EncodedData.applyEncodingsTo(PrivateKey); + +PrivateKey.prototype.validate = function() { + this.doAsBinary(function() { + PrivateKey.super_.prototype.validate.call(this); + if (this.data.length < 32 || (this.data.length > 1 + 32 && !this.compressed()) || (this.data.length == 1 + 32 + 1 && this.data[1 + 32 + 1 - 1] != 1) || this.data.length > 1 + 32 + 1) + throw new Error('invalid data length'); + }); + if (typeof this.network() === 'undefined') throw new Error('invalid network'); +}; + +// get or set the payload data (as a Buffer object) +// overloaded from VersionedData +PrivateKey.prototype.payload = function(data) { + if (data) { + this.doAsBinary(function() { + data.copy(this.data, 1); + }); + return data; + } + var buf = this.as('binary'); + if (buf.length == 1 + 32 + 1) + return buf.slice(1, 1 + 32); + else if (buf.length == 1 + 32) + return buf.slice(1); +}; + +// get or set whether the corresponding public key is compressed +PrivateKey.prototype.compressed = function(compressed) { + if (compressed !== undefined) { + this.doAsBinary(function() { + var len = 1 + 32 + 1; + if (compressed) { + var data = new Buffer(len); + this.data.copy(data); + this.data = data; + this.data[len - 1] = 1; + } else { + this.data = this.data.slice(0, len - 1); + } + }); + } else { + var len = 1 + 32 + 1; + var data = this.as('binary'); + if (data.length == len && data[len - 1] == 1) + return true; + else if (data.length == len - 1) + return false; + else + throw new Error('invalid private key'); + } +}; + +PrivateKey.prototype.network = function() { + var version = this.version(); + + var livenet = networks.livenet; + var testnet = networks.testnet; + + var answer; + if (version === livenet.privKeyVersion) + answer = livenet; + else if (version === testnet.privKeyVersion) + answer = testnet; + + return answer; +}; + +module.exports = PrivateKey; + +}).call(this,require("buffer").Buffer) +},{"../networks":"ULNIu2","../util/EncodedData":"eLfUFE","../util/VersionedData":"QLzNQg","buffer":108,"util":141}],"./lib/PrivateKey":[function(require,module,exports){ module.exports=require('izTl9z'); },{}],"./lib/RpcClient":[function(require,module,exports){ module.exports=require('7siE1N'); },{}],"7siE1N":[function(require,module,exports){ -(function(t){function e(t){t=t||{},this.host=t.host||"127.0.0.1",this.port=t.port||8332,this.user=t.user||"user",this.pass=t.pass||"pass",this.protocol="http"==t.protocol?n:o,this.batchedCalls=null,this.disableAgent=t.disableAgent||!1}function r(t,e,r){function s(t,e){return function(){var s=arguments.length-1;if(this.batchedCalls)var s=arguments.length;for(var n=0;s>n;n++)e[n]&&(arguments[n]=e[n](arguments[n]));this.batchedCalls?this.batchedCalls.push({jsonrpc:"2.0",method:t,params:l(arguments)}):r.call(this,{method:t,params:l(arguments,0,arguments.length-1)},arguments[arguments.length-1])}}var n={str:function(t){return t.toString()},"int":function(t){return parseFloat(t)},"float":function(t){return parseFloat(t)},bool:function(t){return t===!0||"1"==t||"true"==t||"true"==t.toString().toLowerCase()}};for(var o in e)if(e.hasOwnProperty(o)){for(var i=e[o].split(" "),a=0;a=h.map.OP_1&&t<=h.map.OP_16}function n(t){return t=t?2:65535>=t?3:5}function s(e){var r=void 0;return e=e?(r=new t(2),r.writeUInt8(h.map.OP_PUSHDATA1,0),r.writeUInt8(e,1)):65535>=e?(r=new t(3),r.writeUInt8(h.map.OP_PUSHDATA2,0),r.writeUInt16LE(e,1)):(r=new t(5),r.writeUInt8(h.map.OP_PUSHDATA4,0),r.writeUInt32LE(e,1)),r}var u=(require("../config"),require("../util/log")),h=require("./Opcode"),o=require("buffertools"),f=require("../util/util"),c=require("../util/BinaryParser"),p=require("bufferput"),a=0,l=1,g=2,k=3,y=4,S=["unknown","pubkey","pubkeyhash","multisig","scripthash"];e.TX_UNKNOWN=a,e.TX_PUBKEY=l,e.TX_PUBKEYHASH=g,e.TX_MULTISIG=k,e.TX_SCRIPTHASH=y,e.prototype.parse=function(){this.chunks=[];for(var t=new c(this.buffer);!t.eof();){var e,r,n=t.word8();n>0&&nh.map.OP_16)return!1}return!0},e.prototype.isP2SH=function(){return 3==this.chunks.length&&this.chunks[0]==h.map.OP_HASH160&&t.isBuffer(this.chunks[1])&&20==this.chunks[1].length&&this.chunks[2]==h.map.OP_EQUAL},e.prototype.isPubkey=function(){return 2==this.chunks.length&&t.isBuffer(this.chunks[0])&&this.chunks[1]==h.map.OP_CHECKSIG},e.prototype.isPubkeyHash=function(){return 5==this.chunks.length&&this.chunks[0]==h.map.OP_DUP&&this.chunks[1]==h.map.OP_HASH160&&t.isBuffer(this.chunks[2])&&20==this.chunks[2].length&&this.chunks[3]==h.map.OP_EQUALVERIFY&&this.chunks[4]==h.map.OP_CHECKSIG},e.prototype.isMultiSig=function(){return this.chunks.length>3&&r(this.chunks[0])&&this.chunks.slice(1,this.chunks.length-2).every(function(e){return t.isBuffer(e)})&&r(this.chunks[this.chunks.length-2])&&this.chunks[this.chunks.length-1]==h.map.OP_CHECKMULTISIG},e.prototype.isPubkeyHashScriptSig=function(){return 2==this.chunks.length&&t.isBuffer(this.chunks[0])&&t.isBuffer(this.chunks[1])},e.prototype.isP2shScriptSig=function(){if(!r(this.chunks[0])||0!==this.chunks[0])return!1;var t=new e(this.chunks[this.chunks.length-1]),n=t.classify();return n!==a},e.prototype.isMultiSigScriptSig=function(){return r(this.chunks[0])&&0===this.chunks[0]?!this.isP2shScriptSig():!1},e.prototype.isPubkeyScriptSig=function(){return 1==this.chunks.length&&t.isBuffer(this.chunks[0])},e.prototype.countSignatures=function(){var t=0,e=this.chunks.length;return t=this.isMultiSigScriptSig()?e-1:this.isP2shScriptSig()?e-2:this.isPubkeyHashScriptSig()?1:0},e.prototype.getSignatures=function(){ret=[];var t=this.chunks.length;if(this.isMultiSigScriptSig())for(var e=1;t>e;e++)ret.push(this.chunks[e]);else if(this.isP2shScriptSig())for(var e=1;t-1>e;e++)ret.push(this.chunks[e]);else this.isPubkeyHashScriptSig()&&ret.push(this.chunks[0]);return ret},e.prototype.getHashType=function(){for(var t=this.getSignatures(),e=null,r=0;ri;i++){var u=this.chunks[i];if(i>0&&(n+=" "),n+=t.isBuffer(u)?"0x"+f.formatBuffer(u,e?null:0):h.reverseMap[u],r&&i>r){n+=" ...";break}}return n},e.prototype.toString=function(t,e){var r="