From ebddafcaa715375ba3774d6668d74fb6322e33e4 Mon Sep 17 00:00:00 2001 From: Stephen Pair Date: Tue, 9 Jul 2013 17:45:21 -0400 Subject: [PATCH] some more stuff...not complete yet --- Block.js | 54 ++++++++++++++++++++++---------------------- Connection.js | 22 +++++++++--------- PeerManager.js | 11 ++++----- Script.js | 22 ++++++++++-------- ScriptInterpreter.js | 8 ++++--- Transaction.js | 27 +++++++++++----------- config.js | 3 ++- package.json | 9 +++++--- util/error.js | 40 ++++++++++++++++++++++++++++++++ util/log.js | 14 ++++++++++++ util/util.js | 39 +++++++++----------------------- 11 files changed, 146 insertions(+), 103 deletions(-) create mode 100644 util/error.js create mode 100644 util/log.js diff --git a/Block.js b/Block.js index 7d3333641..6c5e80b90 100644 --- a/Block.js +++ b/Block.js @@ -1,17 +1,17 @@ require('classtool'); function spec(b) { - var Util = b.Util || require('../ext/util'); + var util = b.util || require('./util/util'); var Debug1 = b.Debug1 || function() {}; - var Script = b.Script || require('./script').class(); + var Script = b.Script || require('./Script').class(); var Bignum = b.Bignum || require('bignum'); var Put = b.Put || require('bufferput'); var Step = b.Step || require('step'); - var Transaction = b.Transaction || require('./transaction'); - var TransactionIn = b.TransactionIn || require('./transactionIn'); - var TransactionOut = b.TransactionOut || require('./transactionOut'); + var Transaction = b.Transaction || require('./transaction').class(); + var TransactionIn = Transaction.In; + var TransactionOut = Transaction.Out; var COINBASE_OP = Transaction.COINBASE_OP; - var VerificationError = b.VerificationError || require('../ext/error').VerificationError; + var VerificationError = b.VerificationError || require('./util/error').VerificationError; var BlockRules = { maxTimeOffset: 2 * 60 * 60, // How far block timestamps can be into the future largestHash: Bignum(2).pow(256) @@ -23,8 +23,8 @@ function spec(b) { 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.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; @@ -32,7 +32,7 @@ function spec(b) { this.height = data.height || 0; this.size = data.size || 0; this.active = data.active || false; - this.chainWork = data.chainWork || Util.EMPTY_BUFFER; + this.chainWork = data.chainWork || util.EMPTY_BUFFER; this.txs = data.txs || []; }; @@ -50,7 +50,7 @@ function spec(b) { Block.prototype.calcHash = function calcHash() { var header = this.getHeader(); - return Util.twoSha256(header); + return util.twoSha256(header); }; Block.prototype.checkHash = function checkHash() { @@ -65,7 +65,7 @@ function spec(b) { }; Block.prototype.checkProofOfWork = function checkProofOfWork() { - var target = Util.decodeDiffBits(this.bits); + 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. @@ -89,7 +89,7 @@ function spec(b) { * 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); + var target = util.decodeDiffBits(this.bits, true); return BlockRules.largestHash.div(target.add(1)); }; @@ -141,7 +141,7 @@ function spec(b) { // This function is a direct translation of CBlock::BuildMerkleTree(). if (txs.length == 0) { - return [Util.NULL_HASH.slice(0)]; + return [util.NULL_HASH.slice(0)]; } // Start by adding all the hashes of the transactions as leaves of the tree. @@ -157,7 +157,7 @@ function spec(b) { var i2 = Math.min(i + 1, size - 1); var a = tree[j + i]; var b = tree[j + i2]; - tree.push(Util.twoSha256(a.concat(b))); + tree.push(util.twoSha256(a.concat(b))); } j += size; } @@ -199,7 +199,7 @@ function spec(b) { }; Block.getBlockValue = function getBlockValue(height) { - var subsidy = Bignum(50).mul(Util.COIN); + var subsidy = Bignum(50).mul(util.COIN); subsidy = subsidy.div(Bignum(2).pow(Math.floor(height / 210000))); return subsidy; }; @@ -209,7 +209,7 @@ function spec(b) { }; Block.prototype.toString = function toString() { - return ""; + return ""; }; /** @@ -251,7 +251,7 @@ function spec(b) { var self = this; var powLimit = blockChain.getMinDiff(); - var powLimitTarget = Util.decodeDiffBits(powLimit, true); + var powLimitTarget = util.decodeDiffBits(powLimit, true); var targetTimespan = blockChain.getTargetTimespan(); var targetSpacing = blockChain.getTargetSpacing(); @@ -327,7 +327,7 @@ function spec(b) { actualTimespan = targetTimespan*4; } - var oldTarget = Util.decodeDiffBits(self.bits, true); + var oldTarget = util.decodeDiffBits(self.bits, true); var newTarget = oldTarget.mul(actualTimespan).div(targetTimespan); if (newTarget.cmp(powLimitTarget) > 0) { @@ -339,7 +339,7 @@ function spec(b) { Debug1('Before: '+oldTarget.toBuffer().toString('hex')); Debug1('After: '+newTarget.toBuffer().toString('hex')); - callback(null, Util.encodeDiffBits(newTarget)); + callback(null, util.encodeDiffBits(newTarget)); } catch (err) { callback(err); } @@ -426,12 +426,12 @@ function spec(b) { { var tx = new Transaction(); tx.ins.push(new TransactionIn({ - s: Util.EMPTY_BUFFER, + s: util.EMPTY_BUFFER, q: 0xffffffff, o: COINBASE_OP })); tx.outs.push(new TransactionOut({ - v: Util.bigIntToValue(this.getBlockValue()), + v: util.bigIntToValue(this.getBlockValue()), s: Script.createPubKeyOut(beneficiary).getBuffer() })); return tx; @@ -518,7 +518,7 @@ function spec(b) { Block.prototype.solve = function solve(miner, callback) { var header = this.getHeader(); - var target = Util.decodeDiffBits(this.bits); + var target = util.decodeDiffBits(this.bits); miner.solve(header, target, callback); }; @@ -529,10 +529,10 @@ function spec(b) { function getStandardizedObject(txs) { var block = { - hash: Util.formatHashFull(this.getHash()), + hash: util.formatHashFull(this.getHash()), version: this.version, - prev_block: Util.formatHashFull(this.prev_hash), - mrkl_root: Util.formatHashFull(this.merkle_root), + prev_block: util.formatHashFull(this.prev_hash), + mrkl_root: util.formatHashFull(this.merkle_root), time: this.timestamp, bits: this.bits, nonce: this.nonce, @@ -542,13 +542,13 @@ function spec(b) { if (txs) { var mrkl_tree = this.getMerkleTree(txs).map(function (buffer) { - return Util.formatHashFull(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 + totalSize += util.getVarIntSize(txs.length); // txn_count txs = txs.map(function (tx) { tx = tx.getStandardizedObject(); totalSize += tx.size; diff --git a/Connection.js b/Connection.js index f68e152f6..1d0863dae 100644 --- a/Connection.js +++ b/Connection.js @@ -2,6 +2,7 @@ require('classtool'); function spec(b) { var config = b.config || require('./config'); + var log = b.log || require('./util/log')(config); var network = b.network || require('./networks')[config.network]; var MAX_RECEIVE_BUFFER = 10000000; @@ -11,13 +12,12 @@ function spec(b) { var Put = b.Put || require('bufferput'); var Buffers = b.Buffers || require('buffers'); var noop = function() {}; - var log = b.log || require('bitpay/log'); + var util = b.util || require('./util/util'); var Parser = b.Parser || require('./util/BinaryParser').class(); - var util = require('./util/util'); var doubleSha256 = b.doubleSha256 || util.twoSha256; var nonce = util.generateNonce(); - var Block = require('../model/block').class(); + var Block = require('./Block').class(); var BIP0031_VERSION = 60000; @@ -170,7 +170,7 @@ function spec(b) { Connection.prototype.sendVersion = function () { var subversion = '/BitcoinX:0.1/'; - var put = Put(); + var put = new Put(); put.word32le(PROTOCOL_VERSION); // version put.word64le(1); // services put.word64le(Math.round(new Date().getTime()/1000)); // timestamp @@ -185,7 +185,7 @@ function spec(b) { }; Connection.prototype.sendGetBlocks = function (starts, stop) { - var put = Put(); + var put = new Put(); put.word32le(this.sendVer); put.varint(starts.length); @@ -208,7 +208,7 @@ function spec(b) { }; Connection.prototype.sendGetData = function (invs) { - var put = Put(); + var put = new Put(); put.varint(invs.length); for (var i = 0; i < invs.length; i++) { put.word32le(invs[i].type); @@ -218,13 +218,13 @@ function spec(b) { }; Connection.prototype.sendGetAddr = function (invs) { - var put = Put(); + var put = new Put(); this.sendMessage('getaddr', put.buffer()); }; Connection.prototype.sendInv = function(data) { if(!Array.isArray(data)) data = [data]; - var put = Put(); + var put = new Put(); put.varint(data.length); data.forEach(function (value) { if (value instanceof Block) { @@ -240,7 +240,7 @@ function spec(b) { }; Connection.prototype.sendHeaders = function (headers) { - var put = Put(); + var put = new Put(); put.varint(headers.length); headers.forEach(function (header) { put.put(header); @@ -256,7 +256,7 @@ function spec(b) { }; Connection.prototype.sendBlock = function (block, txs) { - var put = Put(); + var put = new Put(); // Block header put.put(block.getHeader()); @@ -283,7 +283,7 @@ function spec(b) { checksum = new Buffer([]); } - var message = Put(); // -- HEADER -- + var message = new Put(); // -- HEADER -- message.put(magic); // magic bytes message.put(commandBuf); // command name message.pad(12 - commandBuf.length); // zero-padded diff --git a/PeerManager.js b/PeerManager.js index 10aa593e0..e87f21b89 100644 --- a/PeerManager.js +++ b/PeerManager.js @@ -2,11 +2,11 @@ require('classtool'); function spec(b) { var config = b.config || require('./config'); + var log = b.log || require('./util/log')(config); var network = b.network || require('./networks')[config.network]; var Connection = b.Connection || require('./Connection').createClass({config: config}); var Peer = b.Peer || require('./Peer').class(); var noop = function() {}; - var log = b.log || {info: noop, warn: noop, err: noop}; GetAdjustedTime = b.GetAdjustedTime || function () { // TODO: Implement actual adjustment @@ -31,16 +31,14 @@ function spec(b) { PeerManager.Connection = Connection; - PeerManager.prototype.start = function() - { + PeerManager.prototype.start = function() { this.active = true; if(!this.timer) { this.timer = setInterval(this.checkStatus.bind(this), this.interval); } }; - PeerManager.prototype.stop = function () - { + PeerManager.prototype.stop = function() { this.active = false; if(this.timer) { clearInterval(this.timer); @@ -63,8 +61,7 @@ function spec(b) { } }; - PeerManager.prototype.checkStatus = function checkStatus() - { + PeerManager.prototype.checkStatus = function checkStatus() { // Make sure we are connected to all forcePeers if(this.peers.length) { var peerIndex = {}; diff --git a/Script.js b/Script.js index 84b9ef1e9..00d4bc57d 100644 --- a/Script.js +++ b/Script.js @@ -1,6 +1,9 @@ require('classtool'); function spec(b) { + var config = b.config || require('./config'); + var log = b.log || require('./util/log')(config); + var Opcode = require('./opcode').class(); // Make opcodes available as pseudo-constants @@ -8,16 +11,15 @@ function spec(b) { eval(i + " = " + Opcode.map[i] + ";"); } - var logger = b.logger || require('../ext/logger'); - var Util = b.Util || require('../ext/util'); - var Parser = b.Parser || require('../ext/binaryParser').class(); + var util = b.util || require('./util/util'); + var Parser = b.Parser || require('./util/BinaryParser').class(); var Put = b.Put || require('bufferput'); function Script(buffer) { if(buffer) { this.buffer = buffer; } else { - this.buffer = Util.EMPTY_BUFFER; + this.buffer = util.EMPTY_BUFFER; } this.chunks = []; this.parse(); @@ -84,10 +86,10 @@ function spec(b) { case 'Address': return this.chunks[2]; case 'Pubkey': - return Util.sha256ripe160(this.chunks[0]); + return util.sha256ripe160(this.chunks[0]); default: - logger.scrdbg("Encountered non-standard scriptPubKey"); - logger.scrdbg("Strange script was: " + this.toString()); + log.debug("Encountered non-standard scriptPubKey"); + log.debug("Strange script was: " + this.toString()); return null; } }; @@ -114,8 +116,8 @@ function spec(b) { case 'Pubkey': return null; default: - logger.scrdbg("Encountered non-standard scriptSig"); - logger.scrdbg("Strange script was: " + this.toString()); + log.debug("Encountered non-standard scriptSig"); + log.debug("Strange script was: " + this.toString()); return null; } }; @@ -144,7 +146,7 @@ function spec(b) { } if (Buffer.isBuffer(chunk)) { - script += "0x"+Util.formatBuffer(chunk, truncate ? null : 0); + script += "0x"+util.formatBuffer(chunk, truncate ? null : 0); } else { script += Opcode.reverseMap[chunk]; } diff --git a/ScriptInterpreter.js b/ScriptInterpreter.js index eec0030aa..fc31bca7b 100644 --- a/ScriptInterpreter.js +++ b/ScriptInterpreter.js @@ -1,6 +1,9 @@ require('classtool'); function spec(b) { + var config = b.config || require('./config'); + var log = b.log || require('./util/log')(config); + var Opcode = require('./opcode').class(); // Make opcodes available as pseudo-constants @@ -9,8 +12,7 @@ function spec(b) { } var bignum = b.bignum || require('bignum'); - var logger = b.logger || require('../ext/logger'); - var Util = b.Util || require('../ext/util'); + var Util = b.Util || require('./util/util'); var Script = require('./script').class(); function ScriptInterpreter() { @@ -717,7 +719,7 @@ function spec(b) { executeStep.call(this, cb); } } catch (e) { - logger.scrdbg("Script aborted: "+ + log.debug("Script aborted: "+ (e.message ? e : e)); cb(e); } diff --git a/Transaction.js b/Transaction.js index a90257047..fd758b1e1 100644 --- a/Transaction.js +++ b/Transaction.js @@ -1,16 +1,17 @@ require('classtool'); function spec(b) { - var Script = b.Script || require('./script').class(); - var ScriptInterpreter = b.ScriptInterpreter || require('./scriptInterpreter').class(); - var util = b.util || require('../ext/util'); + var config = b.config || require('./config'); + var log = b.log || require('./util/log')(config); + var Script = b.Script || require('./Script').class(); + var ScriptInterpreter = b.ScriptInterpreter || require('./ScriptInterpreter').class(); + var util = b.util || require('./util/util'); var bignum = b.bignum || require('bignum'); var Put = b.Put || require('bufferput'); - var error = b.error || require('../ext/error'); - var logger = b.logger || require('../ext/logger'); + var Parser = b.Parser || require('./util/BinaryParser').class(); var Step = b.Step || require('step'); - var Parser = b.Parser || require('../ext/binaryParser').class(); + var error = b.error || require('./util/error'); var VerificationError = error.VerificationError; var MissingSourceError = error.MissingSourceError; @@ -265,9 +266,9 @@ function spec(b) { for (var i = 0, l = results.length; i < l; i++) { if (!results[i]) { var txout = getTxOut(self.ins[i]); - logger.scrdbg('Script evaluated to false'); - logger.scrdbg('|- scriptSig', ""+self.ins[i].getScript()); - logger.scrdbg('`- scriptPubKey', ""+txout.getScript()); + log.debug('Script evaluated to false'); + log.debug('|- scriptSig', ""+self.ins[i].getScript()); + log.debug('`- scriptPubKey', ""+txout.getScript()); throw new VerificationError('Script for input '+i+' evaluated to false'); } } @@ -302,7 +303,7 @@ function spec(b) { blockChain.getConflictingTransactions(outpoints, function (err, results) { if (results.length) { if (results[0].getHash().compare(self.getHash()) == 0) { - logger.warn("Detected tx re-add (recoverable db corruption): " + log.warn("Detected tx re-add (recoverable db corruption): " + util.formatHashAlt(results[0].getHash())); // TODO: Needs to return an error for the memory pool case? callback(null, fees); @@ -360,7 +361,7 @@ function spec(b) { } catch (err) { // It's not our job to validate, so we just ignore any errors and issue // a very low level log message. - logger.debug("Unable to determine affected pubkeys: " + + log.debug("Unable to determine affected pubkeys: " + (err.stack ? err.stack : ""+err)); } }; @@ -395,7 +396,7 @@ function spec(b) { } catch (err) { // It's not our job to validate, so we just ignore any errors and issue // a very low level log message. - logger.debug("Unable to determine affected pubkeys: " + + log.debug("Unable to determine affected pubkeys: " + (err.stack ? err.stack : ""+err)); } } @@ -707,7 +708,7 @@ function spec(b) { cb.apply(null, args); }); } catch (err) { - logger.error("Callback error after connecting tx inputs: "+ + log.err("Callback error after connecting tx inputs: "+ (err.stack ? err.stack : err.toString())); } }; diff --git a/config.js b/config.js index 07a1e6a7e..313ee6311 100644 --- a/config.js +++ b/config.js @@ -1,3 +1,4 @@ module.exports = { - network: 'livenet' + network: 'livenet', + logging: 'normal' // none, normal, debug }; diff --git a/package.json b/package.json index eed32b474..d56afe4d0 100644 --- a/package.json +++ b/package.json @@ -28,9 +28,12 @@ "classtool": ">=1.0.0", "base58-native": ">=0.1.1", "bindings": "1.1.0", - "bufferput": ">=0.1.1" - //"bignum" - //"binary" + "bufferput": ">=0.1.1", + "bignum": "0.6.1", + "binary": "0.3.0", + "step": "0.0.4", + "buffers": "0.0.1", + "buffertools": "1.1.1" }, "devDependencies": {}, "license": "MIT" diff --git a/util/error.js b/util/error.js new file mode 100644 index 000000000..428fc894e --- /dev/null +++ b/util/error.js @@ -0,0 +1,40 @@ + +/** + * Used during transcation verification when a source txout is missing. + * + * When a transaction is being verified by the memory pool this error causes + * it to be added to the orphan pool instead of being discarded. + */ +function MissingSourceError(msg, missingTxHash) { + // TODO: Since this happens in normal operation, perhaps we should + // avoid generating a whole stack trace. + Error.call(this); + Error.captureStackTrace(this, arguments.callee); + this.message = msg; + this.missingTxHash = missingTxHash; + this.name = 'MissingSourceError'; +}; + +MissingSourceError.prototype.__proto__ = Error.prototype; + +exports.MissingSourceError = MissingSourceError; + +/** + * Used in several places to indicate invalid data. + * + * We want to distinguish invalid data errors from program errors, so we use + * this exception to indicate the former. + */ +function VerificationError(msg, missingTxHash) { + // TODO: Since this happens in normal operation, perhaps we should + // avoid generating a whole stack trace. + Error.call(this); + Error.captureStackTrace(this, arguments.callee); + this.message = msg; + this.missingTxHash = missingTxHash; + this.name = 'VerificationError'; +}; + +VerificationError.prototype.__proto__ = Error.prototype; + +exports.VerificationError = VerificationError; diff --git a/util/log.js b/util/log.js new file mode 100644 index 000000000..6570b71ae --- /dev/null +++ b/util/log.js @@ -0,0 +1,14 @@ +var noop = function() {}; + +var loggers = { + none: {info: noop, warn: noop, err: noop, debug: noop}, + normal: {info: console.log, warn: console.log, err: console.log, debug: noop}, + debug: {info: console.log, warn: console.log, err: console.log, debug: console.log}, +}; + +module.exports = function(config) { + config = config || {}; + if(config.log) return config.log; + if(config.loggers) return config.loggers[config.logging || 'normal']; + return loggers[config.logging || 'normal']; +}; diff --git a/util/util.js b/util/util.js index 0013ba217..9e8fe5445 100644 --- a/util/util.js +++ b/util/util.js @@ -2,14 +2,7 @@ require('buffertools'); var crypto = require('crypto'); var bignum = require('bignum'); var Binary = require('binary'); -var Put = require('put'); -//var logger = require('./logger'); - -var ccmodule = require('bindings')('nativetools'); - -exports.ccmodule = ccmodule; - -exports.BitcoinKey = ccmodule.BitcoinKey; +var Put = require('bufferput'); var sha256 = exports.sha256 = function (data) { return new Buffer(crypto.createHash('sha256').update(data).digest('binary'), 'binary'); @@ -31,8 +24,6 @@ var sha256ripe160 = exports.sha256ripe160 = function (data) { return ripe160(sha256(data)); }; -var sha256midstate = exports.sha256midstate = ccmodule.sha256_midstate; - /** * Format a block hash like the official client does. */ @@ -119,7 +110,7 @@ var formatValue = exports.formatValue = function (valueBuffer) { var pubKeyHashToAddress = exports.pubKeyHashToAddress = function (pubKeyHash, addressVersion) { if (!pubKeyHash) return ""; - var put = Put(); + var put = new Put(); // Version if(addressVersion) { put.word8le(addressVersion); @@ -139,7 +130,6 @@ var addressToPubKeyHash = exports.addressToPubKeyHash = function (address) { // Check sanity if (!address.match(/^[1-9A-HJ-NP-Za-km-z]{27,35}$/)) { - //logger.warn("Not a valid Bitcoin address"); return null; } @@ -155,7 +145,6 @@ var addressToPubKeyHash = exports.addressToPubKeyHash = function (address) { // Check checksum var checksum = twoSha256(buffer.slice(0, 21)).slice(0, 4); if (checksum.compare(parser.vars.checksum) !== 0) { - //logger.warn("Checksum comparison failed"); return null; } @@ -284,7 +273,7 @@ var reverseBytes32 = exports.reverseBytes32 = function (data) { if (data.length % 4) { throw new Error("Util.reverseBytes32(): Data length must be multiple of 4"); } - var put = Put(); + var put = new Put(); var parser = Binary.parse(data); while (!parser.eof()) { var word = parser.word32le('word').vars.word; @@ -311,19 +300,13 @@ var getVarIntSize = exports.getVarIntSize = function getVarIntSize(i) { }; // Initializations -try { - var NULL_HASH = exports.NULL_HASH = new Buffer(32).fill(0); - var EMPTY_BUFFER = exports.EMPTY_BUFFER = new Buffer(0); - var ZERO_VALUE = exports.ZERO_VALUE = new Buffer(8).fill(0); - var INT64_MAX = exports.INT64_MAX = decodeHex("ffffffffffffffff"); +exports.NULL_HASH = new Buffer(32).fill(0); +exports.EMPTY_BUFFER = new Buffer(0); +exports.ZERO_VALUE = new Buffer(8).fill(0); +INT64_MAX = new Buffer('ffffffffffffffff', 'hex'); - // How much of Bitcoin's internal integer coin representation - // makes 1 BTC - var COIN = exports.COIN = 100000000; +// How much of Bitcoin's internal integer coin representation +// makes 1 BTC +exports.COIN = 100000000; - var MAX_TARGET = exports.MAX_TARGET = decodeHex('00000000FFFF0000000000000000000000000000000000000000000000000000'); -} catch (e) { - //logger.error("Error while generating utility constants:\n" + - // (e.stack ? e.stack : e.toString())); - process.exit(1); -} +exports.MAX_TARGET = new Buffer('00000000FFFF0000000000000000000000000000000000000000000000000000', 'hex');