diff --git a/app/controllers/socket.js b/app/controllers/socket.js index 629256a..7406198 100644 --- a/app/controllers/socket.js +++ b/app/controllers/socket.js @@ -5,8 +5,10 @@ var ios = null; // io is already taken in express var util = require('bitcore').util; var mdb = require('../../lib/MessageDb').default(); var microtime = require('microtime'); +var enableMessageBroker; -module.exports.init = function(io_ext) { +module.exports.init = function(io_ext, config) { + enableMessageBroker = config ? config.enableMessageBroker : false; ios = io_ext; if (ios) { // when a new socket connects @@ -14,40 +16,43 @@ module.exports.init = function(io_ext) { // when it subscribes, make it join the according room socket.on('subscribe', function(topic) { if (socket.rooms.length === 1) { - console.log('subscribe to '+topic); + console.log('subscribe to ' + topic); socket.join(topic); } }); - // when it requests sync, send him all pending messages - socket.on('sync', function(ts) { - var rooms = socket.rooms; - if (rooms.length !== 2) { - socket.emit('insight-error', 'Must subscribe with public key before syncing'); - return; - } - var to = rooms[1]; - var upper_ts = Math.round(microtime.now()); - mdb.getMessages(to, ts, upper_ts, function(err, messages) { - if (err) { - throw new Error('Couldn\'t get messages on sync request: ' + err); - } - for (var i = 0; i < messages.length; i++) { - broadcastMessage(messages[i], socket); + if (enableMessageBroker) { + // when it requests sync, send him all pending messages + socket.on('sync', function(ts) { + var rooms = socket.rooms; + if (rooms.length !== 2) { + socket.emit('insight-error', 'Must subscribe with public key before syncing'); + return; } + var to = rooms[1]; + var upper_ts = Math.round(microtime.now()); + mdb.getMessages(to, ts, upper_ts, function(err, messages) { + if (err) { + throw new Error('Couldn\'t get messages on sync request: ' + err); + } + for (var i = 0; i < messages.length; i++) { + broadcastMessage(messages[i], socket); + } + }); }); - }); - // when it sends a message, add it to db - socket.on('message', function(m) { - mdb.addMessage(m, function(err) { - if (err) { - throw new Error('Couldn\'t add message to database: ' + err); - } + // when it sends a message, add it to db + socket.on('message', function(m) { + mdb.addMessage(m, function(err) { + if (err) { + throw new Error('Couldn\'t add message to database: ' + err); + } + }); }); - }); + } }); - mdb.on('message', broadcastMessage); + if (enableMessageBroker) + mdb.on('message', broadcastMessage); } }; diff --git a/config/config.js b/config/config.js index 379e983..8d7c02b 100644 --- a/config/config.js +++ b/config/config.js @@ -76,7 +76,7 @@ var bitcoindConf = { disableAgent: true }; - +var enableMessageBroker = process.env.ENABLE_MESSAGE_BROKER === 'true'; if (!fs.existsSync(db)) { var err = fs.mkdirSync(db); @@ -89,6 +89,7 @@ if (!fs.existsSync(db)) { } module.exports = { + enableMessageBroker: enableMessageBroker, version: version, root: rootPath, publicPath: process.env.INSIGHT_PUBLIC_PATH || false, diff --git a/examples/bitcore.js b/examples/bitcore.js deleted file mode 120000 index add1317..0000000 --- a/examples/bitcore.js +++ /dev/null @@ -1 +0,0 @@ -../node_modules/bitcore/browser/bundle.js \ No newline at end of file diff --git a/examples/bitcore.js b/examples/bitcore.js new file mode 100644 index 0000000..3f55d37 --- /dev/null +++ b/examples/bitcore.js @@ -0,0 +1,41841 @@ +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;o 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":95,"preconditions":163}],"./lib/AuthMessage":[function(require,module,exports){ +module.exports=require('cBnJMk'); +},{}],"./lib/BIP39":[function(require,module,exports){ +module.exports=require('82LilS'); +},{}],"82LilS":[function(require,module,exports){ +(function (Buffer){ +var coinUtil = require('../util'); +var sjcl = require('./sjcl'); +var SecureRandom = require('./SecureRandom'); + +var hmacSHA512 = function(key) { + var hasher = new sjcl.misc.hmac(key, sjcl.hash.sha512); + this.encrypt = function() { + return hasher.encrypt.apply(hasher, arguments); + }; +}; + +var pbkdf2Sync_sha512 = function(password, salt, iterations, keylen) { + var derivedKey = sjcl.misc.pbkdf2(password, salt, iterations, 512, hmacSHA512); + return sjcl.codec.hex.fromBits(derivedKey) +}; + +var BIP39 = function() {}; + +BIP39.mnemonic = function(wordlist, bits) { + if (!bits) + bits = 128; + if (bits % 32 != 0) + throw new Error("bits must be multiple of 32"); + var buf = SecureRandom.getRandomBuffer(bits / 8); + return BIP39.entropy2mnemonic(wordlist, buf); +} + +BIP39.entropy2mnemonic = function(wordlist, buf) { + var hash = coinUtil.sha256(buf); + var bin = ""; + var bits = buf.length * 8; + for (var i = 0; i < buf.length; i++) { + bin = bin + ("00000000" + buf[i].toString(2)).slice(-8); + } + var hashbits = hash[0].toString(2); + hashbits = ("00000000" + hashbits).slice(-8).slice(0, bits / 32); + bin = bin + hashbits; + if (bin.length % 11 != 0) + throw new Error("internal error - entropy not an even multiple of 11 bits - " + bin.length); + var mnemonic = ""; + for (var i = 0; i < bin.length / 11; i++) { + if (mnemonic != "") + mnemonic = mnemonic + " "; + var wi = parseInt(bin.slice(i * 11, (i + 1) * 11), 2); + mnemonic = mnemonic + wordlist[wi]; + } + return mnemonic; +} + +BIP39.check = function(wordlist, mnemonic) { + var words = mnemonic.split(' '); + var bin = ""; + for (var i = 0; i < words.length; i++) { + var ind = wordlist.indexOf(words[i]); + if (ind < 0) + return false; + bin = bin + ("00000000000" + ind.toString(2)).slice(-11); + } + + if (bin.length % 11 != 0) { + throw new Error("internal error - entropy not an even multiple of 11 bits - " + bin.length); + } + var cs = bin.length / 33; + var hash_bits = bin.slice(-cs); + var nonhash_bits = bin.slice(0, bin.length - cs); + var buf = new Buffer(nonhash_bits.length / 8); + for (var i = 0; i < nonhash_bits.length / 8; i++) { + buf.writeUInt8(parseInt(bin.slice(i * 8, (i + 1) * 8), 2), i); + } + var hash = coinUtil.sha256(buf); + var expected_hash_bits = hash[0].toString(2); + expected_hash_bits = ("00000000" + expected_hash_bits).slice(-8).slice(0, cs); + return expected_hash_bits == hash_bits; +} + +BIP39.mnemonic2seed = function(mnemonic, passphrase) { + if (!passphrase) + passphrase = ""; + var hex = pbkdf2Sync_sha512(mnemonic, "mnemonic" + passphrase, 2048, 64); + var buf = new Buffer(hex, 'hex'); + return buf; +} + +module.exports = BIP39; + +}).call(this,require("buffer").Buffer) +},{"../util":191,"./SecureRandom":"p4SiC2","./sjcl":"oLMOpG","buffer":95}],"./lib/BIP39WordlistEn":[function(require,module,exports){ +module.exports=require('sp4vFZ'); +},{}],"sp4vFZ":[function(require,module,exports){ +var BIP39WordlistEn = ["abandon", "ability", "able", "about", "above", "absent", "absorb", "abstract", "absurd", "abuse", "access", "accident", "account", "accuse", "achieve", "acid", "acoustic", "acquire", "across", "act", "action", "actor", "actress", "actual", "adapt", "add", "addict", "address", "adjust", "admit", "adult", "advance", "advice", "aerobic", "affair", "afford", "afraid", "again", "age", "agent", "agree", "ahead", "aim", "air", "airport", "aisle", "alarm", "album", "alcohol", "alert", "alien", "all", "alley", "allow", "almost", "alone", "alpha", "already", "also", "alter", "always", "amateur", "amazing", "among", "amount", "amused", "analyst", "anchor", "ancient", "anger", "angle", "angry", "animal", "ankle", "announce", "annual", "another", "answer", "antenna", "antique", "anxiety", "any", "apart", "apology", "appear", "apple", "approve", "april", "arch", "arctic", "area", "arena", "argue", "arm", "armed", "armor", "army", "around", "arrange", "arrest", "arrive", "arrow", "art", "artefact", "artist", "artwork", "ask", "aspect", "assault", "asset", "assist", "assume", "asthma", "athlete", "atom", "attack", "attend", "attitude", "attract", "auction", "audit", "august", "aunt", "author", "auto", "autumn", "average", "avocado", "avoid", "awake", "aware", "away", "awesome", "awful", "awkward", "axis", "baby", "bachelor", "bacon", "badge", "bag", "balance", "balcony", "ball", "bamboo", "banana", "banner", "bar", "barely", "bargain", "barrel", "base", "basic", "basket", "battle", "beach", "bean", "beauty", "because", "become", "beef", "before", "begin", "behave", "behind", "believe", "below", "belt", "bench", "benefit", "best", "betray", "better", "between", "beyond", "bicycle", "bid", "bike", "bind", "biology", "bird", "birth", "bitter", "black", "blade", "blame", "blanket", "blast", "bleak", "bless", "blind", "blood", "blossom", "blouse", "blue", "blur", "blush", "board", "boat", "body", "boil", "bomb", "bone", "bonus", "book", "boost", "border", "boring", "borrow", "boss", "bottom", "bounce", "box", "boy", "bracket", "brain", "brand", "brass", "brave", "bread", "breeze", "brick", "bridge", "brief", "bright", "bring", "brisk", "broccoli", "broken", "bronze", "broom", "brother", "brown", "brush", "bubble", "buddy", "budget", "buffalo", "build", "bulb", "bulk", "bullet", "bundle", "bunker", "burden", "burger", "burst", "bus", "business", "busy", "butter", "buyer", "buzz", "cabbage", "cabin", "cable", "cactus", "cage", "cake", "call", "calm", "camera", "camp", "can", "canal", "cancel", "candy", "cannon", "canoe", "canvas", "canyon", "capable", "capital", "captain", "car", "carbon", "card", "cargo", "carpet", "carry", "cart", "case", "cash", "casino", "castle", "casual", "cat", "catalog", "catch", "category", "cattle", "caught", "cause", "caution", "cave", "ceiling", "celery", "cement", "census", "century", "cereal", "certain", "chair", "chalk", "champion", "change", "chaos", "chapter", "charge", "chase", "chat", "cheap", "check", "cheese", "chef", "cherry", "chest", "chicken", "chief", "child", "chimney", "choice", "choose", "chronic", "chuckle", "chunk", "churn", "cigar", "cinnamon", "circle", "citizen", "city", "civil", "claim", "clap", "clarify", "claw", "clay", "clean", "clerk", "clever", "click", "client", "cliff", "climb", "clinic", "clip", "clock", "clog", "close", "cloth", "cloud", "clown", "club", "clump", "cluster", "clutch", "coach", "coast", "coconut", "code", "coffee", "coil", "coin", "collect", "color", "column", "combine", "come", "comfort", "comic", "common", "company", "concert", "conduct", "confirm", "congress", "connect", "consider", "control", "convince", "cook", "cool", "copper", "copy", "coral", "core", "corn", "correct", "cost", "cotton", "couch", "country", "couple", "course", "cousin", "cover", "coyote", "crack", "cradle", "craft", "cram", "crane", "crash", "crater", "crawl", "crazy", "cream", "credit", "creek", "crew", "cricket", "crime", "crisp", "critic", "crop", "cross", "crouch", "crowd", "crucial", "cruel", "cruise", "crumble", "crunch", "crush", "cry", "crystal", "cube", "culture", "cup", "cupboard", "curious", "current", "curtain", "curve", "cushion", "custom", "cute", "cycle", "dad", "damage", "damp", "dance", "danger", "daring", "dash", "daughter", "dawn", "day", "deal", "debate", "debris", "decade", "december", "decide", "decline", "decorate", "decrease", "deer", "defense", "define", "defy", "degree", "delay", "deliver", "demand", "demise", "denial", "dentist", "deny", "depart", "depend", "deposit", "depth", "deputy", "derive", "describe", "desert", "design", "desk", "despair", "destroy", "detail", "detect", "develop", "device", "devote", "diagram", "dial", "diamond", "diary", "dice", "diesel", "diet", "differ", "digital", "dignity", "dilemma", "dinner", "dinosaur", "direct", "dirt", "disagree", "discover", "disease", "dish", "dismiss", "disorder", "display", "distance", "divert", "divide", "divorce", "dizzy", "doctor", "document", "dog", "doll", "dolphin", "domain", "donate", "donkey", "donor", "door", "dose", "double", "dove", "draft", "dragon", "drama", "drastic", "draw", "dream", "dress", "drift", "drill", "drink", "drip", "drive", "drop", "drum", "dry", "duck", "dumb", "dune", "during", "dust", "dutch", "duty", "dwarf", "dynamic", "eager", "eagle", "early", "earn", "earth", "easily", "east", "easy", "echo", "ecology", "economy", "edge", "edit", "educate", "effort", "egg", "eight", "either", "elbow", "elder", "electric", "elegant", "element", "elephant", "elevator", "elite", "else", "embark", "embody", "embrace", "emerge", "emotion", "employ", "empower", "empty", "enable", "enact", "end", "endless", "endorse", "enemy", "energy", "enforce", "engage", "engine", "enhance", "enjoy", "enlist", "enough", "enrich", "enroll", "ensure", "enter", "entire", "entry", "envelope", "episode", "equal", "equip", "era", "erase", "erode", "erosion", "error", "erupt", "escape", "essay", "essence", "estate", "eternal", "ethics", "evidence", "evil", "evoke", "evolve", "exact", "example", "excess", "exchange", "excite", "exclude", "excuse", "execute", "exercise", "exhaust", "exhibit", "exile", "exist", "exit", "exotic", "expand", "expect", "expire", "explain", "expose", "express", "extend", "extra", "eye", "eyebrow", "fabric", "face", "faculty", "fade", "faint", "faith", "fall", "false", "fame", "family", "famous", "fan", "fancy", "fantasy", "farm", "fashion", "fat", "fatal", "father", "fatigue", "fault", "favorite", "feature", "february", "federal", "fee", "feed", "feel", "female", "fence", "festival", "fetch", "fever", "few", "fiber", "fiction", "field", "figure", "file", "film", "filter", "final", "find", "fine", "finger", "finish", "fire", "firm", "first", "fiscal", "fish", "fit", "fitness", "fix", "flag", "flame", "flash", "flat", "flavor", "flee", "flight", "flip", "float", "flock", "floor", "flower", "fluid", "flush", "fly", "foam", "focus", "fog", "foil", "fold", "follow", "food", "foot", "force", "forest", "forget", "fork", "fortune", "forum", "forward", "fossil", "foster", "found", "fox", "fragile", "frame", "frequent", "fresh", "friend", "fringe", "frog", "front", "frost", "frown", "frozen", "fruit", "fuel", "fun", "funny", "furnace", "fury", "future", "gadget", "gain", "galaxy", "gallery", "game", "gap", "garage", "garbage", "garden", "garlic", "garment", "gas", "gasp", "gate", "gather", "gauge", "gaze", "general", "genius", "genre", "gentle", "genuine", "gesture", "ghost", "giant", "gift", "giggle", "ginger", "giraffe", "girl", "give", "glad", "glance", "glare", "glass", "glide", "glimpse", "globe", "gloom", "glory", "glove", "glow", "glue", "goat", "goddess", "gold", "good", "goose", "gorilla", "gospel", "gossip", "govern", "gown", "grab", "grace", "grain", "grant", "grape", "grass", "gravity", "great", "green", "grid", "grief", "grit", "grocery", "group", "grow", "grunt", "guard", "guess", "guide", "guilt", "guitar", "gun", "gym", "habit", "hair", "half", "hammer", "hamster", "hand", "happy", "harbor", "hard", "harsh", "harvest", "hat", "have", "hawk", "hazard", "head", "health", "heart", "heavy", "hedgehog", "height", "hello", "helmet", "help", "hen", "hero", "hidden", "high", "hill", "hint", "hip", "hire", "history", "hobby", "hockey", "hold", "hole", "holiday", "hollow", "home", "honey", "hood", "hope", "horn", "horror", "horse", "hospital", "host", "hotel", "hour", "hover", "hub", "huge", "human", "humble", "humor", "hundred", "hungry", "hunt", "hurdle", "hurry", "hurt", "husband", "hybrid", "ice", "icon", "idea", "identify", "idle", "ignore", "ill", "illegal", "illness", "image", "imitate", "immense", "immune", "impact", "impose", "improve", "impulse", "inch", "include", "income", "increase", "index", "indicate", "indoor", "industry", "infant", "inflict", "inform", "inhale", "inherit", "initial", "inject", "injury", "inmate", "inner", "innocent", "input", "inquiry", "insane", "insect", "inside", "inspire", "install", "intact", "interest", "into", "invest", "invite", "involve", "iron", "island", "isolate", "issue", "item", "ivory", "jacket", "jaguar", "jar", "jazz", "jealous", "jeans", "jelly", "jewel", "job", "join", "joke", "journey", "joy", "judge", "juice", "jump", "jungle", "junior", "junk", "just", "kangaroo", "keen", "keep", "ketchup", "key", "kick", "kid", "kidney", "kind", "kingdom", "kiss", "kit", "kitchen", "kite", "kitten", "kiwi", "knee", "knife", "knock", "know", "lab", "label", "labor", "ladder", "lady", "lake", "lamp", "language", "laptop", "large", "later", "latin", "laugh", "laundry", "lava", "law", "lawn", "lawsuit", "layer", "lazy", "leader", "leaf", "learn", "leave", "lecture", "left", "leg", "legal", "legend", "leisure", "lemon", "lend", "length", "lens", "leopard", "lesson", "letter", "level", "liar", "liberty", "library", "license", "life", "lift", "light", "like", "limb", "limit", "link", "lion", "liquid", "list", "little", "live", "lizard", "load", "loan", "lobster", "local", "lock", "logic", "lonely", "long", "loop", "lottery", "loud", "lounge", "love", "loyal", "lucky", "luggage", "lumber", "lunar", "lunch", "luxury", "lyrics", "machine", "mad", "magic", "magnet", "maid", "mail", "main", "major", "make", "mammal", "man", "manage", "mandate", "mango", "mansion", "manual", "maple", "marble", "march", "margin", "marine", "market", "marriage", "mask", "mass", "master", "match", "material", "math", "matrix", "matter", "maximum", "maze", "meadow", "mean", "measure", "meat", "mechanic", "medal", "media", "melody", "melt", "member", "memory", "mention", "menu", "mercy", "merge", "merit", "merry", "mesh", "message", "metal", "method", "middle", "midnight", "milk", "million", "mimic", "mind", "minimum", "minor", "minute", "miracle", "mirror", "misery", "miss", "mistake", "mix", "mixed", "mixture", "mobile", "model", "modify", "mom", "moment", "monitor", "monkey", "monster", "month", "moon", "moral", "more", "morning", "mosquito", "mother", "motion", "motor", "mountain", "mouse", "move", "movie", "much", "muffin", "mule", "multiply", "muscle", "museum", "mushroom", "music", "must", "mutual", "myself", "mystery", "myth", "naive", "name", "napkin", "narrow", "nasty", "nation", "nature", "near", "neck", "need", "negative", "neglect", "neither", "nephew", "nerve", "nest", "net", "network", "neutral", "never", "news", "next", "nice", "night", "noble", "noise", "nominee", "noodle", "normal", "north", "nose", "notable", "note", "nothing", "notice", "novel", "now", "nuclear", "number", "nurse", "nut", "oak", "obey", "object", "oblige", "obscure", "observe", "obtain", "obvious", "occur", "ocean", "october", "odor", "off", "offer", "office", "often", "oil", "okay", "old", "olive", "olympic", "omit", "once", "one", "onion", "online", "only", "open", "opera", "opinion", "oppose", "option", "orange", "orbit", "orchard", "order", "ordinary", "organ", "orient", "original", "orphan", "ostrich", "other", "outdoor", "outer", "output", "outside", "oval", "oven", "over", "own", "owner", "oxygen", "oyster", "ozone", "pact", "paddle", "page", "pair", "palace", "palm", "panda", "panel", "panic", "panther", "paper", "parade", "parent", "park", "parrot", "party", "pass", "patch", "path", "patient", "patrol", "pattern", "pause", "pave", "payment", "peace", "peanut", "pear", "peasant", "pelican", "pen", "penalty", "pencil", "people", "pepper", "perfect", "permit", "person", "pet", "phone", "photo", "phrase", "physical", "piano", "picnic", "picture", "piece", "pig", "pigeon", "pill", "pilot", "pink", "pioneer", "pipe", "pistol", "pitch", "pizza", "place", "planet", "plastic", "plate", "play", "please", "pledge", "pluck", "plug", "plunge", "poem", "poet", "point", "polar", "pole", "police", "pond", "pony", "pool", "popular", "portion", "position", "possible", "post", "potato", "pottery", "poverty", "powder", "power", "practice", "praise", "predict", "prefer", "prepare", "present", "pretty", "prevent", "price", "pride", "primary", "print", "priority", "prison", "private", "prize", "problem", "process", "produce", "profit", "program", "project", "promote", "proof", "property", "prosper", "protect", "proud", "provide", "public", "pudding", "pull", "pulp", "pulse", "pumpkin", "punch", "pupil", "puppy", "purchase", "purity", "purpose", "purse", "push", "put", "puzzle", "pyramid", "quality", "quantum", "quarter", "question", "quick", "quit", "quiz", "quote", "rabbit", "raccoon", "race", "rack", "radar", "radio", "rail", "rain", "raise", "rally", "ramp", "ranch", "random", "range", "rapid", "rare", "rate", "rather", "raven", "raw", "razor", "ready", "real", "reason", "rebel", "rebuild", "recall", "receive", "recipe", "record", "recycle", "reduce", "reflect", "reform", "refuse", "region", "regret", "regular", "reject", "relax", "release", "relief", "rely", "remain", "remember", "remind", "remove", "render", "renew", "rent", "reopen", "repair", "repeat", "replace", "report", "require", "rescue", "resemble", "resist", "resource", "response", "result", "retire", "retreat", "return", "reunion", "reveal", "review", "reward", "rhythm", "rib", "ribbon", "rice", "rich", "ride", "ridge", "rifle", "right", "rigid", "ring", "riot", "ripple", "risk", "ritual", "rival", "river", "road", "roast", "robot", "robust", "rocket", "romance", "roof", "rookie", "room", "rose", "rotate", "rough", "round", "route", "royal", "rubber", "rude", "rug", "rule", "run", "runway", "rural", "sad", "saddle", "sadness", "safe", "sail", "salad", "salmon", "salon", "salt", "salute", "same", "sample", "sand", "satisfy", "satoshi", "sauce", "sausage", "save", "say", "scale", "scan", "scare", "scatter", "scene", "scheme", "school", "science", "scissors", "scorpion", "scout", "scrap", "screen", "script", "scrub", "sea", "search", "season", "seat", "second", "secret", "section", "security", "seed", "seek", "segment", "select", "sell", "seminar", "senior", "sense", "sentence", "series", "service", "session", "settle", "setup", "seven", "shadow", "shaft", "shallow", "share", "shed", "shell", "sheriff", "shield", "shift", "shine", "ship", "shiver", "shock", "shoe", "shoot", "shop", "short", "shoulder", "shove", "shrimp", "shrug", "shuffle", "shy", "sibling", "sick", "side", "siege", "sight", "sign", "silent", "silk", "silly", "silver", "similar", "simple", "since", "sing", "siren", "sister", "situate", "six", "size", "skate", "sketch", "ski", "skill", "skin", "skirt", "skull", "slab", "slam", "sleep", "slender", "slice", "slide", "slight", "slim", "slogan", "slot", "slow", "slush", "small", "smart", "smile", "smoke", "smooth", "snack", "snake", "snap", "sniff", "snow", "soap", "soccer", "social", "sock", "soda", "soft", "solar", "soldier", "solid", "solution", "solve", "someone", "song", "soon", "sorry", "sort", "soul", "sound", "soup", "source", "south", "space", "spare", "spatial", "spawn", "speak", "special", "speed", "spell", "spend", "sphere", "spice", "spider", "spike", "spin", "spirit", "split", "spoil", "sponsor", "spoon", "sport", "spot", "spray", "spread", "spring", "spy", "square", "squeeze", "squirrel", "stable", "stadium", "staff", "stage", "stairs", "stamp", "stand", "start", "state", "stay", "steak", "steel", "stem", "step", "stereo", "stick", "still", "sting", "stock", "stomach", "stone", "stool", "story", "stove", "strategy", "street", "strike", "strong", "struggle", "student", "stuff", "stumble", "style", "subject", "submit", "subway", "success", "such", "sudden", "suffer", "sugar", "suggest", "suit", "summer", "sun", "sunny", "sunset", "super", "supply", "supreme", "sure", "surface", "surge", "surprise", "surround", "survey", "suspect", "sustain", "swallow", "swamp", "swap", "swarm", "swear", "sweet", "swift", "swim", "swing", "switch", "sword", "symbol", "symptom", "syrup", "system", "table", "tackle", "tag", "tail", "talent", "talk", "tank", "tape", "target", "task", "taste", "tattoo", "taxi", "teach", "team", "tell", "ten", "tenant", "tennis", "tent", "term", "test", "text", "thank", "that", "theme", "then", "theory", "there", "they", "thing", "this", "thought", "three", "thrive", "throw", "thumb", "thunder", "ticket", "tide", "tiger", "tilt", "timber", "time", "tiny", "tip", "tired", "tissue", "title", "toast", "tobacco", "today", "toddler", "toe", "together", "toilet", "token", "tomato", "tomorrow", "tone", "tongue", "tonight", "tool", "tooth", "top", "topic", "topple", "torch", "tornado", "tortoise", "toss", "total", "tourist", "toward", "tower", "town", "toy", "track", "trade", "traffic", "tragic", "train", "transfer", "trap", "trash", "travel", "tray", "treat", "tree", "trend", "trial", "tribe", "trick", "trigger", "trim", "trip", "trophy", "trouble", "truck", "true", "truly", "trumpet", "trust", "truth", "try", "tube", "tuition", "tumble", "tuna", "tunnel", "turkey", "turn", "turtle", "twelve", "twenty", "twice", "twin", "twist", "two", "type", "typical", "ugly", "umbrella", "unable", "unaware", "uncle", "uncover", "under", "undo", "unfair", "unfold", "unhappy", "uniform", "unique", "unit", "universe", "unknown", "unlock", "until", "unusual", "unveil", "update", "upgrade", "uphold", "upon", "upper", "upset", "urban", "urge", "usage", "use", "used", "useful", "useless", "usual", "utility", "vacant", "vacuum", "vague", "valid", "valley", "valve", "van", "vanish", "vapor", "various", "vast", "vault", "vehicle", "velvet", "vendor", "venture", "venue", "verb", "verify", "version", "very", "vessel", "veteran", "viable", "vibrant", "vicious", "victory", "video", "view", "village", "vintage", "violin", "virtual", "virus", "visa", "visit", "visual", "vital", "vivid", "vocal", "voice", "void", "volcano", "volume", "vote", "voyage", "wage", "wagon", "wait", "walk", "wall", "walnut", "want", "warfare", "warm", "warrior", "wash", "wasp", "waste", "water", "wave", "way", "wealth", "weapon", "wear", "weasel", "weather", "web", "wedding", "weekend", "weird", "welcome", "west", "wet", "whale", "what", "wheat", "wheel", "when", "where", "whip", "whisper", "wide", "width", "wife", "wild", "will", "win", "window", "wine", "wing", "wink", "winner", "winter", "wire", "wisdom", "wise", "wish", "witness", "wolf", "woman", "wonder", "wood", "wool", "word", "work", "world", "worry", "worth", "wrap", "wreck", "wrestle", "wrist", "write", "wrong", "yard", "year", "yellow", "you", "young", "youth", "zebra", "zero", "zone", "zoo"]; +module.exports = BIP39WordlistEn; + +},{}],"./lib/Base58":[function(require,module,exports){ +module.exports=require('6VqyzY'); +},{}],"6VqyzY":[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":61,"buffer":95,"crypto":99}],"./lib/Block":[function(require,module,exports){ +module.exports=require('pJEQEB'); +},{}],"pJEQEB":[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":191,"../util/error":190,"./Script":"hQ0t76","./Transaction":"LJhYtm","bignum":61,"binary":83,"buffer":95,"buffertools":"fugeBw","step":178}],"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'); +},{}],"./lib/Connection":[function(require,module,exports){ +module.exports=require('DB/p3X'); +},{}],"DB/p3X":[function(require,module,exports){ +(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":191,"../util/BinaryParser":"b3ZSD7","../util/log":"AdF7pF","./Block":"pJEQEB","./SecureRandom":"p4SiC2","./Transaction":"LJhYtm","buffer":95,"bufferput":"aXRuS6","buffers":"OBo3aV","buffertools":"fugeBw","events":"T9Wsc/","socks5-client":172,"util":128}],"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 (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":191,"./Key":"ALJ4PS","./Point":"6tXgqr","bignum":61,"buffer":95,"buffertools":"fugeBw"}],"./lib/HierarchicalKey":[function(require,module,exports){ +module.exports=require('x1O6JW'); +},{}],"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":191,"./Base58":"6VqyzY","./Key":"ALJ4PS","./Point":"6tXgqr","./SecureRandom":"p4SiC2","bignum":61,"buffer":95}],"CBDCgz":[function(require,module,exports){ +(function (Buffer){ +'use strict'; +var coinUtil = require('../util'); +var Key = require('./Key'); + +var Message = function() {}; + +Message.sign = function(str, key) { + var hash = Message.magicHash(str); + var sig = key.signSync(hash); + return sig; +}; + +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); +}; + +//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":191,"./Key":"ALJ4PS","buffer":95}],"./lib/Message":[function(require,module,exports){ +module.exports=require('CBDCgz'); +},{}],"./lib/NetworkMonitor":[function(require,module,exports){ +module.exports=require('qYkfjX'); +},{}],"qYkfjX":[function(require,module,exports){ +var log = require('../util/log'); +var networks = require('../networks'); +var Address = require('./Address'); +var Peer = require('./Peer'); +var PeerManager = require('./PeerManager'); +var util = require('util'); +var EventEmitter = require('events').EventEmitter; +var preconditions = require('preconditions').singleton(); + +var NetworkMonitor = function(peerman) { + preconditions.checkArgument(peerman); + this.peerman = peerman; + this.networkName = peerman.config.network; + this.init(); +} + +util.inherits(NetworkMonitor, EventEmitter); + +NetworkMonitor.create = function(config) { + this.config = config; + var peerman = new PeerManager({ + network: config.networkName + }); + + peerman.addPeer(new Peer(config.host, config.port)); + return new NetworkMonitor(peerman); +}; + +NetworkMonitor.prototype.init = function() { + var self = this; + var handleInv = function(info) { + var invs = info.message.invs; + info.conn.sendGetData(invs); + }; + + var handleBlock = function(info) { + self.emit('block', info.message); + }; + + var handleTx = function(info) { + var tx = info.message.tx; + self.emit('tx', tx); + + var from = tx.getSendingAddresses(self.networkName); + for (var i = 0; i < from.length; i++) { + var addr = from[i]; + self.emit('out:'+addr, tx); + } + var to = tx.getReceivingAddresses(self.networkName); + for (var i = 0; i < to.length; i++) { + var addr = to[i]; + self.emit('in:'+addr, tx); + } + }; + + this.peerman.on('connection', function(conn) { + if (self.connection) throw new Error('Cant handle more than one connection'); + self.connection = conn; + conn.on('inv', handleInv); + conn.on('block', handleBlock); + conn.on('tx', handleTx); + }); +}; + +NetworkMonitor.prototype.incoming = function(addrStr, callback) { + preconditions.checkArgument(Address.validate(addrStr)); + this.on('in:'+addrStr, callback); +}; + +NetworkMonitor.prototype.outgoing = function(addrStr, callback) { + preconditions.checkArgument(Address.validate(addrStr)); + this.on('out:'+addrStr, callback); +}; + +NetworkMonitor.prototype.start = function() { + this.peerman.start(); +}; + +NetworkMonitor.prototype.stop = function() { + this.peerman.stop(); +}; + +module.exports = NetworkMonitor; + +},{"../networks":"ULNIu2","../util/log":"AdF7pF","./Address":"G+CcXD","./Peer":"oolY81","./PeerManager":"nsqKeP","events":"T9Wsc/","preconditions":163,"util":128}],"Zm7/h9":[function(require,module,exports){ +function Opcode(num) { + this.code = num; +}; + +Opcode.prototype.toString = function() { + return Opcode.reverseMap[this.code]; +}; + +Opcode.map = { + // push value + OP_FALSE: 0, + OP_0: 0, + OP_PUSHDATA1: 76, + OP_PUSHDATA2: 77, + OP_PUSHDATA4: 78, + OP_1NEGATE: 79, + OP_RESERVED: 80, + OP_TRUE: 81, + OP_1: 81, + OP_2: 82, + OP_3: 83, + OP_4: 84, + OP_5: 85, + OP_6: 86, + OP_7: 87, + OP_8: 88, + OP_9: 89, + OP_10: 90, + OP_11: 91, + OP_12: 92, + OP_13: 93, + OP_14: 94, + OP_15: 95, + OP_16: 96, + + // control + OP_NOP: 97, + OP_VER: 98, + OP_IF: 99, + OP_NOTIF: 100, + OP_VERIF: 101, + OP_VERNOTIF: 102, + OP_ELSE: 103, + OP_ENDIF: 104, + OP_VERIFY: 105, + OP_RETURN: 106, + + // stack ops + OP_TOALTSTACK: 107, + OP_FROMALTSTACK: 108, + OP_2DROP: 109, + OP_2DUP: 110, + OP_3DUP: 111, + OP_2OVER: 112, + OP_2ROT: 113, + OP_2SWAP: 114, + OP_IFDUP: 115, + OP_DEPTH: 116, + OP_DROP: 117, + OP_DUP: 118, + OP_NIP: 119, + OP_OVER: 120, + OP_PICK: 121, + OP_ROLL: 122, + OP_ROT: 123, + OP_SWAP: 124, + OP_TUCK: 125, + + // splice ops + OP_CAT: 126, + OP_SUBSTR: 127, + OP_LEFT: 128, + OP_RIGHT: 129, + OP_SIZE: 130, + + // bit logic + OP_INVERT: 131, + OP_AND: 132, + OP_OR: 133, + OP_XOR: 134, + OP_EQUAL: 135, + OP_EQUALVERIFY: 136, + OP_RESERVED1: 137, + OP_RESERVED2: 138, + + // numeric + OP_1ADD: 139, + OP_1SUB: 140, + OP_2MUL: 141, + OP_2DIV: 142, + OP_NEGATE: 143, + OP_ABS: 144, + OP_NOT: 145, + OP_0NOTEQUAL: 146, + + OP_ADD: 147, + OP_SUB: 148, + OP_MUL: 149, + OP_DIV: 150, + OP_MOD: 151, + OP_LSHIFT: 152, + OP_RSHIFT: 153, + + OP_BOOLAND: 154, + OP_BOOLOR: 155, + OP_NUMEQUAL: 156, + OP_NUMEQUALVERIFY: 157, + OP_NUMNOTEQUAL: 158, + OP_LESSTHAN: 159, + OP_GREATERTHAN: 160, + OP_LESSTHANOREQUAL: 161, + OP_GREATERTHANOREQUAL: 162, + OP_MIN: 163, + OP_MAX: 164, + + OP_WITHIN: 165, + + // crypto + OP_RIPEMD160: 166, + OP_SHA1: 167, + OP_SHA256: 168, + OP_HASH160: 169, + OP_HASH256: 170, + OP_CODESEPARATOR: 171, + OP_CHECKSIG: 172, + OP_CHECKSIGVERIFY: 173, + OP_CHECKMULTISIG: 174, + OP_CHECKMULTISIGVERIFY: 175, + + // expansion + OP_NOP1: 176, + OP_NOP2: 177, + OP_NOP3: 178, + OP_NOP4: 179, + OP_NOP5: 180, + OP_NOP6: 181, + OP_NOP7: 182, + OP_NOP8: 183, + OP_NOP9: 184, + OP_NOP10: 185, + + // template matching params + OP_PUBKEYHASH: 253, + OP_PUBKEY: 254, + OP_INVALIDOPCODE: 255 +}; + +Opcode.reverseMap = []; + +for (var k in Opcode.map) { + if (Opcode.map.hasOwnProperty(k)) { + Opcode.reverseMap[Opcode.map[k]] = k.substr(3); + } +} + +Opcode.asList = function() { + var keys = []; + for (var prop in Opcode.map) { + if (Opcode.map.hasOwnProperty(prop)) { + keys.push(prop); + } + } + return keys; +}; + +module.exports = Opcode; + +},{}],"./lib/Opcode":[function(require,module,exports){ +module.exports=require('Zm7/h9'); +},{}],"./lib/Peer":[function(require,module,exports){ +module.exports=require('oolY81'); +},{}],"oolY81":[function(require,module,exports){ +(function (Buffer){ +var Net = require('net'); +var Binary = require('binary'); +var buffertools = require('buffertools'); + +function Peer(host, port, services) { + if ("string" === typeof host) { + if (host.indexOf(':') && !port) { + var parts = host.split(':'); + host = parts[0]; + port = parts[1]; + } + this.host = host; + this.port = +port || 8333; + } else if (host instanceof Peer) { + this.host = host.host; + this.port = host.port; + } else if (Buffer.isBuffer(host)) { + if (buffertools.compare(Peer.IPV6_IPV4_PADDING, host.slice(0, 12)) != 0) { + throw new Error('IPV6 not supported yet! Cannot instantiate host.'); + } + this.host = Array.prototype.slice.apply(host.slice(12)).join('.'); + this.port = +port || 8333; + } else { + throw new Error('Could not instantiate peer, invalid parameter type: ' + + typeof host); + } + + this.services = (services) ? services : null; + this.lastSeen = 0; +}; + +Peer.IPV6_IPV4_PADDING = new Buffer([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255]); + +Peer.prototype.createConnection = function() { + this.connection = Net.createConnection(this.port, this.host); + return this.connection; +}; + +Peer.prototype.getHostAsBuffer = function() { + return new Buffer(this.host.split('.')); +}; + +Peer.prototype.toString = function() { + return this.host + ":" + this.port; +}; + +Peer.prototype.toBuffer = function() { + var put = Binary.put(); + put.word32le(this.lastSeen); + put.word64le(this.services); + put.put(this.getHostAsBuffer()); + put.word16be(this.port); + return put.buffer(); +}; + +module.exports = Peer; + +}).call(this,require("buffer").Buffer) +},{"binary":83,"buffer":95,"buffertools":"fugeBw","net":91}],"./lib/PeerManager":[function(require,module,exports){ +module.exports=require('nsqKeP'); +},{}],"nsqKeP":[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":82,"dns":91,"events":"T9Wsc/","util":128}],"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":95,"util":128}],"./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 (Buffer){ +// RpcClient.js +// MIT/X11-like license. See LICENSE.txt. +// Copyright 2013 BitPay, Inc. +// +var http = require('http'); +var https = require('https'); +var log = require('../util/log'); + +function RpcClient(opts) { + opts = opts || {}; + this.host = opts.host || '127.0.0.1'; + this.port = opts.port || 8332; + this.user = opts.user || 'user'; + this.pass = opts.pass || 'pass'; + this.protocol = (opts.protocol == 'http') ? http : https; + this.batchedCalls = null; + this.disableAgent = opts.disableAgent || false; +} + +RpcClient.prototype.batch = function(batchCallback, resultCallback) { + this.batchedCalls = []; + batchCallback(); + rpc.call(this, this.batchedCalls, resultCallback); + this.batchedCalls = null; +} + +var callspec = { + addMultiSigAddress: '', + addNode: '', + backupWallet: '', + createMultiSig: '', + createRawTransaction: '', + decodeRawTransaction: '', + dumpPrivKey: '', + encryptWallet: '', + getAccount: '', + getAccountAddress: 'str', + getAddedNodeInfo: '', + getAddressesByAccount: '', + getBalance: 'str int', + getBestBlockHash: '', + getBlock: '', + getBlockCount: '', + getBlockHash: 'int', + getBlockNumber: '', + getBlockTemplate: '', + getConnectionCount: '', + getDifficulty: '', + getGenerate: '', + getHashesPerSec: '', + getInfo: '', + getMemoryPool: '', + getMiningInfo: '', + getNewAddress: '', + getPeerInfo: '', + getRawMemPool: '', + getRawTransaction: 'str int', + getReceivedByAccount: 'str int', + getReceivedByAddress: 'str int', + getTransaction: '', + getTxOut: 'str int bool', + getTxOutSetInfo: '', + getWork: '', + help: '', + importAddress: 'str str bool', + importPrivKey: 'str str bool', + keyPoolRefill: '', + listAccounts: 'int', + listAddressGroupings: '', + listReceivedByAccount: 'int bool', + listReceivedByAddress: 'int bool', + listSinceBlock: 'str int', + listTransactions: 'str int int', + listUnspent: 'int int', + listLockUnspent: 'bool', + lockUnspent: '', + move: 'str str float int str', + sendFrom: 'str str float int str str', + sendMany: 'str str int str', //not sure this is will work + sendRawTransaction: '', + sendToAddress: 'str float str str', + setAccount: '', + setGenerate: 'bool int', + setTxFee: 'float', + signMessage: '', + signRawTransaction: '', + stop: '', + submitBlock: '', + validateAddress: '', + verifyMessage: '', + walletLock: '', + walletPassPhrase: 'string int', + walletPassphraseChange: '', +}; + +var slice = function(arr, start, end) { + return Array.prototype.slice.call(arr, start, end); +}; + +function generateRPCMethods(constructor, apiCalls, rpc) { + function createRPCMethod(methodName, argMap) { + return function() { + var limit = arguments.length - 1; + if (this.batchedCalls) var limit = arguments.length; + for (var i = 0; i < limit; i++) { + if (argMap[i]) arguments[i] = argMap[i](arguments[i]); + }; + if (this.batchedCalls) { + this.batchedCalls.push({ + jsonrpc: '2.0', + method: methodName, + params: slice(arguments) + }); + } else { + rpc.call(this, { + method: methodName, + params: slice(arguments, 0, arguments.length - 1) + }, arguments[arguments.length - 1]); + } + }; + }; + + var types = { + str: function(arg) { + return arg.toString(); + }, + int: function(arg) { + return parseFloat(arg); + }, + float: function(arg) { + return parseFloat(arg); + }, + bool: function(arg) { + return (arg === true || arg == '1' || arg == 'true' || arg.toString().toLowerCase() == 'true'); + }, + }; + + for (var k in apiCalls) { + if (apiCalls.hasOwnProperty(k)) { + var spec = apiCalls[k].split(' '); + for (var i = 0; i < spec.length; i++) { + if (types[spec[i]]) { + spec[i] = types[spec[i]]; + } else { + spec[i] = types.string; + } + } + var methodName = k.toLowerCase(); + constructor.prototype[k] = createRPCMethod(methodName, spec); + constructor.prototype[methodName] = constructor.prototype[k]; + } + } +} + +function rpc(request, callback) { + var self = this; + var request; + request = JSON.stringify(request); + var auth = Buffer(self.user + ':' + self.pass).toString('base64'); + + var options = { + host: self.host, + path: '/', + method: 'POST', + port: self.port, + agent: self.disableAgent ? false : undefined, + }; + if (self.httpOptions) { + for (var k in self.httpOptions) { + options[k] = self.httpOptions[k]; + } + } + var err = null; + var req = this.protocol.request(options, function(res) { + + var buf = ''; + res.on('data', function(data) { + buf += data; + }); + res.on('end', function() { + if (res.statusCode == 401) { + callback(new Error('bitcoin JSON-RPC connection rejected: 401 unauthorized')); + return; + } + if (res.statusCode == 403) { + callback(new Error('bitcoin JSON-RPC connection rejected: 403 forbidden')); + return; + } + + if (err) { + callback(err); + return; + } + try { + var parsedBuf = JSON.parse(buf); + } catch (e) { + log.err(e.stack); + log.err(buf); + log.err('HTTP Status code:' + res.statusCode); + callback(e); + return; + } + callback(parsedBuf.error, parsedBuf); + }); + }); + req.on('error', function(e) { + var err = new Error('Could not connect to bitcoin via RPC: ' + e.message); + log.err(err); + callback(err); + }); + + req.setHeader('Content-Length', request.length); + req.setHeader('Content-Type', 'application/json'); + req.setHeader('Authorization', 'Basic ' + auth); + req.write(request); + req.end(); +}; + +generateRPCMethods(RpcClient, callspec, rpc); + +module.exports = RpcClient; + +}).call(this,require("buffer").Buffer) +},{"../util/log":"AdF7pF","buffer":95,"http":106,"https":110}],"./lib/SIN":[function(require,module,exports){ +module.exports=require('tBM27q'); +},{}],"tBM27q":[function(require,module,exports){ +(function (Buffer){ +'use strict'; +var VersionedData = require('../util/VersionedData'); +var EncodedData = require('../util/EncodedData'); +var util = require('util'); +var coinUtil = require('../util'); + +function SIN(type, payload) { + if (typeof type != 'number') { + SIN.super_.call(this, type, payload); + return; + } + if (!Buffer.isBuffer(payload) || payload.length != 20) + throw new Error('Payload must be 20 bytes'); + + this.data = new Buffer(1 + 1 + payload.length); + this.converters = this.encodings['binary'].converters; + this._encoding = this.encodings['binary']._encoding; + this.encoding('binary'); + this.prefix(0x0F); // SIN magic number, in numberspace + this.type(type); + this.payload(payload); +}; + +util.inherits(SIN, VersionedData); +EncodedData.applyEncodingsTo(SIN); + +SIN.SIN_PERSIST_MAINNET = 0x01; // associated with sacrifice TX +SIN.SIN_PERSIST_TESTNET = 0x11; // associated with sacrifice TX +SIN.SIN_EPHEM = 0x02; // generate off-net at any time + +// get or set the prefix data (the first byte of the address) +SIN.prototype.prefix = function(num) { + if (num || (num === 0)) { + this.doAsBinary(function() { + this.data.writeUInt8(num, 0); + }); + return num; + } + return this.as('binary').readUInt8(0); +}; + +// get or set the SIN-type data (the second byte of the address) +SIN.prototype.type = function(num) { + if (num || (num === 0)) { + this.doAsBinary(function() { + this.data.writeUInt8(num, 1); + }); + return num; + } + return this.as('binary').readUInt8(1); +}; + +// get or set the payload data (as a Buffer object) +SIN.prototype.payload = function(data) { + if (data) { + this.doAsBinary(function() { + data.copy(this.data, 2); + }); + return data; + } + return this.as('binary').slice(1); +}; + +SIN.prototype.validate = function() { + this.doAsBinary(function() { + SIN.super_.prototype.validate.call(this); + if (this.data.length != 22) throw new Error('invalid data length'); + }); +}; + + +// create a SIN from a public key +SIN.fromPubKey = function(pubKey, type) { + if (!type) + type = SIN.SIN_EPHEM; + + if (!Buffer.isBuffer(pubKey) || (pubKey.length !== 33 && pubKey.length != 65)) + throw new Error('Invalid public key'); + + var hash = coinUtil.sha256ripe160(pubKey); + return new SIN(hash, type); +}; + +module.exports = SIN; + +}).call(this,require("buffer").Buffer) +},{"../util":191,"../util/EncodedData":"eLfUFE","../util/VersionedData":"QLzNQg","buffer":95,"util":128}],"EyghZQ":[function(require,module,exports){ +var coinUtil = require('../util'); +var timeUtil = require('../util/time'); +var Key = require('./Key'); +var SIN = require('./SIN'); + +function SINKey(cfg) { + if (typeof cfg != 'object') + cfg = {}; + + this.created = cfg.created; + this.privKey = cfg.privKey; +}; + +SINKey.prototype.generate = function() { + this.privKey = Key.generateSync(); + this.created = timeUtil.curtime(); +}; + +SINKey.prototype.pubkeyHash = function() { + return coinUtil.sha256ripe160(this.privKey.public); +}; + +SINKey.prototype.storeObj = function() { + var pubKey = this.privKey.public.toString('hex'); + var pubKeyHash = this.pubkeyHash(); + var sin = new SIN(SIN.SIN_EPHEM, pubKeyHash); + var obj = { + created: this.created, + priv: this.privKey.private.toString('hex'), + pub: pubKey, + sin: sin.toString(), + }; + + return obj; +}; + +module.exports = SINKey; + +},{"../util":191,"../util/time":194,"./Key":"ALJ4PS","./SIN":"tBM27q"}],"./lib/SINKey":[function(require,module,exports){ +module.exports=require('EyghZQ'); +},{}],"./lib/Script":[function(require,module,exports){ +module.exports=require('hQ0t76'); +},{}],"hQ0t76":[function(require,module,exports){ +(function (Buffer){ +var config = require('../config'); +var log = require('../util/log'); +var Opcode = require('./Opcode'); +var buffertools = require('buffertools'); +var util = require('../util/util'); +var Parser = require('../util/BinaryParser'); +var Put = require('bufferput'); + +var TX_UNKNOWN = 0; +var TX_PUBKEY = 1; +var TX_PUBKEYHASH = 2; +var TX_MULTISIG = 3; +var TX_SCRIPTHASH = 4; + +var TX_TYPES = [ + 'unknown', + 'pubkey', + 'pubkeyhash', + 'multisig', + 'scripthash' +]; + +function Script(buffer) { + if (buffer) { + this.buffer = buffer; + } else { + this.buffer = util.EMPTY_BUFFER; + } + this.chunks = []; + this.parse(); +} + +Script.TX_UNKNOWN = TX_UNKNOWN; +Script.TX_PUBKEY = TX_PUBKEY; +Script.TX_PUBKEYHASH = TX_PUBKEYHASH; +Script.TX_MULTISIG = TX_MULTISIG; +Script.TX_SCRIPTHASH = TX_SCRIPTHASH; + +Script.prototype.parse = function() { + this.chunks = []; + + var parser = new Parser(this.buffer); + while (!parser.eof()) { + var opcode = parser.word8(); + + var len, chunk; + if (opcode > 0 && opcode < Opcode.map.OP_PUSHDATA1) { + // Read some bytes of data, opcode value is the length of data + this.chunks.push(parser.buffer(opcode)); + } else if (opcode === Opcode.map.OP_PUSHDATA1) { + len = parser.word8(); + chunk = parser.buffer(len); + this.chunks.push(chunk); + } else if (opcode === Opcode.map.OP_PUSHDATA2) { + len = parser.word16le(); + chunk = parser.buffer(len); + this.chunks.push(chunk); + } else if (opcode === Opcode.map.OP_PUSHDATA4) { + len = parser.word32le(); + chunk = parser.buffer(len); + this.chunks.push(chunk); + } else { + this.chunks.push(opcode); + } + } +}; + +Script.prototype.isPushOnly = function() { + for (var i = 0; i < this.chunks.length; i++) { + var op = this.chunks[i]; + if (!Buffer.isBuffer(op) && op > Opcode.map.OP_16) { + return false; + } + } + + return true; +}; + +Script.prototype.isP2SH = function() { + return (this.chunks.length == 3 && + this.chunks[0] == Opcode.map.OP_HASH160 && + Buffer.isBuffer(this.chunks[1]) && + this.chunks[1].length == 20 && + this.chunks[2] == Opcode.map.OP_EQUAL); +}; + +Script.prototype.isPubkey = function() { + return (this.chunks.length == 2 && + Buffer.isBuffer(this.chunks[0]) && + this.chunks[1] == Opcode.map.OP_CHECKSIG); +}; + +Script.prototype.isPubkeyHash = function() { + return (this.chunks.length == 5 && + this.chunks[0] == Opcode.map.OP_DUP && + this.chunks[1] == Opcode.map.OP_HASH160 && + Buffer.isBuffer(this.chunks[2]) && + this.chunks[2].length == 20 && + this.chunks[3] == Opcode.map.OP_EQUALVERIFY && + this.chunks[4] == Opcode.map.OP_CHECKSIG); +}; + +function isSmallIntOp(opcode) { + return ((opcode == Opcode.map.OP_0) || + ((opcode >= Opcode.map.OP_1) && (opcode <= Opcode.map.OP_16))); +}; + +Script.prototype.isMultiSig = function() { + return (this.chunks.length > 3 && + isSmallIntOp(this.chunks[0]) && + this.chunks.slice(1, this.chunks.length - 2).every(function(i) { + return Buffer.isBuffer(i); + }) && + isSmallIntOp(this.chunks[this.chunks.length - 2]) && + this.chunks[this.chunks.length - 1] == Opcode.map.OP_CHECKMULTISIG); +}; + +Script.prototype.isPubkeyHashScriptSig = function() { + return (this.chunks.length == 2 && + Buffer.isBuffer(this.chunks[0]) && + Buffer.isBuffer(this.chunks[1])); +}; + +Script.prototype.isP2shScriptSig = function() { + if (!isSmallIntOp(this.chunks[0]) || this.chunks[0] !== 0) + return false; + + var redeemScript = new Script(this.chunks[this.chunks.length - 1]); + var type = redeemScript.classify(); + return type !== TX_UNKNOWN; +}; + +Script.prototype.isMultiSigScriptSig = function() { + if (!isSmallIntOp(this.chunks[0]) || this.chunks[0] !== 0) + return false; + return !this.isP2shScriptSig(); +}; +Script.prototype.isPubkeyScriptSig = function() { + return (this.chunks.length == 1 && + Buffer.isBuffer(this.chunks[0])); +}; + +Script.prototype.countSignatures = function() { + var ret = 0; + var l = this.chunks.length; + // Multisig? + if (this.isMultiSigScriptSig()) { + ret = l - 1; + } + // p2sh + else if (this.isP2shScriptSig()) { + ret = l - 2; + } + // p2pubkeyhash + else if (this.isPubkeyHashScriptSig()) { + ret = 1; + } + // p2pubkey + else { + ret = 0; + } + return ret; +}; + + +Script.prototype.getSignatures = function() { + ret = []; + var l = this.chunks.length; + // Multisig? + if (this.isMultiSigScriptSig()) { + for(var i = 1; i marked 0 + redeemScript + } + } + // p2pubkey or p2pubkeyhash + else { + if (buffertools.compare(this.getBuffer(), util.EMPTY_BUFFER) === 0) { + ret = 1; + } + } + return ret; +}; + +Script.prototype.finishedMultiSig = function() { + var missing = this.countMissingSignatures(); + if (missing === null) return null; + + return missing === 0; +}; + +Script.prototype.getMultiSigInfo = function() { + if (!this.isMultiSig()) { + throw new Error("Script.getMultiSigInfo(): Not a multiSig script."); + } + + var nsigs = this.chunks[0] - 80; //see OP_2-OP_16; + var npubkeys = this.chunks[this.chunks.length - 2] - 80; //see OP_2-OP_16; + + var pubkeys = []; + for (var i = 1; i < this.chunks.length - 2; i++) { + pubkeys.push(this.chunks[i]); + } + + if (pubkeys.length != npubkeys) { + throw new Error("Script.getMultiSigInfo(): Amount of PKs does not match what the script specifies."); + } + + return { + nsigs: nsigs, + npubkeys: npubkeys, + pubkeys: pubkeys + } +}; + +Script.prototype.prependOp0 = function() { + var chunks = [0]; + for (i in this.chunks) { + if (this.chunks.hasOwnProperty(i)) { + chunks.push(this.chunks[i]); + } + } + this.chunks = chunks; + this.updateBuffer(); + return this; +}; + +// is this a script form we know? +Script.prototype.classify = function() { + if (this.isPubkeyHash()) + return TX_PUBKEYHASH; + if (this.isP2SH()) + return TX_SCRIPTHASH; + if (this.isMultiSig()) + return TX_MULTISIG; + if (this.isPubkey()) + return TX_PUBKEY; + return TX_UNKNOWN; +}; + +// extract useful data items from known scripts +Script.prototype.capture = function() { + var txType = this.classify(); + var res = []; + switch (txType) { + case TX_PUBKEY: + res.push(this.chunks[0]); + break; + case TX_PUBKEYHASH: + res.push(this.chunks[2]); + break; + case TX_MULTISIG: + for (var i = 1; i < (this.chunks.length - 2); i++) + res.push(this.chunks[i]); + break; + case TX_SCRIPTHASH: + res.push(this.chunks[1]); + break; + + case TX_UNKNOWN: + default: + // do nothing + break; + } + + return res; +}; + +// return first extracted data item from script +Script.prototype.captureOne = function() { + var arr = this.capture(); + return arr[0]; +}; + +Script.prototype.getOutType = function() { + var txType = this.classify(); + switch (txType) { + case TX_PUBKEY: + return 'Pubkey'; + case TX_PUBKEYHASH: + return 'Address'; + default: + return 'Strange'; + } +}; + +Script.prototype.getRawOutType = function() { + return TX_TYPES[this.classify()]; +}; + +Script.prototype.simpleOutHash = function() { + switch (this.getOutType()) { + case 'Address': + return this.chunks[2]; + case 'Pubkey': + return util.sha256ripe160(this.chunks[0]); + default: + log.debug("Encountered non-standard scriptPubKey"); + log.debug("Strange script was: " + this.toString()); + return null; + } +}; + +Script.prototype.getInType = function() { + if (this.chunks.length == 1) { + // Direct IP to IP transactions only have the public key in their scriptSig. + return 'Pubkey'; + } else if (this.chunks.length == 2 && + Buffer.isBuffer(this.chunks[0]) && + Buffer.isBuffer(this.chunks[1])) { + return 'Address'; + } else { + return 'Strange'; + } +}; + +Script.prototype.simpleInPubKey = function() { + switch (this.getInType()) { + case 'Address': + return this.chunks[1]; + case 'Pubkey': + return null; + default: + log.debug("Encountered non-standard scriptSig"); + log.debug("Strange script was: " + this.toString()); + return null; + } +}; + +Script.prototype.getBuffer = function() { + return this.buffer; +}; + +Script.prototype.serialize = Script.prototype.getBuffer; + +Script.prototype.getStringContent = function(truncate, maxEl) { + if (truncate === null) { + truncate = true; + } + + if ('undefined' === typeof maxEl) { + maxEl = 15; + } + + var s = ''; + for (var i = 0, l = this.chunks.length; i < l; i++) { + var chunk = this.chunks[i]; + + if (i > 0) { + s += ' '; + } + + if (Buffer.isBuffer(chunk)) { + s += '0x' + util.formatBuffer(chunk, truncate ? null : 0); + } else { + s += Opcode.reverseMap[chunk]; + } + + if (maxEl && i > maxEl) { + s += ' ...'; + break; + } + } + return s; +}; + +Script.prototype.toString = function(truncate, maxEl) { + var script = "