From 57491aaadce2a01d32bbf33bdef4260d3f68341d Mon Sep 17 00:00:00 2001 From: Christopher Jeffrey Date: Wed, 9 Dec 2015 16:12:52 -0800 Subject: [PATCH] add network.js and testnet support. see #40. --- lib/bcoin.js | 2 + lib/bcoin/chain.js | 8 +- lib/bcoin/hd.js | 16 ++- lib/bcoin/protocol/constants.js | 23 ----- lib/bcoin/protocol/framer.js | 3 +- lib/bcoin/protocol/index.js | 2 +- lib/bcoin/protocol/network.js | 178 ++++++++++++++++++++++++++++++++ lib/bcoin/protocol/parser.js | 3 +- lib/bcoin/utils.js | 40 ++++++- lib/bcoin/wallet.js | 11 +- 10 files changed, 242 insertions(+), 44 deletions(-) create mode 100644 lib/bcoin/protocol/network.js diff --git a/lib/bcoin.js b/lib/bcoin.js index a638a1af..c80cb2e4 100644 --- a/lib/bcoin.js +++ b/lib/bcoin.js @@ -14,3 +14,5 @@ bcoin.wallet = require('./bcoin/wallet'); bcoin.peer = require('./bcoin/peer'); bcoin.pool = require('./bcoin/pool'); bcoin.hd = require('./bcoin/hd'); + +bcoin.protocol.network.set(process.env.BCOIN_NETWORK || 'main'); diff --git a/lib/bcoin/chain.js b/lib/bcoin/chain.js index 025adeeb..c0937331 100644 --- a/lib/bcoin/chain.js +++ b/lib/bcoin/chain.js @@ -3,7 +3,7 @@ var EventEmitter = require('events').EventEmitter; var bcoin = require('../bcoin'); var constants = bcoin.protocol.constants; -var preload = bcoin.protocol.preload; +var network = bcoin.protocol.network; var utils = bcoin.utils; var assert = utils.assert; @@ -38,6 +38,8 @@ function Chain(options) { }; this.request = new utils.RequestCache(); + var preload = network.preload; + // Start from the genesis block // if we're a full node. if (this.options.fullNode) { @@ -516,6 +518,7 @@ Chain.prototype.toJSON = function toJSON() { return { v: 1, type: 'chain', + network: network.type, hashes: first.hashes.concat(last.hashes), ts: first.ts.concat(last.ts), heights: first.heights.concat(last.heights) @@ -525,6 +528,7 @@ Chain.prototype.toJSON = function toJSON() { Chain.prototype.fromJSON = function fromJSON(json) { assert.equal(json.v, 1); assert.equal(json.type, 'chain'); + assert.equal(json.network, network.type); this.index.hashes = json.hashes.slice(); this.index.ts = json.ts.slice(); this.index.heights = json.heights.slice(); @@ -534,7 +538,7 @@ Chain.prototype.fromJSON = function fromJSON(json) { this.index.bloom = new bcoin.bloom(28 * 1024 * 1024, 16, 0xdeadbeef); if (this.index.hashes.length === 0) - this.add(new bcoin.block(constants.genesis, 'block')); + this.add(new bcoin.block(network.genesis, 'block')); for (var i = 0; i < this.index.hashes.length; i++) { this.index.bloom.add(this.index.hashes[i], 'hex'); diff --git a/lib/bcoin/hd.js b/lib/bcoin/hd.js index 6bd63298..4297e55e 100644 --- a/lib/bcoin/hd.js +++ b/lib/bcoin/hd.js @@ -56,6 +56,7 @@ var bn = require('bn.js'); var elliptic = require('elliptic'); var utils = bcoin.utils; var assert = utils.assert; +var network = bcoin.protocol.network; var EventEmitter = require('events').EventEmitter; @@ -112,11 +113,6 @@ HDSeed._mnemonic = function(entropy) { * HD Keys */ -var VERSION = { - xpubkey: 0x0488b21e, - xprivkey: 0x0488ade4 -}; - var HARDENED = 0x80000000; var MAX_INDEX = 2 * HARDENED; var MIN_ENTROPY = 128 / 8; @@ -155,7 +151,7 @@ function HDPriv(options) { data = options; } - data = this._normalize(data, VERSION.xprivkey); + data = this._normalize(data, network.prefixes.xprivkey); this.data = data; @@ -165,7 +161,7 @@ function HDPriv(options) { HDPriv.prototype._normalize = function(data, version) { var b; - data.version = version || VERSION.xprivkey; + data.version = version || network.prefixes.xprivkey; data.version = +data.version; data.depth = +data.depth; @@ -219,7 +215,7 @@ HDPriv.prototype._seed = function(seed) { var hash = sha512hmac(seed, 'Bitcoin seed'); return { - // version: VERSION.xprivkey, + // version: network.prefixes.xprivkey, depth: 0, parentFingerPrint: 0, childIndex: 0, @@ -413,7 +409,7 @@ function HDPub(options) { else data = options; - data = this._normalize(data, VERSION.xpubkey); + data = this._normalize(data, network.prefixes.xpubkey); this.data = data; @@ -518,7 +514,7 @@ HDPub.prototype.derive = function(index, hard) { var publicKey = bcoin.ecdsa.keyFromPublic(pubkeyPoint).getPublic(true, 'array'); return new HDPub({ - // version: VERSION.xpubkey, + // version: network.prefixes.xpubkey, depth: this.depth + 1, parentFingerPrint: this.fingerPrint, childIndex: index, diff --git a/lib/bcoin/protocol/constants.js b/lib/bcoin/protocol/constants.js index 25075f09..7f9722c1 100644 --- a/lib/bcoin/protocol/constants.js +++ b/lib/bcoin/protocol/constants.js @@ -3,29 +3,6 @@ var utils = bcoin.utils; exports.minVersion = 70001; exports.version = 70002; -exports.magic = 0xd9b4bef9; -exports.genesis = { - version: 1, - prevBlock: [ 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0 ], - merkleRoot: utils.toArray( - '4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b', - 'hex' - ).reverse(), - ts: 1231006505, - bits: 0x1d00ffff, - nonce: 2083236893 -}; - -// address versions -exports.addr = { - normal: 0, - p2pkh: 0, - multisig: 0, - p2sh: 5 -}; // version - services field exports.services = { diff --git a/lib/bcoin/protocol/framer.js b/lib/bcoin/protocol/framer.js index 7dfbb213..12c4d0b7 100644 --- a/lib/bcoin/protocol/framer.js +++ b/lib/bcoin/protocol/framer.js @@ -1,4 +1,5 @@ var bcoin = require('../../bcoin'); +var network = require('./network'); var constants = require('./constants'); var utils = bcoin.utils; var assert = utils.assert; @@ -27,7 +28,7 @@ Framer.prototype.header = function header(cmd, payload) { var h = new Array(24); // Magic value - writeU32(h, constants.magic, 0); + writeU32(h, network.magic, 0); // Command var len = writeAscii(h, cmd, 4); diff --git a/lib/bcoin/protocol/index.js b/lib/bcoin/protocol/index.js index 7002c0fb..954909bd 100644 --- a/lib/bcoin/protocol/index.js +++ b/lib/bcoin/protocol/index.js @@ -3,4 +3,4 @@ var protocol = exports; protocol.constants = require('./constants'); protocol.framer = require('./framer'); protocol.parser = require('./parser'); -protocol.preload = require('./preload'); +protocol.network = require('./network'); diff --git a/lib/bcoin/protocol/network.js b/lib/bcoin/protocol/network.js new file mode 100644 index 00000000..1ac88836 --- /dev/null +++ b/lib/bcoin/protocol/network.js @@ -0,0 +1,178 @@ +var bcoin = require('../../bcoin'); +var utils = bcoin.utils; + +/** + * Network + */ + +var network = exports; + +network.set = function(type) { + var net = network[type]; + utils.merge(network, net); +}; + +/** + * Main + */ + +var main = network.main = {}; + +main.prefixes = { + pubkey: 0, + script: 5, + privkey: 128, + xpubkey: 0x0488b21e, + xprivkey: 0x0488ade4 +}; + +utils.merge(main.prefixes, { + normal: main.prefixes.pubkey, + p2pkh: main.prefixes.pubkey, + multisig: main.prefixes.pubkey, + p2sh: main.prefixes.script +}); + +main.type = 'main'; + +main.seeds = [ + 'seed.bitcoin.sipa.be', // Pieter Wuille + 'dnsseed.bluematt.me', // Matt Corallo + 'dnsseed.bitcoin.dashjr.org', // Luke Dashjr + 'seed.bitcoinstats.com', // Christian Decker + 'bitseed.xf2.org', // Jeff Garzik + 'seed.bitcoin.jonasschnelli.ch' // Jonas Schnelli +]; + +main.port = 8333; + +main.alertKey = utils.toArray('' + + '04fc9702847840aaf195de8442ebecedf5b095c' + + 'dbb9bc716bda9110971b28a49e0ead8564ff0db' + + '22209e0374782c093bb899692d524e9d6a6956e' + + '7c5ecbcd68284', + 'hex'); + +main.checkpoints = [ + { height: 11111, hash: '0000000069e244f73d78e8fd29ba2fd2ed618bd6fa2ee92559f542fdb26e7c1d' }, + { height: 33333, hash: '000000002dd5588a74784eaa7ab0507a18ad16a236e7b1ce69f00d7ddfb5d0a6' }, + { height: 74000, hash: '0000000000573993a3c9e41ce34471c079dcf5f52a0e824a81e7f953b8661a20' }, + { height: 105000, hash: '00000000000291ce28027faea320c8d2b054b2e0fe44a773f3eefb151d6bdc97' }, + { height: 134444, hash: '00000000000005b12ffd4cd315cd34ffd4a594f430ac814c91184a0d42d2b0fe' }, + { height: 168000, hash: '000000000000099e61ea72015e79632f216fe6cb33d7899acb35b75c8303b763' }, + { height: 193000, hash: '000000000000059f452a5f7340de6682a977387c17010ff6e6c3bd83ca8b1317' }, + { height: 210000, hash: '000000000000048b95347e83192f69cf0366076336c639f9b7228e9ba171342e' }, + { height: 216116, hash: '00000000000001b4f4b433e81ee46494af945cf96014816a4e2370f11b23df4e' }, + { height: 225430, hash: '00000000000001c108384350f74090433e7fcf79a606b8e797f065b130575932' }, + { height: 250000, hash: '000000000000003887df1f29024b06fc2200b55f8af8f35453d7be294df2d214' }, + { height: 279000, hash: '0000000000000001ae8c72a0b0c301f67e3afca10e819efa9041e458e9bd7e40' }, + { height: 295000, hash: '00000000000000004d9b4ef50f0f9d686fd69db2e03af35a100370c64632a983' } +]; + +main.checkpoints.tsLastCheckpoint = 1397080064; +main.checkpoints.txsLastCheckpoint = 36544669; +main.checkpoints.txsPerDay = 60000.0; + +// http://blockexplorer.com/b/0 +// http://blockexplorer.com/rawblock/000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f +main.genesis = { + version: 1, + _hash: utils.toArray( + '000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f', + 'hex' + ).reverse(), + prevBlock: [ 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0 ], + merkleRoot: utils.toArray( + '4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b', + 'hex' + ).reverse(), + ts: 1231006505, + bits: 0x1d00ffff, + nonce: 2083236893 +}; + +main.magic = 0xd9b4bef9; + +main.preload = require('./preload'); + +/** + * Testnet (v3) + * https://en.bitcoin.it/wiki/Testnet + */ + +var testnet = network.testnet = {}; + +testnet.type = 'testnet'; + +testnet.prefixes = { + pubkey: 111, + script: 196, + privkey: 239, + xpubkey: 0x043587cf, + xprivkey: 0x04358394 +}; + +utils.merge(testnet.prefixes, { + normal: testnet.prefixes.pubkey, + p2pkh: testnet.prefixes.pubkey, + multisig: testnet.prefixes.pubkey, + p2sh: testnet.prefixes.script +}); + +testnet.seeds = [ + 'testnet-seed.alexykot.me', + 'testnet-seed.bitcoin.petertodd.org', + 'testnet-seed.bluematt.me', + 'testnet-seed.bitcoin.schildbach.de' +]; + +testnet.port = 18333; + +testnet.alertKey = utils.toArray('' + + '04302390343f91cc401d56d68b123028bf52e5f' + + 'ca1939df127f63c6467cdf9c8e2c14b61104cf8' + + '17d0b780da337893ecc4aaff1309e536162dabb' + + 'db45200ca2b0a', + 'hex'); + +testnet.checkpoints = [ + { height: 546, hash: '000000002a936ca763904c3c35fce2f3556c559c0214345d31b1bcebf76acb70' } +]; + +testnet.checkpoints.tsLastCheckpoint = 1338180505; +testnet.checkpoints.txsLastCheckpoint = 16341; +testnet.checkpoints.txsPerDay = 300; + +// http://blockexplorer.com/testnet/b/0 +// http://blockexplorer.com/testnet/rawblock/000000000933ea01ad0ee984209779baaec3ced90fa3f408719526f8d77f4943 +testnet.genesis = { + version: 1, + _hash: utils.toArray( + '000000000933ea01ad0ee984209779baaec3ced90fa3f408719526f8d77f4943', + 'hex' + ).reverse(), + prevBlock: [ 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0 ], + merkleRoot: utils.toArray( + '4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b', + 'hex' + ).reverse(), + ts: 1296688602, + bits: 0x1d00ffff, + nonce: 414098458 +}; + +testnet.magic = 0x0709110b; + +testnet.preload = { + 'v': 1, + 'type': 'chain', + 'hashes': [utils.toHex(testnet.genesis._hash)], + 'ts': [testnet.genesis.ts], + 'heights': [0] +}; diff --git a/lib/bcoin/protocol/parser.js b/lib/bcoin/protocol/parser.js index a5fd849d..c13bcbd9 100644 --- a/lib/bcoin/protocol/parser.js +++ b/lib/bcoin/protocol/parser.js @@ -6,6 +6,7 @@ var bcoin = require('../../bcoin'); var utils = bcoin.utils; var assert = utils.assert; var constants = require('./constants'); +var network = require('./network'); var readU32 = utils.readU32; var readU64 = utils.readU64; @@ -71,7 +72,7 @@ Parser.prototype.parse = function parse(chunk) { Parser.prototype.parseHeader = function parseHeader(h) { var magic = readU32(h, 0); - if (magic !== constants.magic) { + if (magic !== network.magic) { return this._error('Invalid magic value: ' + magic.toString(16)); } diff --git a/lib/bcoin/utils.js b/lib/bcoin/utils.js index d4566c6e..a58e0acd 100644 --- a/lib/bcoin/utils.js +++ b/lib/bcoin/utils.js @@ -2,6 +2,7 @@ var utils = exports; var bn = require('bn.js'); var hash = require('hash.js'); +var util = require('util'); function toArray(msg, enc) { if (Array.isArray(msg)) @@ -491,6 +492,41 @@ utils.toKeyArray = function(msg) { return utils.fromBase58(msg); }; -utils.debug = function(msg) { - console.log('\x1b[31m' + msg + '\x1b[m'); +utils.inspect = function(obj) { + return typeof obj !== 'string' + ? util.inspect(obj, null, 20, true) + : obj; +}; + +utils.print = function(msg) { + return typeof msg === 'object' + ? process.stdout.write(utils.inspect(msg) + '\n') + : console.log.apply(console, arguments); +}; + +utils.debug = function() { + var args = Array.prototype.slice.call(arguments); + args[0] = '\x1b[31m' + args[0] + '\x1b[m'; + return utils.print.apply(null, args); +}; + +utils.merge = function(target) { + var args = Array.prototype.slice.call(arguments, 1); + args.forEach(function(obj) { + Object.keys(obj).forEach(function(key) { + target[key] = obj[key]; + }); + }); + return target; +}; + +utils.fromBTC = function(btc) { + var satoshi = new bn(+btc || 0); + satoshi.imuln(100000000); + return satoshi; +}; + +utils.ntoBTC = function(satoshi) { + satoshi = new bn(Math.floor(+satoshi || 0).toString(16), 16); + return bcoin.utils.toBTC(satoshi); }; diff --git a/lib/bcoin/wallet.js b/lib/bcoin/wallet.js index 6dfd9a33..4b21ba97 100644 --- a/lib/bcoin/wallet.js +++ b/lib/bcoin/wallet.js @@ -6,6 +6,7 @@ var EventEmitter = require('events').EventEmitter; var utils = bcoin.utils; var assert = utils.assert; var constants = bcoin.protocol.constants; +var network = bcoin.protocol.network; function Wallet(options, passphrase) { if (!(this instanceof Wallet)) @@ -166,7 +167,7 @@ Wallet.prototype.getPrivateKey = function getPrivateKey(enc) { if (enc === 'base58') { // We'll be using ncompressed public key as an address - var arr = [ 128 ]; + var arr = [ network.prefixes.privkey ]; // 0-pad key while (arr.length + priv.length < 33) @@ -258,7 +259,7 @@ Wallet.prototype.getAddress = function getAddress() { Wallet.hash2addr = function hash2addr(hash, version) { hash = utils.toArray(hash, 'hex'); - version = constants.addr[version || 'normal']; + version = network.prefixes[version || 'normal']; hash = [ version ].concat(hash); var addr = hash.concat(utils.checksum(hash)); @@ -269,7 +270,7 @@ Wallet.addr2hash = function addr2hash(addr, version) { if (!Array.isArray(addr)) addr = utils.fromBase58(addr); - version = constants.addr[version || 'normal']; + version = network.prefixes[version || 'normal']; if (addr.length !== 25) return []; @@ -417,6 +418,7 @@ Wallet.prototype.toJSON = function toJSON() { return { v: 1, type: 'wallet', + network: network.type, pub: this.getOwnPublicKey('base58'), priv: this.getPrivateKey('base58'), tx: this.tx.toJSON(), @@ -432,6 +434,7 @@ Wallet.prototype.toJSON = function toJSON() { Wallet.fromJSON = function fromJSON(json) { assert.equal(json.v, 1); assert.equal(json.type, 'wallet'); + assert.equal(json.network, network.type); var priv; var pub; @@ -440,7 +443,7 @@ Wallet.fromJSON = function fromJSON(json) { if (json.priv) { var key = bcoin.utils.fromBase58(json.priv); assert(utils.isEqual(key.slice(-4), utils.checksum(key.slice(0, -4)))); - assert.equal(key[0], 128); + assert.equal(key[0], network.prefixes.privkey); key = key.slice(0, -4); if (key.length === 34) {