diff --git a/bench/script.js b/bench/script.js index e0b0a655..badbcfe6 100644 --- a/bench/script.js +++ b/bench/script.js @@ -5,7 +5,7 @@ var constants = require('../lib/protocol/constants'); var crypto = require('../lib/crypto/crypto'); var Script = require('../lib/script/script'); var bench = require('./bench'); -var opcodes = constants.opcodes; +var opcodes = Script.opcodes; var i, hashes, end; Script.prototype.fromPubkeyhashOld = function fromScripthash(hash) { diff --git a/bench/tx.js b/bench/tx.js index 36fcd004..ba02b93a 100644 --- a/bench/tx.js +++ b/bench/tx.js @@ -4,10 +4,12 @@ var fs = require('fs'); var Block = require('../lib/primitives/block'); var Address = require('../lib/primitives/address'); var TX = require('../lib/primitives/tx'); +var Script = require('../lib/script/script'); var MTX = require('../lib/primitives/mtx'); var Coin = require('../lib/primitives/coin'); var CoinView = require('../lib/coins/coinview'); var constants = require('../lib/protocol/constants'); +var encoding = require('../lib/utils/encoding'); var crypto = require('../lib/crypto/crypto'); var bench = require('./bench'); @@ -92,7 +94,7 @@ end(i); end = bench('verify'); for (i = 0; i < 3000; i++) - tx3.tx.verify(tx3.view, constants.flags.VERIFY_P2SH); + tx3.tx.verify(tx3.view, Script.flags.VERIFY_P2SH); end(i * tx3.tx.inputs.length); end = bench('fee'); @@ -100,7 +102,7 @@ for (i = 0; i < 1000; i++) tx3.tx.getFee(tx3.view); end(i); -flags = constants.flags.VERIFY_P2SH | constants.flags.VERIFY_DERSIG; +flags = Script.flags.VERIFY_P2SH | Script.flags.VERIFY_DERSIG; end = bench('verify multisig'); for (i = 0; i < 3000; i++) btx.tx.verify(btx.view, flags); @@ -111,7 +113,7 @@ tx = new MTX(); for (i = 0; i < 100; i++) { tx.addInput({ prevout: { - hash: constants.NULL_HASH, + hash: encoding.NULL_HASH, index: 0 }, script: [ diff --git a/lib/blockchain/chain.js b/lib/blockchain/chain.js index 1b99e757..567e6141 100644 --- a/lib/blockchain/chain.js +++ b/lib/blockchain/chain.js @@ -12,12 +12,13 @@ var Network = require('../protocol/network'); var Logger = require('../node/logger'); var ChainDB = require('./chaindb'); var constants = require('../protocol/constants'); +var consensus = require('../protocol/consensus'); var util = require('../utils/util'); -var btcutils = require('../btc/utils'); var Locker = require('../utils/locker'); var LRU = require('../utils/lru'); var ChainEntry = require('./chainentry'); var CoinView = require('../coins/coinview'); +var Script = require('../script/script'); var assert = require('assert'); var errors = require('../btc/errors'); var VerifyError = errors.VerifyError; @@ -414,7 +415,7 @@ Chain.prototype.verify = co(function* verify(block, prev) { // Check block weight (different from block size // check in non-contextual verification). - if (block.getWeight() > constants.block.MAX_WEIGHT) { + if (block.getWeight() > consensus.MAX_BLOCK_WEIGHT) { throw new VerifyError(block, 'invalid', 'bad-blk-weight', @@ -468,8 +469,8 @@ Chain.prototype.getDeployments = co(function* getDeployments(block, prev) { // not have a signature. See: // 6a26d2ecb67f27d1fa5524763b49029d7106e91e3cc05743073461a719776192 // 9c08a4d78931342b37fd5f72900fb9983087e6f46c4a097d8a1f52c74e28eaf6 - if (block.ts >= constants.block.BIP16_TIME) - state.flags |= constants.flags.VERIFY_P2SH; + if (block.ts >= consensus.BIP16_TIME) + state.flags |= Script.flags.VERIFY_P2SH; // Coinbase heights are now enforced (bip34). if (height >= this.network.block.bip34height) @@ -477,16 +478,16 @@ Chain.prototype.getDeployments = co(function* getDeployments(block, prev) { // Signature validation is now enforced (bip66). if (height >= this.network.block.bip66height) - state.flags |= constants.flags.VERIFY_DERSIG; + state.flags |= Script.flags.VERIFY_DERSIG; // CHECKLOCKTIMEVERIFY is now usable (bip65) if (height >= this.network.block.bip65height) - state.flags |= constants.flags.VERIFY_CHECKLOCKTIMEVERIFY; + state.flags |= Script.flags.VERIFY_CHECKLOCKTIMEVERIFY; // Segregrated witness is now usable (bip141 - segnet3) if (this.options.witness && this.network.oldWitness) { if (height >= this.network.block.bip141height) - state.flags |= constants.flags.VERIFY_WITNESS; + state.flags |= Script.flags.VERIFY_WITNESS; } if (this.network.oldWitness) @@ -496,18 +497,18 @@ Chain.prototype.getDeployments = co(function* getDeployments(block, prev) { // past locktimes are now usable (bip9 & bip113). active = yield this.isActive(prev, deployments.csv); if (active) { - state.flags |= constants.flags.VERIFY_CHECKSEQUENCEVERIFY; - state.lockFlags |= constants.flags.VERIFY_SEQUENCE; - state.lockFlags |= constants.flags.MEDIAN_TIME_PAST; + state.flags |= Script.flags.VERIFY_CHECKSEQUENCEVERIFY; + state.lockFlags |= constants.lockFlags.VERIFY_SEQUENCE; + state.lockFlags |= constants.lockFlags.MEDIAN_TIME_PAST; } // Segregrated witness is now usable (bip141 - segnet4) active = yield this.isActive(prev, deployments.segwit); if (active) { if (this.options.witness) - state.flags |= constants.flags.VERIFY_WITNESS; + state.flags |= Script.flags.VERIFY_WITNESS; // BIP147 - state.flags |= constants.flags.VERIFY_NULLDUMMY; + state.flags |= Script.flags.VERIFY_NULLDUMMY; } return state; @@ -575,8 +576,8 @@ Chain.prototype.verifyDuplicates = co(function* verifyDuplicates(block, prev, st // Blocks 91842 and 91880 created duplicate // txids by using the same exact output script // and extraNonce. - if (constants.bip30[height]) { - if (block.hash('hex') === constants.bip30[height]) + if (this.network.bip30[height]) { + if (block.hash('hex') === this.network.bip30[height]) continue; } throw new VerifyError(block, 'invalid', 'bad-txns-BIP30', 100); @@ -648,7 +649,7 @@ Chain.prototype.verifyInputs = co(function* verifyInputs(block, prev, state) { // Count sigops (legacy + scripthash? + witness?) sigops += tx.getSigopsCost(view, state.flags); - if (sigops > constants.block.MAX_SIGOPS_COST) { + if (sigops > consensus.MAX_BLOCK_SIGOPS_COST) { throw new VerifyError(block, 'invalid', 'bad-blk-sigops', @@ -1988,7 +1989,7 @@ Chain.prototype.retarget = function retarget(prev, first) { return prev.bits; actualTimespan = prev.ts - first.ts; - target = btcutils.fromCompact(prev.bits); + target = consensus.fromCompact(prev.bits); if (actualTimespan < targetTimespan / 4 | 0) actualTimespan = targetTimespan / 4 | 0; @@ -2002,7 +2003,7 @@ Chain.prototype.retarget = function retarget(prev, first) { if (target.cmp(pow.limit) > 0) return pow.bits; - return btcutils.toCompact(target); + return consensus.toCompact(target); }; /** @@ -2222,10 +2223,10 @@ Chain.prototype.verifyFinal = co(function* verifyFinal(prev, tx, flags) { var ts; // We can skip MTP if the locktime is height. - if (tx.locktime < constants.LOCKTIME_THRESHOLD) + if (tx.locktime < consensus.LOCKTIME_THRESHOLD) return tx.isFinal(height, -1); - if (flags & constants.flags.MEDIAN_TIME_PAST) { + if (flags & constants.lockFlags.MEDIAN_TIME_PAST) { ts = yield prev.getMedianTimeAsync(); return tx.isFinal(height, ts); } @@ -2244,11 +2245,11 @@ Chain.prototype.verifyFinal = co(function* verifyFinal(prev, tx, flags) { */ Chain.prototype.getLocks = co(function* getLocks(prev, tx, view, flags) { - var mask = constants.sequence.MASK; - var granularity = constants.sequence.GRANULARITY; - var disableFlag = constants.sequence.DISABLE_FLAG; - var typeFlag = constants.sequence.TYPE_FLAG; - var hasFlag = flags & constants.flags.VERIFY_SEQUENCE; + var mask = consensus.SEQUENCE_MASK; + var granularity = consensus.SEQUENCE_GRANULARITY; + var disableFlag = consensus.SEQUENCE_DISABLE_FLAG; + var typeFlag = consensus.SEQUENCE_TYPE_FLAG; + var hasFlag = flags & constants.lockFlags.VERIFY_SEQUENCE; var nextHeight = this.height + 1; var minHeight = -1; var minTime = -1; @@ -2325,9 +2326,9 @@ function DeploymentState() { if (!(this instanceof DeploymentState)) return new DeploymentState(); - this.flags = constants.flags.MANDATORY_VERIFY_FLAGS; - this.flags &= ~constants.flags.VERIFY_P2SH; - this.lockFlags = constants.flags.MANDATORY_LOCKTIME_FLAGS; + this.flags = Script.flags.MANDATORY_VERIFY_FLAGS; + this.flags &= ~Script.flags.VERIFY_P2SH; + this.lockFlags = constants.lockFlags.MANDATORY_LOCKTIME_FLAGS; this.bip34 = false; } @@ -2337,7 +2338,7 @@ function DeploymentState() { */ DeploymentState.prototype.hasP2SH = function hasP2SH() { - return (this.flags & constants.flags.VERIFY_P2SH) !== 0; + return (this.flags & Script.flags.VERIFY_P2SH) !== 0; }; /** @@ -2355,7 +2356,7 @@ DeploymentState.prototype.hasBIP34 = function hasBIP34() { */ DeploymentState.prototype.hasBIP66 = function hasBIP66() { - return (this.flags & constants.flags.VERIFY_DERSIG) !== 0; + return (this.flags & Script.flags.VERIFY_DERSIG) !== 0; }; /** @@ -2364,7 +2365,7 @@ DeploymentState.prototype.hasBIP66 = function hasBIP66() { */ DeploymentState.prototype.hasCLTV = function hasCLTV() { - return (this.flags & constants.flags.VERIFY_CHECKLOCKTIMEVERIFY) !== 0; + return (this.flags & Script.flags.VERIFY_CHECKLOCKTIMEVERIFY) !== 0; }; /** @@ -2373,7 +2374,7 @@ DeploymentState.prototype.hasCLTV = function hasCLTV() { */ DeploymentState.prototype.hasMTP = function hasMTP() { - return (this.lockFlags & constants.flags.MEDIAN_TIME_PAST) !== 0; + return (this.lockFlags & constants.lockFlags.MEDIAN_TIME_PAST) !== 0; }; /** @@ -2382,7 +2383,7 @@ DeploymentState.prototype.hasMTP = function hasMTP() { */ DeploymentState.prototype.hasCSV = function hasCSV() { - return (this.flags & constants.flags.VERIFY_CHECKSEQUENCEVERIFY) !== 0; + return (this.flags & Script.flags.VERIFY_CHECKSEQUENCEVERIFY) !== 0; }; /** @@ -2391,7 +2392,7 @@ DeploymentState.prototype.hasCSV = function hasCSV() { */ DeploymentState.prototype.hasWitness = function hasWitness() { - return (this.flags & constants.flags.VERIFY_WITNESS) !== 0; + return (this.flags & Script.flags.VERIFY_WITNESS) !== 0; }; /** diff --git a/lib/blockchain/chaindb.js b/lib/blockchain/chaindb.js index e7db629e..bde770c2 100644 --- a/lib/blockchain/chaindb.js +++ b/lib/blockchain/chaindb.js @@ -7,10 +7,9 @@ 'use strict'; -var AsyncObject = require('../utils/async'); -var constants = require('../protocol/constants'); -var util = require('../utils/util'); var assert = require('assert'); +var AsyncObject = require('../utils/async'); +var util = require('../utils/util'); var BufferReader = require('../utils/reader'); var StaticWriter = require('../utils/staticwriter'); var Amount = require('../btc/amount'); @@ -312,7 +311,7 @@ ChainDB.prototype.getHeight = co(function* getHeight(hash) { assert(typeof hash === 'string'); - if (hash === constants.NULL_HASH) + if (hash === encoding.NULL_HASH) return -1; entry = this.cacheHash.get(hash); @@ -441,7 +440,7 @@ ChainDB.prototype.getEntryByHash = co(function* getEntryByHash(hash) { assert(typeof hash === 'string'); - if (hash === constants.NULL_HASH) + if (hash === encoding.NULL_HASH) return; entry = this.cacheHash.get(hash); @@ -567,8 +566,8 @@ ChainDB.prototype.getStateCache = co(function* getStateCache() { var i, items, item, key, bit, hash, state; items = yield this.db.range({ - gte: layout.v(0, constants.ZERO_HASH), - lte: layout.v(255, constants.MAX_HASH), + gte: layout.v(0, encoding.ZERO_HASH), + lte: layout.v(255, encoding.MAX_HASH), values: true }); @@ -690,8 +689,8 @@ ChainDB.prototype.invalidateCache = co(function* invalidateCache(bit, batch) { var i, keys, key; keys = yield this.db.keys({ - gte: layout.v(bit, constants.ZERO_HASH), - lte: layout.v(bit, constants.MAX_HASH) + gte: layout.v(bit, encoding.ZERO_HASH), + lte: layout.v(bit, encoding.MAX_HASH) }); for (i = 0; i < keys.length; i++) { @@ -731,7 +730,7 @@ ChainDB.prototype.isMainChain = co(function* isMainChain(hash) { return true; } - if (hash === constants.NULL_HASH) + if (hash === encoding.NULL_HASH) return false; entry = this.cacheHash.get(hash); @@ -756,8 +755,8 @@ ChainDB.prototype.isMainChain = co(function* isMainChain(hash) { ChainDB.prototype.getEntries = function getEntries() { var self = this; return this.db.values({ - gte: layout.e(constants.ZERO_HASH), - lte: layout.e(constants.MAX_HASH), + gte: layout.e(encoding.ZERO_HASH), + lte: layout.e(encoding.MAX_HASH), parse: function(value) { return ChainEntry.fromRaw(self.chain, value); } @@ -771,8 +770,8 @@ ChainDB.prototype.getEntries = function getEntries() { ChainDB.prototype.getTips = function getTips() { return this.db.keys({ - gte: layout.p(constants.ZERO_HASH), - lte: layout.p(constants.MAX_HASH), + gte: layout.p(encoding.ZERO_HASH), + lte: layout.p(encoding.MAX_HASH), parse: layout.pp }); }; @@ -1058,8 +1057,8 @@ ChainDB.prototype.getCoinsByAddress = co(function* getCoinsByAddress(addresses) continue; keys = yield this.db.keys({ - gte: layout.C(hash, constants.ZERO_HASH, 0), - lte: layout.C(hash, constants.MAX_HASH, 0xffffffff), + gte: layout.C(hash, encoding.ZERO_HASH, 0), + lte: layout.C(hash, encoding.MAX_HASH, 0xffffffff), parse: layout.Cc }); @@ -1096,8 +1095,8 @@ ChainDB.prototype.getHashesByAddress = co(function* getHashesByAddress(addresses continue; yield this.db.keys({ - gte: layout.T(hash, constants.ZERO_HASH), - lte: layout.T(hash, constants.MAX_HASH), + gte: layout.T(hash, encoding.ZERO_HASH), + lte: layout.T(hash, encoding.MAX_HASH), parse: function(key) { var hash = layout.Tt(key); hashes[hash] = true; @@ -2062,7 +2061,7 @@ ChainOptions.fromRaw = function fromRaw(data) { */ function ChainState() { - this.tip = constants.ZERO_HASH; + this.tip = encoding.ZERO_HASH; this.tx = 0; this.coin = 0; this.value = 0; diff --git a/lib/blockchain/chainentry.js b/lib/blockchain/chainentry.js index 8c28d6d2..03d8cc6d 100644 --- a/lib/blockchain/chainentry.js +++ b/lib/blockchain/chainentry.js @@ -7,13 +7,14 @@ 'use strict'; +var assert = require('assert'); var BN = require('bn.js'); var Network = require('../protocol/network'); var constants = require('../protocol/constants'); +var consensus = require('../protocol/consensus'); var util = require('../utils/util'); -var btcutils = require('../btc/utils'); var crypto = require('../crypto/crypto'); -var assert = require('assert'); +var encoding = require('../utils/encoding'); var BufferReader = require('../utils/reader'); var StaticWriter = require('../utils/staticwriter'); var Headers = require('../primitives/headers'); @@ -52,10 +53,10 @@ function ChainEntry(chain, options, prev) { this.chain = chain; this.network = chain ? chain.network : Network.primary; - this.hash = constants.NULL_HASH; + this.hash = encoding.NULL_HASH; this.version = 1; - this.prevBlock = constants.NULL_HASH; - this.merkleRoot = constants.NULL_HASH; + this.prevBlock = encoding.NULL_HASH; + this.merkleRoot = encoding.NULL_HASH; this.ts = 0; this.bits = 0; this.nonce = 0; @@ -66,6 +67,21 @@ function ChainEntry(chain, options, prev) { this.fromOptions(options, prev); } +/** + * The max chainwork (1 << 256). + * @const {BN} + */ + +ChainEntry.MAX_CHAINWORK = new BN(1).ushln(256); + +/** + * Size of set to pick median time from. + * @const {Number} + * @default + */ + +ChainEntry.MEDIAN_TIMESPAN = 11; + /** * Inject properties from options. * @private @@ -112,20 +128,13 @@ ChainEntry.fromOptions = function fromOptions(chain, options, prev) { return new ChainEntry(chain).fromOptions(options, prev); }; -/** - * The max chainwork (1 << 256). - * @const {BN} - */ - -ChainEntry.MAX_CHAINWORK = new BN(1).ushln(256); - /** * Calculate the proof: (1 << 256) / (target + 1) * @returns {BN} proof */ ChainEntry.prototype.getProof = function getProof() { - var target = btcutils.fromCompact(this.bits); + var target = consensus.fromCompact(this.bits); if (target.isNeg() || target.cmpn(0) === 0) return new BN(0); return ChainEntry.MAX_CHAINWORK.div(target.iaddn(1)); @@ -164,7 +173,7 @@ ChainEntry.prototype.isGenesis = function isGenesis() { */ ChainEntry.prototype.getRetargetAncestors = function getRetargetAncestors() { - var timespan = constants.block.MEDIAN_TIMESPAN; + var timespan = ChainEntry.MEDIAN_TIMESPAN; var interval = this.network.pow.retargetInterval; var reset = this.network.pow.difficultyReset; var max = timespan; @@ -317,7 +326,7 @@ ChainEntry.prototype.getNextEntry = co(function* getNextEntry() { */ ChainEntry.prototype.getMedianTime = function getMedianTime(ancestors) { - var timespan = constants.block.MEDIAN_TIMESPAN; + var timespan = ChainEntry.MEDIAN_TIMESPAN; var entry = this; var median = []; var i; @@ -336,7 +345,7 @@ ChainEntry.prototype.getMedianTime = function getMedianTime(ancestors) { */ ChainEntry.prototype.getMedianTimeAsync = co(function* getMedianTimeAsync() { - var timespan = constants.block.MEDIAN_TIMESPAN; + var timespan = ChainEntry.MEDIAN_TIMESPAN; var ancestors = yield this.getAncestors(timespan); return this.getMedianTime(ancestors); }); diff --git a/lib/btc/errors.js b/lib/btc/errors.js index 6d612654..3ad2ffcf 100644 --- a/lib/btc/errors.js +++ b/lib/btc/errors.js @@ -9,8 +9,6 @@ var assert = require('assert'); var util = require('../utils/util'); -var constants = require('../protocol/constants'); -var Amount = require('./amount'); /** * An error thrown during verification. Can be either @@ -69,95 +67,9 @@ function VerifyResult() { this.score = 0; } -/** - * An error thrown from the scripting system, - * potentially pertaining to Script execution. - * @exports ScriptError - * @constructor - * @extends Error - * @param {String} code - Error code. - * @param {(Number|String)?} op - Opcode. - * @param {Number?} ip - Instruction pointer. - * @property {String} message - Error message. - * @property {String} code - Original code passed in. - * @property {String?} op - Symbolic opcode. - * @property {Number?} ip - Instruction pointer. - */ - -function ScriptError(code, op, ip) { - Error.call(this); - - if (Error.captureStackTrace) - Error.captureStackTrace(this, ScriptError); - - this.type = 'ScriptError'; - this.code = code; - - if (typeof op !== 'string') { - if (op || ip != null) { - code += ' ('; - if (op) { - op = constants.opcodesByVal[op] || op; - code += 'op=' + op; - if (ip != null) - code += ', '; - } - if (ip != null) - code += 'ip=' + ip; - code += ')'; - } - - this.message = code; - this.op = op || ''; - this.ip = ip != null ? ip : -1; - } else { - this.message = op; - this.op = ''; - this.ip = -1; - } -} - -util.inherits(ScriptError, Error); - -/** - * An error thrown from the coin selector. - * @exports FundingError - * @constructor - * @extends Error - * @param {String} msg - * @param {Amount} available - * @param {Amount} required - * @property {String} message - Error message. - * @property {Amount} availableFunds - * @property {Amount} requiredFunds - */ - -function FundingError(msg, available, required) { - Error.call(this); - - if (Error.captureStackTrace) - Error.captureStackTrace(this, FundingError); - - this.type = 'FundingError'; - this.message = msg; - this.availableFunds = -1; - this.requiredFunds = -1; - - if (available != null) { - this.message += ' (available=' + Amount.btc(available) + ','; - this.message += ' required=' + Amount.btc(required) + ')'; - this.availableFunds = available; - this.requiredFunds = required; - } -} - -util.inherits(FundingError, Error); - /* * Expose */ exports.VerifyError = VerifyError; exports.VerifyResult = VerifyResult; -exports.ScriptError = ScriptError; -exports.FundingError = FundingError; diff --git a/lib/btc/utils.js b/lib/btc/utils.js deleted file mode 100644 index 952882f8..00000000 --- a/lib/btc/utils.js +++ /dev/null @@ -1,324 +0,0 @@ -/*! - * utils.js - bitcoin-related utils for bcoin - * Copyright (c) 2014-2015, Fedor Indutny (MIT License) - * Copyright (c) 2014-2016, Christopher Jeffrey (MIT License). - * https://github.com/bcoin-org/bcoin - */ - -'use strict'; - -var assert = require('assert'); -var BN = require('bn.js'); -var constants = require('../protocol/constants'); -var util = require('../utils/util'); -var Amount = require('./amount'); -var utils = exports; - -/** - * Convert a compact number to a big number. - * Used for `block.bits` -> `target` conversion. - * @param {Number} compact - * @returns {BN} - */ - -utils.fromCompact = function fromCompact(compact) { - var exponent = compact >>> 24; - var negative = (compact >>> 23) & 1; - var mantissa = compact & 0x7fffff; - var num; - - if (compact === 0) - return new BN(0); - - // Logic ported from btcd since - // the bitcoind code is a nightmare. - if (exponent <= 3) { - mantissa >>>= 8 * (3 - exponent); - num = new BN(mantissa); - } else { - num = new BN(mantissa); - num.iushln(8 * (exponent - 3)); - } - - if (negative) - num.ineg(); - - return num; -}; - -/** - * Convert a big number to a compact number. - * Used for `target` -> `block.bits` conversion. - * @param {BN} num - * @returns {Number} - */ - -utils.toCompact = function toCompact(num) { - var mantissa, exponent, compact; - - if (num.cmpn(0) === 0) - return 0; - - exponent = num.byteLength(); - - // Logic ported from btcd since - // the bitcoind code is a nightmare. - if (exponent <= 3) { - mantissa = num.toNumber(); - mantissa <<= 8 * (3 - exponent); - } else { - mantissa = num.ushrn(8 * (exponent - 3)).toNumber(); - } - - if (mantissa & 0x800000) { - mantissa >>= 8; - exponent++; - } - - compact = (exponent << 24) | mantissa; - - if (num.isNeg()) - compact |= 0x800000; - - compact >>>= 0; - - return compact; -}; - -/** - * Verify proof-of-work. - * @returns {Boolean} - */ - -utils.verifyPOW = function verifyPOW(hash, bits) { - var target = utils.fromCompact(bits); - - if (target.isNeg() || target.cmpn(0) === 0) - return false; - - hash = new BN(hash, 'le'); - - if (hash.cmp(target) > 0) - return false; - - return true; -}; - -/** - * Calculate block subsidy. - * @param {Number} height - Reward era by height. - * @returns {Amount} - */ - -utils.getReward = function getReward(height, interval) { - var halvings = Math.floor(height / interval); - - assert(height >= 0, 'Bad height for reward.'); - - // BIP 42 (well, our own version of it, - // since we can only handle 32 bit shifts). - // https://github.com/bitcoin/bips/blob/master/bip-0042.mediawiki - if (halvings >= 33) - return 0; - - // We need to shift right by `halvings`, - // but 50 btc is a 33 bit number, so we - // cheat. We only start halving once the - // halvings are at least 1. - if (halvings === 0) - return 5000000000; - - return 2500000000 >>> (halvings - 1); -}; - -/** - * Calculate minimum fee based on rate and size. - * @param {Number?} size - * @param {Rate?} rate - Rate of satoshi per kB. - * @returns {Amount} fee - */ - -utils.getMinFee = function getMinFee(size, rate) { - var fee; - - if (rate == null) - rate = constants.tx.MIN_RELAY; - - fee = Math.floor(rate * size / 1000); - - if (fee === 0 && rate > 0) - fee = rate; - - return fee; -}; - -/** - * Calculate the minimum fee in order for the transaction - * to be relayable, but _round to the nearest kilobyte - * when taking into account size. - * @param {Number?} size - * @param {Rate?} rate - Rate of satoshi per kB. - * @returns {Amount} fee - */ - -utils.getRoundFee = function getRoundFee(size, rate) { - var fee; - - if (rate == null) - rate = constants.tx.MIN_RELAY; - - fee = rate * Math.ceil(size / 1000); - - if (fee === 0 && rate > 0) - fee = rate; - - return fee; -}; - -/** - * Calculate a fee rate based on size and fees. - * @param {Number} size - * @param {Amount} fee - * @returns {Rate} - */ - -utils.getRate = function getRate(size, fee) { - return Math.floor(fee * 1000 / size); -}; - -/** - * Safely convert satoshis to a BTC string. - * This function explicitly avoids any - * floating point arithmetic. - * @param {Amount} value - Satoshis. - * @returns {String} BTC string. - */ - -utils.btc = function btc(value) { - if (util.isFloat(value)) - return value; - return Amount.fromValue(value).toBTC(); -}; - -/** - * Safely convert a BTC string to satoshis. - * This function explicitly avoids any - * floating point arithmetic. It also does - * extra validation to ensure the resulting - * Number will be 53 bits or less. - * @param {String} value - BTC - * @returns {Amount} Satoshis. - * @throws on parse error - */ - -utils.satoshi = function satoshi(value) { - if (util.isInt(value)) - return value; - return Amount.fromBTC(value).toValue(); -}; - -/** - * Test and validate a satoshi value (Number). - * @param {Number?} value - * @returns {Boolean} - */ - -utils.isSatoshi = function isSatoshi(value) { - try { - Amount.fromValue(value); - return true; - } catch (e) { - return false; - } -}; - -/** - * Test and validate a BTC string. - * @param {String?} value - * @returns {Boolean} - */ - -utils.isBTC = function isBTC(value) { - try { - Amount.fromBTC(value); - return true; - } catch (e) { - return false; - } -}; - -/** - * Sort an array of transactions in dependency order. - * @param {TX[]} txs - * @returns {TX[]} - */ - -utils.sortTX = function sortTX(txs) { - var depMap = {}; - var count = {}; - var result = []; - var top = []; - var map = txs; - var i, j, tx, hash, input; - var prev, hasDeps, deps; - - if (Array.isArray(txs)) { - map = {}; - for (i = 0; i < txs.length; i++) { - tx = txs[i]; - hash = tx.hash('hex'); - map[hash] = tx; - } - } - - for (i = 0; i < txs.length; i++) { - tx = txs[i]; - hash = tx.hash('hex'); - hasDeps = false; - - count[hash] = 0; - - for (j = 0; j < tx.inputs.length; j++) { - input = tx.inputs[j]; - prev = input.prevout.hash; - - if (!map[prev]) - continue; - - count[hash] += 1; - hasDeps = true; - - if (!depMap[prev]) - depMap[prev] = []; - - depMap[prev].push(tx); - } - - if (hasDeps) - continue; - - top.push(tx); - } - - for (i = 0; i < top.length; i++) { - tx = top[i]; - hash = tx.hash('hex'); - - result.push(tx); - - deps = depMap[hash]; - - if (!deps) - continue; - - for (j = 0; j < deps.length; j++) { - tx = deps[j]; - hash = tx.hash('hex'); - - if (--count[hash] === 0) - top.push(tx); - } - } - - return result; -}; diff --git a/lib/coins/coins.js b/lib/coins/coins.js index c10412df..21d97b1d 100644 --- a/lib/coins/coins.js +++ b/lib/coins/coins.js @@ -8,7 +8,6 @@ var util = require('../utils/util'); var assert = require('assert'); -var constants = require('../protocol/constants'); var Coin = require('../primitives/coin'); var Output = require('../primitives/output'); var BufferReader = require('../utils/reader'); @@ -35,7 +34,7 @@ function Coins(options) { return new Coins(options); this.version = 1; - this.hash = constants.NULL_HASH; + this.hash = encoding.NULL_HASH; this.height = -1; this.coinbase = true; this.outputs = []; diff --git a/lib/hd/common.js b/lib/hd/common.js index 0414b3c5..f129aa18 100644 --- a/lib/hd/common.js +++ b/lib/hd/common.js @@ -7,9 +7,48 @@ 'use strict'; var LRU = require('../utils/lru'); -var constants = require('../protocol/constants'); var common = exports; +/** + * Index at which hardening begins. + * @const {Number} + * @default + */ + +common.HARDENED = 0x80000000; + +/** + * Max index (u32max + 1). + * @const {Number} + * @default + */ + +common.MAX_INDEX = 0x100000000; + +/** + * Min entropy bits. + * @const {Number} + * @default + */ + +common.MIN_ENTROPY = 128; + +/** + * Max entropy bits. + * @const {Number} + * @default + */ + +common.MAX_ENTROPY = 512; + +/** + * Seed salt for key derivation ("Bitcoin seed"). + * @const {Buffer} + * @default + */ + +common.SEED_SALT = new Buffer('Bitcoin seed', 'ascii'); + /** * LRU cache to avoid deriving keys twice. * @type {LRU} @@ -32,7 +71,7 @@ common.parsePath = function parsePath(path, max) { var i, hardened, index; if (max == null) - max = constants.hd.MAX_INDEX; + max = common.MAX_INDEX; if (root !== 'm' && root !== 'M' @@ -54,7 +93,7 @@ common.parsePath = function parsePath(path, max) { index = parseInt(index, 10); if (hardened) - index += constants.hd.HARDENED; + index += common.HARDENED; if (!(index >= 0 && index < max)) throw new Error('Index out of range.'); @@ -86,10 +125,10 @@ common.isMaster = function isMaster(key) { common.isAccount44 = function isAccount44(key, accountIndex) { if (accountIndex != null) { - if (key.childIndex !== constants.hd.HARDENED + accountIndex) + if (key.childIndex !== common.HARDENED + accountIndex) return false; } - return key.depth === 3 && key.childIndex >= constants.hd.HARDENED; + return key.depth === 3 && key.childIndex >= common.HARDENED; }; /** @@ -99,5 +138,5 @@ common.isAccount44 = function isAccount44(key, accountIndex) { */ common.isPurpose45 = function isPurpose45(key) { - return key.depth === 1 && key.childIndex === constants.hd.HARDENED + 45; + return key.depth === 1 && key.childIndex === common.HARDENED + 45; }; diff --git a/lib/hd/mnemonic.js b/lib/hd/mnemonic.js index a1e5a4b4..e9990a9b 100644 --- a/lib/hd/mnemonic.js +++ b/lib/hd/mnemonic.js @@ -6,14 +6,14 @@ 'use strict'; +var assert = require('assert'); var util = require('../utils/util'); var crypto = require('../crypto/crypto'); -var assert = require('assert'); -var constants = require('../protocol/constants'); var StaticWriter = require('../utils/staticwriter'); var BufferReader = require('../utils/reader'); var encoding = require('../utils/encoding'); var wordlist = require('./wordlist'); +var common = require('./common'); var nfkd = require('../utils/nfkd'); /** @@ -37,7 +37,7 @@ function Mnemonic(options) { if (!(this instanceof Mnemonic)) return new Mnemonic(options); - this.bits = constants.hd.MIN_ENTROPY; + this.bits = common.MIN_ENTROPY; this.language = 'english'; this.entropy = null; this.phrase = null; @@ -74,8 +74,8 @@ Mnemonic.prototype.fromOptions = function fromOptions(options) { if (options.bits != null) { assert(util.isNumber(options.bits)); - assert(options.bits >= constants.hd.MIN_ENTROPY); - assert(options.bits <= constants.hd.MAX_ENTROPY); + assert(options.bits >= common.MIN_ENTROPY); + assert(options.bits <= common.MAX_ENTROPY); assert(options.bits % 32 === 0); this.bits = options.bits; } @@ -119,7 +119,7 @@ Mnemonic.fromOptions = function fromOptions(options) { */ Mnemonic.prototype.destroy = function destroy() { - this.bits = constants.hd.MIN_ENTROPY; + this.bits = common.MIN_ENTROPY; this.language = 'english'; if (this.entropy) { crypto.cleanse(this.entropy); @@ -238,8 +238,8 @@ Mnemonic.prototype.fromPhrase = function fromPhrase(phrase) { cbytes = Math.ceil(cbits / 8); bits -= cbits; - assert(bits >= constants.hd.MIN_ENTROPY); - assert(bits <= constants.hd.MAX_ENTROPY); + assert(bits >= common.MIN_ENTROPY); + assert(bits <= common.MAX_ENTROPY); assert(bits % 32 === 0); assert(cbits !== 0, 'Invalid checksum.'); @@ -308,8 +308,8 @@ Mnemonic.fromPhrase = function fromPhrase(phrase) { Mnemonic.prototype.fromEntropy = function fromEntropy(entropy, lang) { assert(Buffer.isBuffer(entropy)); - assert(entropy.length * 8 >= constants.hd.MIN_ENTROPY); - assert(entropy.length * 8 <= constants.hd.MAX_ENTROPY); + assert(entropy.length * 8 >= common.MIN_ENTROPY); + assert(entropy.length * 8 <= common.MAX_ENTROPY); assert((entropy.length * 8) % 32 === 0); assert(!lang || Mnemonic.languages.indexOf(lang) !== -1); @@ -390,8 +390,8 @@ Mnemonic.prototype.fromJSON = function fromJSON(json) { assert(typeof json.entropy === 'string'); assert(typeof json.phrase === 'string'); assert(typeof json.passphrase === 'string'); - assert(json.bits >= constants.hd.MIN_ENTROPY); - assert(json.bits <= constants.hd.MAX_ENTROPY); + assert(json.bits >= common.MIN_ENTROPY); + assert(json.bits <= common.MAX_ENTROPY); assert(json.bits % 32 === 0); assert(json.bits / 8 === json.entropy.length / 2); @@ -471,8 +471,8 @@ Mnemonic.prototype.fromReader = function fromReader(br) { this.passphrase = br.readVarString('utf8'); assert(this.language); - assert(this.bits >= constants.hd.MIN_ENTROPY); - assert(this.bits <= constants.hd.MAX_ENTROPY); + assert(this.bits >= common.MIN_ENTROPY); + assert(this.bits <= common.MAX_ENTROPY); assert(this.bits % 32 === 0); return this; diff --git a/lib/hd/private.js b/lib/hd/private.js index 1dc7d27f..e82141c1 100644 --- a/lib/hd/private.js +++ b/lib/hd/private.js @@ -6,27 +6,20 @@ 'use strict'; +var assert = require('assert'); var util = require('../utils/util'); var crypto = require('../crypto/crypto'); var ec = require('../crypto/ec'); -var assert = require('assert'); -var constants = require('../protocol/constants'); var networks = require('../protocol/networks'); var Network = require('../protocol/network'); var StaticWriter = require('../utils/staticwriter'); var BufferReader = require('../utils/reader'); var StaticWriter = require('../utils/staticwriter'); var base58 = require('../utils/base58'); +var encoding = require('../utils/encoding'); +var common = require('./common'); var Mnemonic = require('./mnemonic'); var HDPublicKey = require('./public'); -var common = require('./common'); - -/* - * Constants - */ - -var FINGER_PRINT = new Buffer('00000000', 'hex'); -var SEED_SALT = new Buffer('Bitcoin seed', 'ascii'); /** * HDPrivateKey @@ -55,12 +48,12 @@ function HDPrivateKey(options) { this.network = Network.primary; this.depth = 0; - this.parentFingerPrint = FINGER_PRINT; + this.parentFingerPrint = encoding.ZERO_U32; this.childIndex = 0; - this.chainCode = constants.ZERO_HASH; - this.privateKey = constants.ZERO_HASH; + this.chainCode = encoding.ZERO_HASH; + this.privateKey = encoding.ZERO_HASH; - this.publicKey = constants.ZERO_KEY; + this.publicKey = encoding.ZERO_KEY; this.fingerPrint = null; this.mnemonic = null; @@ -229,12 +222,12 @@ HDPrivateKey.prototype.derive = function derive(index, hardened, cache) { if (typeof index === 'string') return this.derivePath(index, cache); - hardened = index >= constants.hd.HARDENED ? true : hardened; + hardened = index >= common.HARDENED ? true : hardened; - if (index < constants.hd.HARDENED && hardened) - index += constants.hd.HARDENED; + if (index < common.HARDENED && hardened) + index += common.HARDENED; - if (!(index >= 0 && index < constants.hd.MAX_INDEX)) + if (!(index >= 0 && index < common.MAX_INDEX)) throw new Error('Index out of range.'); if (this.depth >= 0xff) @@ -416,7 +409,7 @@ HDPrivateKey.isValidPath = function isValidPath(path) { return false; try { - common.parsePath(path, constants.hd.MAX_INDEX); + common.parsePath(path, common.MAX_INDEX); return true; } catch (e) { return false; @@ -431,7 +424,7 @@ HDPrivateKey.isValidPath = function isValidPath(path) { */ HDPrivateKey.prototype.derivePath = function derivePath(path, cache) { - var indexes = common.parsePath(path, constants.hd.MAX_INDEX); + var indexes = common.parsePath(path, common.MAX_INDEX); var key = this; var i; @@ -511,12 +504,12 @@ HDPrivateKey.prototype.fromSeed = function fromSeed(seed, network) { assert(Buffer.isBuffer(seed)); - if (!(seed.length * 8 >= constants.hd.MIN_ENTROPY - && seed.length * 8 <= constants.hd.MAX_ENTROPY)) { + if (!(seed.length * 8 >= common.MIN_ENTROPY + && seed.length * 8 <= common.MAX_ENTROPY)) { throw new Error('Entropy not in range.'); } - hash = crypto.hmac('sha512', seed, SEED_SALT); + hash = crypto.hmac('sha512', seed, common.SEED_SALT); left = hash.slice(0, 32); right = hash.slice(32, 64); diff --git a/lib/hd/public.js b/lib/hd/public.js index 22590e2f..81e8bdb6 100644 --- a/lib/hd/public.js +++ b/lib/hd/public.js @@ -6,24 +6,18 @@ 'use strict'; +var assert = require('assert'); var util = require('../utils/util'); var crypto = require('../crypto/crypto'); var ec = require('../crypto/ec'); -var assert = require('assert'); -var constants = require('../protocol/constants'); var networks = require('../protocol/networks'); var Network = require('../protocol/network'); var StaticWriter = require('../utils/staticwriter'); var BufferReader = require('../utils/reader'); var base58 = require('../utils/base58'); +var encoding = require('../utils/encoding'); var common = require('./common'); -/* - * Constants - */ - -var FINGER_PRINT = new Buffer('00000000', 'hex'); - /** * HDPublicKey * @exports HDPublicKey @@ -49,10 +43,10 @@ function HDPublicKey(options) { this.network = Network.primary; this.depth = 0; - this.parentFingerPrint = FINGER_PRINT; + this.parentFingerPrint = encoding.ZERO_U32; this.childIndex = 0; - this.chainCode = constants.ZERO_HASH; - this.publicKey = constants.ZERO_KEY; + this.chainCode = encoding.ZERO_HASH; + this.publicKey = encoding.ZERO_KEY; this.fingerPrint = null; @@ -187,7 +181,7 @@ HDPublicKey.prototype.derive = function derive(index, hardened, cache) { if (typeof index === 'string') return this.derivePath(index, cache); - if (index >= constants.hd.HARDENED || hardened) + if (index >= common.HARDENED || hardened) throw new Error('Index out of range.'); if (index < 0) @@ -316,7 +310,7 @@ HDPublicKey.isValidPath = function isValidPath(path) { return false; try { - common.parsePath(path, constants.hd.HARDENED); + common.parsePath(path, common.HARDENED); return true; } catch (e) { return false; @@ -332,7 +326,7 @@ HDPublicKey.isValidPath = function isValidPath(path) { */ HDPublicKey.prototype.derivePath = function derivePath(path, cache) { - var indexes = common.parsePath(path, constants.hd.HARDENED); + var indexes = common.parsePath(path, common.HARDENED); var key = this; var i; diff --git a/lib/http/rpc.js b/lib/http/rpc.js index c092a193..af1ee4bb 100644 --- a/lib/http/rpc.js +++ b/lib/http/rpc.js @@ -8,7 +8,6 @@ var fs = require('fs'); var EventEmitter = require('events').EventEmitter; - var util = require('../utils/util'); var co = require('../utils/co'); var crypto = require('../crypto/crypto'); @@ -32,6 +31,9 @@ var Output = require('../primitives/output'); var TX = require('../primitives/tx'); var Logger = require('../node/logger'); var IP = require('../utils/ip'); +var encoding = require('../utils/encoding'); +var consensus = require('../protocol/consensus'); +var USER_VERSION = require('../../package.json').version; /** * RPC @@ -322,8 +324,8 @@ RPC.prototype.getinfo = co(function* getinfo(args) { balance = yield this.wallet.getBalance(); return { - version: constants.USER_VERSION, - protocolversion: constants.VERSION, + version: USER_VERSION, + protocolversion: this.pool.protoVersion, walletversion: 0, balance: Amount.btc(balance.unconfirmed, true), blocks: this.chain.height, @@ -375,14 +377,14 @@ RPC.prototype.getnetworkinfo = co(function* getnetworkinfo(args) { throw new RPCError('getnetworkinfo'); return { - version: constants.USER_VERSION, - subversion: constants.USER_AGENT, - protocolversion: constants.VERSION, - localservices: this.pool.services, + version: USER_VERSION, + subversion: this.pool.userAgent, + protocolversion: this.pool.protoVersion, + localservices: this.pool.address.services, timeoffset: this.network.time.offset, connections: this.pool.peers.size(), networks: [], - relayfee: Amount.btc(this.network.getMinRelay(), true), + relayfee: Amount.btc(this.network.minRelay, true), localaddresses: [], warnings: '' }; @@ -889,7 +891,7 @@ RPC.prototype._headerToJSON = co(function* _headerToJSON(entry) { bits: entry.bits, difficulty: this._getDifficulty(entry), chainwork: entry.chainwork.toString('hex', 64), - previousblockhash: entry.prevBlock !== constants.NULL_HASH + previousblockhash: entry.prevBlock !== encoding.NULL_HASH ? util.revHex(entry.prevBlock) : null, nextblockhash: nextHash ? util.revHex(nextHash) : null @@ -920,7 +922,7 @@ RPC.prototype._blockToJSON = co(function* _blockToJSON(entry, block, txDetails) bits: entry.bits, difficulty: this._getDifficulty(entry), chainwork: entry.chainwork.toString('hex', 64), - previousblockhash: entry.prevBlock !== constants.NULL_HASH + previousblockhash: entry.prevBlock !== encoding.NULL_HASH ? util.revHex(entry.prevBlock) : null, nextblockhash: nextHash ? util.revHex(nextHash) : null @@ -1441,7 +1443,7 @@ RPC.prototype.submitblock = co(function* submitblock(args) { this.logger.debug(tx); // Recreate witness nonce (all zeroes). - tx.inputs[0].witness.set(0, constants.ZERO_HASH); + tx.inputs[0].witness.set(0, encoding.ZERO_HASH); tx.inputs[0].witness.compile(); tx.refresh(); @@ -1560,7 +1562,7 @@ RPC.prototype._template = co(function* _template(version, coinbase, rules) { RPC.prototype.__template = co(function* _template(version, coinbase, rules) { var attempt = yield this._getAttempt(false); - var scale = attempt.witness ? 1 : constants.WITNESS_SCALE_FACTOR; + var scale = attempt.witness ? 1 : consensus.WITNESS_SCALE_FACTOR; var block = attempt.block; var mutable = ['time', 'transactions', 'prevblock']; var txs = []; @@ -1652,9 +1654,9 @@ RPC.prototype.__template = co(function* _template(version, coinbase, rules) { mintime: block.ts, maxtime: block.ts + 7200, expires: block.ts + 7200, - sigoplimit: constants.block.MAX_SIGOPS_COST / scale | 0, - sizelimit: constants.block.MAX_RAW_SIZE, - weightlimit: constants.block.MAX_WEIGHT, + sigoplimit: consensus.MAX_BLOCK_SIGOPS_COST / scale | 0, + sizelimit: consensus.MAX_RAW_BLOCK_SIZE, + weightlimit: consensus.MAX_BLOCK_WEIGHT, longpollid: this.chain.tip.rhash() + util.pad32(this._totalTX()), submitold: false, coinbaseaux: { @@ -2187,7 +2189,7 @@ RPC.prototype.signrawtransaction = co(function* signrawtransaction(args) { }); RPC.prototype._signrawtransaction = co(function* signrawtransaction(wallet, tx, args) { - var type = constants.hashType.ALL; + var type = Script.hashType.ALL; var keys = []; var keyMap = {}; var i, j, k, secret, key; @@ -2262,7 +2264,7 @@ RPC.prototype._signrawtransaction = co(function* signrawtransaction(wallet, tx, if (args.length > 3) { parts = toString(args[3]).split('|'); - type = constants.hashType[parts[0]]; + type = Script.hashType[parts[0]]; if (type == null) throw new RPCError('Invalid parameter'); @@ -2273,7 +2275,7 @@ RPC.prototype._signrawtransaction = co(function* signrawtransaction(wallet, tx, if (parts.length === 2) { if (parts[1] !== 'ANYONECANPAY') throw new RPCError('Invalid parameter'); - type |= constants.hashType.ANYONECANPAY; + type |= Script.hashType.ANYONECANPAY; } } @@ -2373,7 +2375,7 @@ RPC.prototype._createRedeem = co(function* _createRedeem(args) { throw new RPCError('Invalid parameters.'); } - if (script.getSize() > constants.script.MAX_PUSH) + if (script.getSize() > consensus.MAX_SCRIPT_PUSH) throw new RPCError('Redeem script exceeds size limit.'); return script; @@ -2726,7 +2728,7 @@ RPC.prototype.dumpwallet = co(function* dumpwallet(args) { file = toString(args[0]); time = util.date(); out = [ - util.fmt('# Wallet Dump created by BCoin %s', constants.USER_VERSION), + util.fmt('# Wallet Dump created by BCoin %s', USER_VERSION), util.fmt('# * Created on %s', time), util.fmt('# * Best block at time of backup was %d (%s),', this.chain.height, this.chain.tip.rhash()), @@ -3535,7 +3537,7 @@ RPC.prototype.listsinceblock = co(function* listsinceblock(args) { transactions: out, lastblock: highest && highest.block ? util.revHex(highest.block) - : constants.NULL_HASH + : encoding.NULL_HASH }; }); diff --git a/lib/http/server.js b/lib/http/server.js index 5cb80df1..b9c3ecf8 100644 --- a/lib/http/server.js +++ b/lib/http/server.js @@ -12,7 +12,6 @@ var EventEmitter = require('events').EventEmitter; var assert = require('assert'); -var constants = require('../protocol/constants'); var HTTPBase = require('./base'); var util = require('../utils/util'); var co = require('../utils/co'); @@ -26,6 +25,7 @@ var Outpoint = require('../primitives/outpoint'); var HD = require('../hd/hd'); var Script = require('../script/script'); var crypto = require('../crypto/crypto'); +var USER_VERSION = require('../../package.json').version; var con = co.con; var RPC; @@ -126,8 +126,8 @@ HTTPServer.prototype._init = function _init() { return res.end(); } - res.setHeader('X-Bcoin-Version', constants.USER_VERSION); - res.setHeader('X-Bcoin-Agent', constants.USER_AGENT); + res.setHeader('X-Bcoin-Version', USER_VERSION); + res.setHeader('X-Bcoin-Agent', this.pool.userAgent); res.setHeader('X-Bcoin-Network', this.network.type); res.setHeader('X-Bcoin-Height', this.chain.height + ''); res.setHeader('X-Bcoin-Tip', util.revHex(this.chain.tip.hash)); @@ -633,8 +633,8 @@ HTTPServer.prototype._init = function _init() { var size = this.mempool ? this.mempool.getSize() : 0; send(200, { - version: constants.USER_VERSION, - agent: constants.USER_AGENT, + version: USER_VERSION, + agent: this.pool.userAgent, network: this.network.type, chain: { height: this.chain.height, @@ -1314,8 +1314,8 @@ HTTPServer.prototype._initIO = function _initIO() { }); socket.emit('version', { - version: constants.USER_VERSION, - agent: constants.USER_AGENT, + version: USER_VERSION, + agent: self.pool.userAgent, network: self.network.type }); }); diff --git a/lib/mempool/fees.js b/lib/mempool/fees.js index 0bde4c27..7c9e5542 100644 --- a/lib/mempool/fees.js +++ b/lib/mempool/fees.js @@ -8,9 +8,10 @@ 'use strict'; -var util = require('../utils/util'); var assert = require('assert'); -var constants = require('../protocol/constants'); +var util = require('../utils/util'); +var consensus = require('../protocol/consensus'); +var policy = require('../protocol/policy'); var BufferReader = require('../utils/reader'); var StaticWriter = require('../utils/staticwriter'); var encoding = require('../utils/encoding'); @@ -32,13 +33,13 @@ var SUFFICIENT_FEETXS = 1; var SUFFICIENT_PRITXS = 0.2; var MIN_FEERATE = 10; var MAX_FEERATE = 1e7; -var INF_FEERATE = constants.MAX_MONEY; +var INF_FEERATE = consensus.MAX_MONEY; var MIN_PRIORITY = 10; var MAX_PRIORITY = 1e16; -var INF_PRIORITY = 1e9 * constants.MAX_MONEY; +var INF_PRIORITY = 1e9 * consensus.MAX_MONEY; var FEE_SPACING = 1.1; var PRI_SPACING = 2; -var FREE_THRESHOLD = constants.tx.FREE_THRESHOLD; +var FREE_THRESHOLD = policy.FREE_THRESHOLD; /** * Confirmation stats. diff --git a/lib/mempool/mempool.js b/lib/mempool/mempool.js index 9c937780..a77bec7a 100644 --- a/lib/mempool/mempool.js +++ b/lib/mempool/mempool.js @@ -6,18 +6,18 @@ 'use strict'; +var assert = require('assert'); var AsyncObject = require('../utils/async'); var constants = require('../protocol/constants'); +var policy = require('../protocol/policy'); var util = require('../utils/util'); var co = require('../utils/co'); -var assert = require('assert'); var crypto = require('../crypto/crypto'); var errors = require('../btc/errors'); -var VerifyError = errors.VerifyError; -var VerifyResult = errors.VerifyResult; var Bloom = require('../utils/bloom'); var Address = require('../primitives/address'); var Coin = require('../primitives/coin'); +var Script = require('../script/script'); var Locker = require('../utils/locker'); var Outpoint = require('../primitives/outpoint'); var TX = require('../primitives/tx'); @@ -26,6 +26,8 @@ var TXMeta = require('../primitives/txmeta'); var MempoolEntry = require('./mempoolentry'); var CoinView = require('../coins/coinview'); var Coins = require('../coins/coins'); +var VerifyError = errors.VerifyError; +var VerifyResult = errors.VerifyResult; /** * Represents a mempool. @@ -106,15 +108,48 @@ function Mempool(options) { this.paranoid = !!this.options.paranoid; this.replaceByFee = !!this.options.replaceByFee; - this.maxSize = options.maxSize || constants.mempool.MAX_MEMPOOL_SIZE; - this.maxOrphans = options.maxOrphans || constants.mempool.MAX_ORPHAN_TX; - this.maxAncestors = options.maxAncestors || constants.mempool.ANCESTOR_LIMIT; - this.expiryTime = options.expiryTime || constants.mempool.MEMPOOL_EXPIRY; + this.maxSize = options.maxSize || Mempool.MAX_SIZE; + this.maxOrphans = options.maxOrphans || Mempool.MAX_ORPHANS; + this.maxAncestors = options.maxAncestors || Mempool.ANCESTOR_LIMIT; + this.expiryTime = options.expiryTime || Mempool.EXPIRY_TIME; this.minRelay = this.network.minRelay; } util.inherits(Mempool, AsyncObject); +/** + * Default ancestor limit. + * @const {Number} + * @default + */ + +Mempool.ANCESTOR_LIMIT = 25; + +/** + * Default maximum mempool size in bytes. + * @const {Number} + * @default + */ + +Mempool.MAX_SIZE = 100 * 1000000; + +/** + * Time at which transactions + * fall out of the mempool. + * @const {Number} + * @default + */ + +Mempool.EXPIRY_TIME = 72 * 60 * 60; + +/** + * Maximum number of orphan transactions. + * @const {Number} + * @default + */ + +Mempool.MAX_ORPHANS = 100; + /** * Open the chain, wait for the database to load. * @alias Mempool#open @@ -657,7 +692,7 @@ Mempool.prototype.addTX = co(function* addTX(tx) { */ Mempool.prototype._addTX = co(function* _addTX(tx) { - var lockFlags = constants.flags.STANDARD_LOCKTIME_FLAGS; + var lockFlags = constants.lockFlags.STANDARD_LOCKTIME_FLAGS; var hash = tx.hash('hex'); var ret = new VerifyResult(); var entry, view, missing; @@ -796,8 +831,8 @@ Mempool.prototype._addTX = co(function* _addTX(tx) { Mempool.prototype.verify = co(function* verify(entry, view) { var height = this.chain.height + 1; - var lockFlags = constants.flags.STANDARD_LOCKTIME_FLAGS; - var flags = constants.flags.STANDARD_VERIFY_FLAGS; + var lockFlags = constants.lockFlags.STANDARD_LOCKTIME_FLAGS; + var flags = Script.flags.STANDARD_VERIFY_FLAGS; var ret = new VerifyResult(); var tx = entry.tx; var now, minFee, result; @@ -831,7 +866,7 @@ Mempool.prototype.verify = co(function* verify(entry, view) { } // Annoying process known as sigops counting. - if (entry.sigops > constants.tx.MAX_SIGOPS_COST) { + if (entry.sigops > policy.MAX_TX_SIGOPS_COST) { throw new VerifyError(tx, 'nonstandard', 'bad-txns-too-many-sigops', @@ -895,8 +930,8 @@ Mempool.prototype.verify = co(function* verify(entry, view) { throw err; // Try without segwit and cleanstack. - flags &= ~constants.flags.VERIFY_WITNESS; - flags &= ~constants.flags.VERIFY_CLEANSTACK; + flags &= ~Script.flags.VERIFY_WITNESS; + flags &= ~Script.flags.VERIFY_CLEANSTACK; result = yield this.verifyResult(tx, view, flags); // If it failed, the first verification @@ -906,7 +941,7 @@ Mempool.prototype.verify = co(function* verify(entry, view) { // If it succeeded, segwit may be causing the // failure. Try with segwit but without cleanstack. - flags |= constants.flags.VERIFY_CLEANSTACK; + flags |= Script.flags.VERIFY_CLEANSTACK; result = yield this.verifyResult(tx, view, flags); // Cleanstack was causing the failure. @@ -920,7 +955,7 @@ Mempool.prototype.verify = co(function* verify(entry, view) { // Paranoid checks. if (this.paranoid) { - flags = constants.flags.MANDATORY_VERIFY_FLAGS; + flags = Script.flags.MANDATORY_VERIFY_FLAGS; result = yield this.verifyResult(tx, view, flags); assert(result, 'BUG: Verify failed for mandatory but not standard.'); } @@ -959,14 +994,14 @@ Mempool.prototype.verifyInputs = co(function* verifyInputs(tx, view, flags) { if (yield tx.verifyAsync(view, flags)) return; - if (!(flags & constants.flags.UNSTANDARD_VERIFY_FLAGS)) { + if (!(flags & Script.flags.UNSTANDARD_VERIFY_FLAGS)) { throw new VerifyError(tx, 'nonstandard', 'non-mandatory-script-verify-flag', 0); } - flags &= ~constants.flags.UNSTANDARD_VERIFY_FLAGS; + flags &= ~Script.flags.UNSTANDARD_VERIFY_FLAGS; if (yield tx.verifyAsync(view, flags)) { throw new VerifyError(tx, @@ -1318,7 +1353,7 @@ Mempool.prototype.storeOrphan = function storeOrphan(tx, missing) { var hash = tx.hash('hex'); var i, prev; - if (tx.getWeight() > constants.tx.MAX_WEIGHT) { + if (tx.getWeight() > policy.MAX_TX_WEIGHT) { this.logger.debug('Ignoring large orphan: %s', tx.txid()); if (!tx.hasWitness()) this.rejects.add(tx.hash()); diff --git a/lib/mempool/mempoolentry.js b/lib/mempool/mempoolentry.js index 47bcf3f0..92eb1911 100644 --- a/lib/mempool/mempoolentry.js +++ b/lib/mempool/mempoolentry.js @@ -7,8 +7,9 @@ 'use strict'; var constants = require('../protocol/constants'); +var policy = require('../protocol/policy'); var util = require('../utils/util'); -var btcutils = require('../btc/utils'); +var Script = require('../script/script'); /** * Represents a mempool entry. @@ -82,7 +83,7 @@ MempoolEntry.fromOptions = function fromOptions(options) { */ MempoolEntry.prototype.fromTX = function fromTX(tx, view, height) { - var flags = constants.flags.STANDARD_VERIFY_FLAGS; + var flags = Script.flags.STANDARD_VERIFY_FLAGS; var priority = tx.getPriority(view, height); var value = tx.getChainValue(view, height); var sigops = tx.getSigopsCost(view, flags); @@ -156,7 +157,7 @@ MempoolEntry.prototype.getFee = function getFee() { */ MempoolEntry.prototype.getRate = function getRate() { - return btcutils.getRate(this.size, this.fee); + return policy.getRate(this.size, this.fee); }; /** @@ -169,7 +170,7 @@ MempoolEntry.prototype.getRate = function getRate() { MempoolEntry.prototype.isFree = function isFree(height) { var priority = this.getPriority(height); - return priority > constants.tx.FREE_THRESHOLD; + return priority > policy.FREE_THRESHOLD; }; /* diff --git a/lib/mining/miner.js b/lib/mining/miner.js index 60bb55a1..6b02e475 100644 --- a/lib/mining/miner.js +++ b/lib/mining/miner.js @@ -7,14 +7,15 @@ 'use strict'; +var assert = require('assert'); var util = require('../utils/util'); var co = require('../utils/co'); -var assert = require('assert'); -var constants = require('../protocol/constants'); var AsyncObject = require('../utils/async'); var Address = require('../primitives/address'); var MinerBlock = require('./minerblock'); var BlockEntry = MinerBlock.BlockEntry; +var consensus = require('../protocol/consensus'); +var policy = require('../protocol/policy'); /** * A bitcoin miner (supports mining witness blocks). @@ -53,10 +54,10 @@ function Miner(options) { this.coinbaseFlags = new Buffer('mined by bcoin', 'ascii'); this.minWeight = 0; - this.maxWeight = 750000 * constants.WITNESS_SCALE_FACTOR; - this.maxSigops = constants.block.MAX_SIGOPS_COST; - this.priorityWeight = 50000 * constants.WITNESS_SCALE_FACTOR; - this.minPriority = constants.tx.FREE_THRESHOLD; + this.maxWeight = 750000 * consensus.WITNESS_SCALE_FACTOR; + this.maxSigops = consensus.MAX_BLOCK_SIGOPS_COST; + this.priorityWeight = 50000 * consensus.WITNESS_SCALE_FACTOR; + this.minPriority = policy.FREE_THRESHOLD; this._initOptions(options); this._init(); @@ -106,7 +107,7 @@ Miner.prototype._initOptions = function _initOptions(options) { if (options.maxSigops != null) { assert(util.isNumber(options.maxSigops)); - assert(options.maxSigops <= constants.block.MAX_SIGOPS_COST); + assert(options.maxSigops <= consensus.MAX_BLOCK_SIGOPS_COST); this.maxSigops = options.maxSigops; } diff --git a/lib/mining/minerblock.js b/lib/mining/minerblock.js index 73ee7f27..b3db5ef9 100644 --- a/lib/mining/minerblock.js +++ b/lib/mining/minerblock.js @@ -8,13 +8,11 @@ 'use strict'; var assert = require('assert'); +var BN = require('bn.js'); var util = require('../utils/util'); -var btcutils = require('../btc/utils'); var co = require('../utils/co'); var StaticWriter = require('../utils/staticwriter'); -var constants = require('../protocol/constants'); var Network = require('../protocol/network'); -var BN = require('bn.js'); var EventEmitter = require('events').EventEmitter; var TX = require('../primitives/tx'); var Block = require('../primitives/block'); @@ -22,6 +20,8 @@ var Input = require('../primitives/input'); var Output = require('../primitives/output'); var mine = require('./mine'); var workerPool = require('../workers/workerpool').pool; +var consensus = require('../protocol/consensus'); +var encoding = require('../utils/encoding'); /** * MinerBlock @@ -52,7 +52,7 @@ function MinerBlock(options) { this.version = options.version; this.height = options.tip.height + 1; this.bits = options.bits; - this.target = btcutils.fromCompact(this.bits).toArrayLike(Buffer, 'le', 32); + this.target = consensus.fromCompact(this.bits).toArrayLike(Buffer, 'le', 32); this.locktime = options.locktime; this.flags = options.flags; this.nonce1 = 0; @@ -62,7 +62,7 @@ function MinerBlock(options) { this.witness = options.witness; this.address = options.address; this.network = Network.get(options.network); - this.reward = btcutils.getReward(this.height, this.network.halvingInterval); + this.reward = consensus.getReward(this.height, this.network.halvingInterval); this.destroyed = false; this.committed = false; @@ -115,7 +115,7 @@ MinerBlock.prototype.getRate = function() { */ MinerBlock.prototype._init = function _init() { - var scale = constants.WITNESS_SCALE_FACTOR; + var scale = consensus.WITNESS_SCALE_FACTOR; var block = this.block; var cb = this.coinbase; var input, output, nonce; @@ -123,7 +123,7 @@ MinerBlock.prototype._init = function _init() { // Setup our block. block.version = this.version; block.prevBlock = this.tip.hash; - block.merkleRoot = constants.NULL_HASH; + block.merkleRoot = encoding.NULL_HASH; block.ts = Math.max(this.network.now(), this.tip.ts + 1); block.bits = this.bits; block.nonce = 0; @@ -138,6 +138,7 @@ MinerBlock.prototype._init = function _init() { // miner succeeded. input.script.set(1, this.coinbaseFlags); + // Smaller nonce for good measure. input.script.set(2, util.nonce().slice(0, 4)); // extraNonce - incremented when @@ -183,9 +184,6 @@ MinerBlock.prototype._init = function _init() { // 4 extra bytes for varint tx count. this.weight += 4 * scale; - // 8 extra bytes for extra nonce. - this.weight += 8 * scale; - // Initialize sigops weight. this.sigops = cb.getSigopsCost(null, this.flags); }; @@ -301,10 +299,10 @@ MinerBlock.prototype.addTX = function addTX(tx, view) { if (!tx.isFinal(this.height, this.locktime)) return false; - if (this.weight + weight > constants.block.MAX_WEIGHT) + if (this.weight + weight > consensus.MAX_BLOCK_WEIGHT) return false; - if (this.sigops + sigops > constants.block.MAX_SIGOPS_COST) + if (this.sigops + sigops > consensus.MAX_BLOCK_SIGOPS_COST) return false; if (!this.witness && tx.hasWitness()) diff --git a/lib/net/bip150.js b/lib/net/bip150.js index 1b714743..934a5ebe 100644 --- a/lib/net/bip150.js +++ b/lib/net/bip150.js @@ -9,15 +9,15 @@ 'use strict'; var EventEmitter = require('events').EventEmitter; +var assert = require('assert'); var util = require('../utils/util'); var co = require('../utils/co'); var crypto = require('../crypto/crypto'); var packets = require('./packets'); -var assert = require('assert'); -var constants = require('../protocol/constants'); var ec = require('../crypto/ec'); var StaticWriter = require('../utils/staticwriter'); var base58 = require('../utils/base58'); +var encoding = require('../utils/encoding'); /** * Represents a BIP150 input and output stream. @@ -85,13 +85,13 @@ BIP150.prototype.challenge = function challenge(hash) { assert(!this.challengeReceived, 'Peer challenged twice.'); this.challengeReceived = true; - if (util.equal(hash, constants.ZERO_HASH)) + if (util.equal(hash, encoding.ZERO_HASH)) throw new Error('Auth failure.'); msg = this.hash(this.input.sid, type, this.publicKey); if (!crypto.ccmp(hash, msg)) - return constants.ZERO_SIG64; + return encoding.ZERO_SIG64; if (this.isAuthed()) { this.auth = true; @@ -112,7 +112,7 @@ BIP150.prototype.reply = function reply(data) { assert(!this.replyReceived, 'Peer replied twice.'); this.replyReceived = true; - if (util.equal(data, constants.ZERO_SIG64)) + if (util.equal(data, encoding.ZERO_SIG64)) throw new Error('Auth failure.'); if (!this.peerIdentity) @@ -149,7 +149,7 @@ BIP150.prototype.propose = function propose(hash) { match = this.findAuthorized(hash); if (!match) - return constants.ZERO_HASH; + return encoding.ZERO_HASH; this.peerIdentity = match; diff --git a/lib/net/bip151.js b/lib/net/bip151.js index 1e58e66d..9d21f68e 100644 --- a/lib/net/bip151.js +++ b/lib/net/bip151.js @@ -17,7 +17,6 @@ var util = require('../utils/util'); var co = require('../utils/co'); var crypto = require('../crypto/crypto'); var assert = require('assert'); -var constants = require('../protocol/constants'); var chachapoly = require('../crypto/chachapoly'); var packets = require('./packets'); var ec = require('../crypto/ec'); @@ -325,6 +324,14 @@ function BIP151(cipher) { util.inherits(BIP151, EventEmitter); +/** + * Max message size. + * @const {Number} + * @default + */ + +BIP151.MAX_MESSAGE = 12 * 1000 * 1000; + /** * Emit an error. * @param {...String} msg @@ -387,7 +394,7 @@ BIP151.prototype.toEncack = function toEncack() { BIP151.prototype.toRekey = function toRekey() { assert(this.handshake, 'Cannot rekey before handshake.'); - return new EncackPacket(constants.ZERO_KEY); + return new EncackPacket(encoding.ZERO_KEY); }; /** @@ -410,7 +417,7 @@ BIP151.prototype.encinit = function encinit(publicKey, cipher) { BIP151.prototype.encack = function encack(publicKey) { assert(this.initSent, 'Unsolicited ACK.'); - if (util.equal(publicKey, constants.ZERO_KEY)) { + if (util.equal(publicKey, encoding.ZERO_KEY)) { assert(this.handshake, 'No initialization before rekey.'); if (this.bip150 && this.bip150.auth) { @@ -678,7 +685,7 @@ BIP151.prototype.parse = function parse(data) { // potential dos'er or a cipher state mismatch. // Note that 6 is the minimum size: // varint-cmdlen(1) str-cmd(1) u32-size(4) payload(0) - if (size < 6 || size > constants.MAX_MESSAGE * 3) { + if (size < 6 || size > BIP151.MAX_MESSAGE) { this.waiting = 4; this.error('Bad packet size: %d.', util.mb(size)); return; diff --git a/lib/net/bip152.js b/lib/net/bip152.js index 388f837d..a08f04fa 100644 --- a/lib/net/bip152.js +++ b/lib/net/bip152.js @@ -11,9 +11,9 @@ var BufferReader = require('../utils/reader'); var BufferWriter = require('../utils/writer'); var StaticWriter = require('../utils/staticwriter'); var encoding = require('../utils/encoding'); +var consensus = require('../protocol/consensus'); var crypto = require('../crypto/crypto'); var assert = require('assert'); -var constants = require('../protocol/constants'); var siphash = require('../crypto/siphash'); var AbstractBlock = require('../primitives/abstractblock'); var TX = require('../primitives/tx'); @@ -298,7 +298,7 @@ CompactBlock.prototype.init = function init() { if (this.totalTX === 0) throw new Error('Empty vectors.'); - if (this.totalTX > constants.block.MAX_SIZE / 10) + if (this.totalTX > consensus.MAX_BLOCK_SIZE / 10) throw new Error('Compact block too big.'); // No sparse arrays here, v8. diff --git a/lib/net/common.js b/lib/net/common.js new file mode 100644 index 00000000..d7a031b6 --- /dev/null +++ b/lib/net/common.js @@ -0,0 +1,112 @@ +/*! + * common.js - p2p constants for bcoin + * Copyright (c) 2014-2015, Fedor Indutny (MIT License) + * Copyright (c) 2014-2016, Christopher Jeffrey (MIT License). + * https://github.com/bcoin-org/bcoin + */ + +'use strict'; + +var USER_VERSION = require('../../package.json').version; + +/** + * Default protocol version. + * @const {Number} + * @default + */ + +exports.PROTOCOL_VERSION = 70014; + +/** + * Minimum protocol version we're willing to talk to. + * @const {Number} + * @default + */ + +exports.MIN_VERSION = 70001; + +/** + * Service bits. + * @enum {Number} + * @default + */ + +exports.services = { + /** + * Whether network services are enabled. + */ + + NETWORK: 1 << 0, + + /** + * Whether the peer supports the getutxos packet. + */ + + GETUTXO: 1 << 1, + + /** + * Whether the peer supports BIP37. + */ + + BLOOM: 1 << 2, + + /** + * Whether the peer supports segregated witness. + */ + + WITNESS: 1 << 3 +}; + +/** + * BCoin's services (we support everything). + * @const {Number} + * @default + */ + +exports.LOCAL_SERVICES = 0 + | exports.services.NETWORK + | exports.services.GETUTXO + | exports.services.BLOOM + | exports.services.WITNESS; + +/** + * Required services (network and segwit). + * @const {Number} + * @default + */ + +exports.REQUIRED_SERVICES = 0 + | exports.services.NETWORK + | exports.services.WITNESS; + +/** + * Default user agent: `/bcoin:[version]/`. + * @const {String} + * @default + */ + +exports.USER_AGENT = '/bcoin:' + USER_VERSION + '/'; + +/** + * Max message size (~4mb with segwit, formerly 2mb) + * @const {Number} + * @default + */ + +exports.MAX_MESSAGE = 4 * 1000 * 1000; + +/** + * Amount of time to ban misbheaving peers. + * @const {Number} + * @default + */ + +exports.BAN_TIME = 24 * 60 * 60; + +/** + * Ban score threshold before ban is placed in effect. + * @const {Number} + * @default + */ + +exports.BAN_SCORE = 100; diff --git a/lib/net/index.js b/lib/net/index.js index 6822f967..e728abbf 100644 --- a/lib/net/index.js +++ b/lib/net/index.js @@ -3,11 +3,12 @@ exports.BIP150 = require('./bip150'); exports.BIP151 = require('./bip151'); exports.bip152 = require('./bip152'); -exports.packets = require('./packets'); +exports.common = require('./common'); +exports.dns = require('./dns'); exports.Framer = require('./framer'); exports.HostList = require('./hostlist'); +exports.packets = require('./packets'); exports.Parser = require('./parser'); exports.Peer = require('./peer'); exports.Pool = require('./pool'); exports.tcp = require('./tcp'); -exports.dns = require('./dns'); diff --git a/lib/net/packets.js b/lib/net/packets.js index f307a0c3..b71b2c12 100644 --- a/lib/net/packets.js +++ b/lib/net/packets.js @@ -7,7 +7,7 @@ 'use strict'; -var constants = require('../protocol/constants'); +var common = require('./common'); var util = require('../utils/util'); var assert = require('assert'); var crypto = require('../crypto/crypto'); @@ -172,13 +172,13 @@ function VersionPacket(options) { Packet.call(this); - this.version = constants.VERSION; - this.services = constants.LOCAL_SERVICES; + this.version = common.PROTOCOL_VERSION; + this.services = common.LOCAL_SERVICES; this.ts = util.now(); this.recv = new NetAddress(); this.from = new NetAddress(); - this.nonce = constants.ZERO_U64; - this.agent = constants.USER_AGENT; + this.nonce = encoding.ZERO_U64; + this.agent = common.USER_AGENT; this.height = 0; this.noRelay = false; @@ -289,7 +289,7 @@ VersionPacket.prototype.toRaw = function toRaw() { */ VersionPacket.prototype.hasNetwork = function hasNetwork() { - return (this.services & constants.services.NETWORK) !== 0; + return (this.services & common.services.NETWORK) !== 0; }; /** @@ -299,7 +299,7 @@ VersionPacket.prototype.hasNetwork = function hasNetwork() { VersionPacket.prototype.hasBloom = function hasBloom() { return this.version >= 70011 - && (this.services & constants.services.BLOOM) !== 0; + && (this.services & common.services.BLOOM) !== 0; }; /** @@ -308,7 +308,7 @@ VersionPacket.prototype.hasBloom = function hasBloom() { */ VersionPacket.prototype.hasUTXO = function hasUTXO() { - return (this.services & constants.services.GETUTXO) !== 0; + return (this.services & common.services.GETUTXO) !== 0; }; /** @@ -317,7 +317,7 @@ VersionPacket.prototype.hasUTXO = function hasUTXO() { */ VersionPacket.prototype.hasWitness = function hasWitness() { - return (this.services & constants.services.WITNESS) !== 0; + return (this.services & common.services.WITNESS) !== 0; }; /** @@ -577,7 +577,7 @@ function PongPacket(nonce) { Packet.call(this); - this.nonce = nonce || constants.ZERO_U64; + this.nonce = nonce || encoding.ZERO_U64; } util.inherits(PongPacket, Packet); @@ -695,7 +695,7 @@ function AlertPacket(options) { this.cancel = 0; this.cancels = []; this.minVer = 10000; - this.maxVer = constants.VERSION; + this.maxVer = common.PROTOCOL_VERSION; this.subVers = []; this.priority = 100; this.comment = ''; @@ -1350,7 +1350,7 @@ function GetBlocksPacket(locator, stop) { Packet.call(this); - this.version = constants.VERSION; + this.version = common.PROTOCOL_VERSION; this.locator = locator || []; this.stop = stop || null; } @@ -1388,7 +1388,7 @@ GetBlocksPacket.prototype.toWriter = function toWriter(bw) { for (i = 0; i < this.locator.length; i++) bw.writeHash(this.locator[i]); - bw.writeHash(this.stop || constants.ZERO_HASH); + bw.writeHash(this.stop || encoding.ZERO_HASH); return bw; }; @@ -1421,7 +1421,7 @@ GetBlocksPacket.prototype.fromReader = function fromReader(br) { this.stop = br.readHash('hex'); - if (this.stop === constants.NULL_HASH) + if (this.stop === encoding.NULL_HASH) this.stop = null; return this; @@ -1854,7 +1854,7 @@ TXPacket.fromRaw = function fromRaw(data, enc) { * @exports RejectPacket * @constructor * @property {(Number|String)?} code - Code - * (see {@link constants.reject}). + * (see {@link RejectPacket.codes}). * @property {String?} msg - Message. * @property {String?} reason - Reason. * @property {(Hash|Buffer)?} data - Transaction or block hash. @@ -1867,7 +1867,7 @@ function RejectPacket(options) { Packet.call(this); this.message = ''; - this.code = constants.reject.INVALID; + this.code = RejectPacket.codes.INVALID; this.reason = ''; this.hash = null; @@ -1877,6 +1877,36 @@ function RejectPacket(options) { util.inherits(RejectPacket, Packet); +/** + * Reject codes. Note that `internal` and higher + * are not meant for use on the p2p network. + * @enum {Number} + * @default + */ + +RejectPacket.codes = { + MALFORMED: 0x01, + INVALID: 0x10, + OBSOLETE: 0x11, + DUPLICATE: 0x12, + NONSTANDARD: 0x40, + DUST: 0x41, + INSUFFICIENTFEE: 0x42, + CHECKPOINT: 0x43, + // Internal codes (NOT FOR USE ON NETWORK) + INTERNAL: 0x100, + HIGHFEE: 0x100, + ALREADYKNOWN: 0x101, + CONFLICT: 0x102 +}; + +/** + * Reject codes by value. + * @const {RevMap} + */ + +RejectPacket.codesByVal = util.revMap(RejectPacket.codes); + RejectPacket.prototype.cmd = 'reject'; RejectPacket.prototype.type = exports.types.REJECT; @@ -1894,10 +1924,10 @@ RejectPacket.prototype.fromOptions = function fromOptions(options) { if (code != null) { if (typeof code === 'string') - code = constants.reject[code.toUpperCase()]; + code = RejectPacket.codes[code.toUpperCase()]; - if (code >= constants.reject.INTERNAL) - code = constants.reject.INVALID; + if (code >= RejectPacket.codes.INTERNAL) + code = RejectPacket.codes.INVALID; this.code = code; } @@ -1936,7 +1966,7 @@ RejectPacket.prototype.rhash = function rhash() { */ RejectPacket.prototype.getCode = function getCode() { - var code = constants.rejectByVal[this.code]; + var code = RejectPacket.codesByVal[this.code]; if (!code) return this.code + ''; @@ -2058,13 +2088,13 @@ RejectPacket.fromRaw = function fromRaw(data, enc) { RejectPacket.prototype.fromReason = function fromReason(code, reason, obj) { if (typeof code === 'string') - code = constants.reject[code.toUpperCase()]; + code = RejectPacket.codes[code.toUpperCase()]; if (!code) - code = constants.reject.INVALID; + code = RejectPacket.codes.INVALID; - if (code >= constants.reject.INTERNAL) - code = constants.reject.INVALID; + if (code >= RejectPacket.codes.INTERNAL) + code = RejectPacket.codes.INVALID; this.message = ''; this.code = code; @@ -2109,7 +2139,7 @@ RejectPacket.fromError = function fromError(err, obj) { RejectPacket.prototype.inspect = function inspect() { return ''; @@ -2595,7 +2625,7 @@ function UTXOsPacket(options) { Packet.call(this); this.height = -1; - this.tip = constants.NULL_HASH; + this.tip = encoding.NULL_HASH; this.hits = []; this.coins = []; @@ -3351,7 +3381,7 @@ function EncinitPacket(publicKey, cipher) { Packet.call(this); - this.publicKey = publicKey || constants.ZERO_KEY; + this.publicKey = publicKey || encoding.ZERO_KEY; this.cipher = cipher || 0; } @@ -3448,7 +3478,7 @@ function EncackPacket(publicKey) { Packet.call(this); - this.publicKey = publicKey || constants.ZERO_KEY; + this.publicKey = publicKey || encoding.ZERO_KEY; } util.inherits(EncackPacket, Packet); @@ -3542,7 +3572,7 @@ function AuthChallengePacket(hash) { Packet.call(this); - this.hash = hash || constants.ZERO_HASH; + this.hash = hash || encoding.ZERO_HASH; } util.inherits(AuthChallengePacket, Packet); @@ -3636,7 +3666,7 @@ function AuthReplyPacket(signature) { Packet.call(this); - this.signature = signature || constants.ZERO_SIG64; + this.signature = signature || encoding.ZERO_SIG64; } util.inherits(AuthReplyPacket, Packet); @@ -3730,7 +3760,7 @@ function AuthProposePacket(hash) { Packet.call(this); - this.hash = hash || constants.ZERO_HASH; + this.hash = hash || encoding.ZERO_HASH; } util.inherits(AuthProposePacket, Packet); diff --git a/lib/net/parser.js b/lib/net/parser.js index 5c8473a0..29523f1d 100644 --- a/lib/net/parser.js +++ b/lib/net/parser.js @@ -12,7 +12,7 @@ var EventEmitter = require('events').EventEmitter; var util = require('../utils/util'); var crypto = require('../crypto/crypto'); var assert = require('assert'); -var constants = require('../protocol/constants'); +var common = require('./common'); var packets = require('./packets'); /** @@ -123,7 +123,7 @@ Parser.prototype.feed = function feed(data) { Parser.prototype.parse = function parse(data) { var payload, checksum; - assert(data.length <= constants.MAX_MESSAGE); + assert(data.length <= common.MAX_MESSAGE); if (!this.header) { this.header = this.parseHeader(data); @@ -177,7 +177,7 @@ Parser.prototype.parseHeader = function parseHeader(data) { size = data.readUInt32LE(16, true); - if (size > constants.MAX_MESSAGE) { + if (size > common.MAX_MESSAGE) { this.waiting = 24; return this.error('Packet length too large: %dmb.', util.mb(size)); } diff --git a/lib/net/peer.js b/lib/net/peer.js index 1500a411..f20773c1 100644 --- a/lib/net/peer.js +++ b/lib/net/peer.js @@ -14,7 +14,8 @@ var co = require('../utils/co'); var Parser = require('./parser'); var Framer = require('./framer'); var packets = require('./packets'); -var constants = require('../protocol/constants'); +var consensus = require('../protocol/consensus'); +var common = require('./common'); var InvItem = require('../primitives/invitem'); var Locker = require('../utils/locker'); var Bloom = require('../utils/bloom'); @@ -23,6 +24,7 @@ var BIP150 = require('./bip150'); var BIP152 = require('./bip152'); var Block = require('../primitives/block'); var TX = require('../primitives/tx'); +var encoding = require('../utils/encoding'); var errors = require('../btc/errors'); var NetAddress = require('../primitives/netaddress'); var invTypes = InvItem.types; @@ -823,12 +825,12 @@ Peer.prototype.sendHeaders = function sendHeaders(items) { Peer.prototype.sendVersion = function sendVersion() { var packet = new packets.VersionPacket(); - packet.version = constants.VERSION; + packet.version = this.pool.protoVersion; packet.services = this.pool.address.services; packet.ts = this.network.now(); packet.from = this.pool.address; packet.nonce = this.pool.localNonce; - packet.agent = constants.USER_AGENT; + packet.agent = this.pool.userAgent; packet.height = this.chain.height; packet.noRelay = this.options.noRelay; this.send(packet); @@ -1528,7 +1530,7 @@ Peer.prototype.handleFilterLoad = co(function* handleFilterLoad(packet) { Peer.prototype.handleFilterAdd = co(function* handleFilterAdd(packet) { var data = packet.data; - if (data.length > constants.script.MAX_PUSH) { + if (data.length > consensus.MAX_SCRIPT_PUSH) { this.increaseBan(100); return; } @@ -1588,7 +1590,7 @@ Peer.prototype.handleMerkleBlock = co(function* handleMerkleBlock(packet) { Peer.prototype.handleFeeFilter = co(function* handleFeeFilter(packet) { var rate = packet.rate; - if (!(rate >= 0 && rate <= constants.MAX_MONEY)) { + if (!(rate >= 0 && rate <= consensus.MAX_MONEY)) { this.increaseBan(100); return; } @@ -1790,7 +1792,7 @@ Peer.prototype.handleVersion = co(function* handleVersion(packet) { throw new Error('We connected to ourself. Oops.'); } - if (packet.version < constants.MIN_VERSION) + if (packet.version < common.MIN_VERSION) throw new Error('Peer does not support required protocol version.'); if (this.options.witness) { @@ -2211,7 +2213,7 @@ Peer.prototype.handlePong = co(function* handlePong(packet) { } if (!util.equal(nonce, this.challenge)) { - if (util.equal(nonce, constants.ZERO_U64)) { + if (util.equal(nonce, encoding.ZERO_U64)) { this.logger.debug('Peer sent a zero nonce (%s).', this.hostname); this.challenge = null; return; diff --git a/lib/net/pool.js b/lib/net/pool.js index 5aa96166..959bc385 100644 --- a/lib/net/pool.js +++ b/lib/net/pool.js @@ -13,7 +13,7 @@ var AsyncObject = require('../utils/async'); var util = require('../utils/util'); var IP = require('../utils/ip'); var co = require('../utils/co'); -var constants = require('../protocol/constants'); +var common = require('./common'); var errors = require('../btc/errors'); var NetAddress = require('../primitives/netaddress'); var Address = require('../primitives/address'); @@ -28,6 +28,7 @@ var List = require('../utils/list'); var tcp = require('./tcp'); var dns = require('./dns'); var HostList = require('./hostlist'); +var InvItem = require('../primitives/invitem'); var invTypes = InvItem.types; var VerifyError = errors.VerifyError; var VerifyResult = errors.VerifyResult; @@ -104,6 +105,7 @@ function Pool(options) { this.maxOutbound = 8; this.maxInbound = 8; this.connected = false; + this.syncing = false; this.createSocket = tcp.createSocket; this.createServer = tcp.createServer; this.resolve = dns.resolve; @@ -111,19 +113,16 @@ function Pool(options) { this.authdb = null; this.identityKey = null; this.proxyServer = null; - this.banTime = constants.BAN_TIME; - this.banScore = constants.BAN_SCORE; + this.banTime = common.BAN_TIME; + this.banScore = common.BAN_SCORE; this.feeRate = -1; // Required services. - this.reqServices = constants.services.NETWORK; - this.reqServices |= constants.services.WITNESS; - - this.syncing = false; + this.reqServices = common.REQUIRED_SERVICES; this.address = new NetAddress(); this.address.ts = this.network.now(); - this.address.services = constants.LOCAL_SERVICES; + this.address.services = common.LOCAL_SERVICES; this.address.setPort(this.network.port); this.hosts = new HostList(this); @@ -131,6 +130,9 @@ function Pool(options) { this.localNonce = util.nonce(); + this.protoVersion = common.PROTOCOL_VERSION; + this.userAgent = common.USER_AGENT; + this.spvFilter = null; this.txFilter = null; @@ -165,8 +167,8 @@ Pool.prototype._initOptions = function _initOptions() { this.options.headers = this.options.spv; if (!this.options.witness) { - this.address.services &= ~constants.services.WITNESS; - this.reqServices &= ~constants.services.WITNESS; + this.address.services &= ~common.services.WITNESS; + this.reqServices &= ~common.services.WITNESS; } if (this.options.host != null) { @@ -211,12 +213,23 @@ Pool.prototype._initOptions = function _initOptions() { if (this.options.selfish) { assert(typeof this.options.selfish === 'boolean'); - this.address.services &= ~constants.services.NETWORK; + this.address.services &= ~common.services.NETWORK; } if (this.options.spv) { assert(typeof this.options.spv === 'boolean'); - this.address.services &= ~constants.services.NETWORK; + this.address.services &= ~common.services.NETWORK; + } + + if (this.options.protoVersion) { + assert(typeof this.options.protoVersion === 'number'); + this.protoVersion = this.options.protoVersion; + } + + if (this.options.userAgent) { + assert(typeof this.options.userAgent === 'string'); + assert(this.options.userAgent.length < 256); + this.userAgent = this.options.userAgent; } if (this.options.bip150) { @@ -260,7 +273,7 @@ Pool.prototype._initOptions = function _initOptions() { if (this.options.spv) { this.spvFilter = Bloom.fromRate(10000, 0.001, Bloom.flags.ALL); - this.reqServices |= constants.services.BLOOM; + this.reqServices |= common.services.BLOOM; } if (!this.options.mempool) diff --git a/lib/node/fullnode.js b/lib/node/fullnode.js index 19a9ff83..8916cc21 100644 --- a/lib/node/fullnode.js +++ b/lib/node/fullnode.js @@ -7,7 +7,6 @@ 'use strict'; -var constants = require('../protocol/constants'); var util = require('../utils/util'); var co = require('../utils/co'); var Node = require('./node'); @@ -18,6 +17,7 @@ var Pool = require('../net/pool'); var Miner = require('../mining/miner'); var WalletDB = require('../wallet/walletdb'); var HTTPServer = require('../http/server'); +var policy = require('../protocol/policy'); /** * Create a fullnode complete with a chain, @@ -80,7 +80,7 @@ function FullNode(options) { // Fee estimation. this.fees = new Fees( - constants.tx.MIN_RELAY, + policy.MIN_RELAY, this.network, this.logger); diff --git a/lib/primitives/abstractblock.js b/lib/primitives/abstractblock.js index bc5e03bc..c83f5157 100644 --- a/lib/primitives/abstractblock.js +++ b/lib/primitives/abstractblock.js @@ -8,13 +8,13 @@ 'use strict'; var assert = require('assert'); -var constants = require('../protocol/constants'); var util = require('../utils/util'); var crypto = require('../crypto/crypto'); -var btcutils = require('../btc/utils'); var VerifyResult = require('../btc/errors').VerifyResult; var StaticWriter = require('../utils/staticwriter'); var InvItem = require('./invitem'); +var encoding = require('../utils/encoding'); +var consensus = require('../protocol/consensus'); /** * The class which all block-like objects inherit from. @@ -40,8 +40,8 @@ function AbstractBlock() { return new AbstractBlock(); this.version = 1; - this.prevBlock = constants.NULL_HASH; - this.merkleRoot = constants.NULL_HASH; + this.prevBlock = encoding.NULL_HASH; + this.merkleRoot = encoding.NULL_HASH; this.ts = 0; this.bits = 0; this.nonce = 0; @@ -312,7 +312,7 @@ AbstractBlock.prototype._verifyHeaders = function verifyHeaders(ret) { */ AbstractBlock.prototype.verifyPOW = function verifyPOW() { - return btcutils.verifyPOW(this.hash(), this.bits); + return consensus.verifyPOW(this.hash(), this.bits); }; /** diff --git a/lib/primitives/address.js b/lib/primitives/address.js index b79d649a..ed2ee7d2 100644 --- a/lib/primitives/address.js +++ b/lib/primitives/address.js @@ -7,16 +7,16 @@ 'use strict'; +var assert = require('assert'); var Network = require('../protocol/network'); var networks = require('../protocol/networks'); -var constants = require('../protocol/constants'); +var encoding = require('../script/encoding'); +var enc = require('../utils/encoding'); var util = require('../utils/util'); var crypto = require('../crypto/crypto'); -var assert = require('assert'); var BufferReader = require('../utils/reader'); var StaticWriter = require('../utils/staticwriter'); var base58 = require('../utils/base58'); -var scriptTypes = constants.scriptTypes; /** * Represents an address. @@ -38,8 +38,8 @@ function Address(options) { if (!(this instanceof Address)) return new Address(options); - this.hash = constants.ZERO_HASH160; - this.type = scriptTypes.PUBKEYHASH; + this.hash = enc.ZERO_HASH160; + this.type = Address.types.PUBKEYHASH; this.version = -1; this.network = Network.primary; @@ -52,7 +52,14 @@ function Address(options) { * @enum {Number} */ -Address.types = scriptTypes; +Address.types = encoding.types; + +/** + * Address types by value. + * @const {RevMap} + */ + +Address.typesByVal = encoding.typesByVal; /** * Inject properties from options object. @@ -127,7 +134,7 @@ Address.prototype.verifyNetwork = function verifyNetwork(network) { */ Address.prototype.getType = function getType() { - return constants.scriptTypesByVal[this.type].toLowerCase(); + return encoding.typesByVal[this.type].toLowerCase(); }; /** @@ -283,42 +290,42 @@ Address.fromBase58 = function fromBase58(address) { Address.prototype.fromScript = function fromScript(script) { if (script.isPubkey()) { this.hash = crypto.hash160(script.get(0)); - this.type = scriptTypes.PUBKEYHASH; + this.type = Address.types.PUBKEYHASH; this.version = -1; return this; } if (script.isPubkeyhash()) { this.hash = script.get(2); - this.type = scriptTypes.PUBKEYHASH; + this.type = Address.types.PUBKEYHASH; this.version = -1; return this; } if (script.isScripthash()) { this.hash = script.get(1); - this.type = scriptTypes.SCRIPTHASH; + this.type = Address.types.SCRIPTHASH; this.version = -1; return this; } if (script.isWitnessPubkeyhash()) { this.hash = script.get(1); - this.type = scriptTypes.WITNESSPUBKEYHASH; + this.type = Address.types.WITNESSPUBKEYHASH; this.version = 0; return this; } if (script.isWitnessScripthash()) { this.hash = script.get(1); - this.type = scriptTypes.WITNESSSCRIPTHASH; + this.type = Address.types.WITNESSSCRIPTHASH; this.version = 0; return this; } if (script.isWitnessMasthash()) { this.hash = script.get(1); - this.type = scriptTypes.WITNESSSCRIPTHASH; + this.type = Address.types.WITNESSSCRIPTHASH; this.version = 1; return this; } @@ -326,7 +333,7 @@ Address.prototype.fromScript = function fromScript(script) { // Put this last: it's the slowest to check. if (script.isMultisig()) { this.hash = script.hash160(); - this.type = scriptTypes.SCRIPTHASH; + this.type = Address.types.SCRIPTHASH; this.version = -1; return this; } @@ -343,14 +350,14 @@ Address.prototype.fromWitness = function fromWitness(witness) { // since we can't get the version. if (witness.isPubkeyhashInput()) { this.hash = crypto.hash160(witness.get(1)); - this.type = scriptTypes.WITNESSPUBKEYHASH; + this.type = Address.types.WITNESSPUBKEYHASH; this.version = 0; return this; } if (witness.isScripthashInput()) { this.hash = crypto.sha256(witness.get(witness.length - 1)); - this.type = scriptTypes.WITNESSSCRIPTHASH; + this.type = Address.types.WITNESSSCRIPTHASH; this.version = 0; return this; } @@ -365,14 +372,14 @@ Address.prototype.fromWitness = function fromWitness(witness) { Address.prototype.fromInputScript = function fromInputScript(script) { if (script.isPubkeyhashInput()) { this.hash = crypto.hash160(script.get(1)); - this.type = scriptTypes.PUBKEYHASH; + this.type = Address.types.PUBKEYHASH; this.version = -1; return this; } if (script.isScripthashInput()) { this.hash = crypto.hash160(script.get(script.length - 1)); - this.type = scriptTypes.SCRIPTHASH; + this.type = Address.types.SCRIPTHASH; this.version = -1; return this; } @@ -430,10 +437,10 @@ Address.prototype.fromHash = function fromHash(hash, type, version, network) { hash = new Buffer(hash, 'hex'); if (typeof type === 'string') - type = scriptTypes[type.toUpperCase()]; + type = Address.types[type.toUpperCase()]; if (type == null) - type = scriptTypes.PUBKEYHASH; + type = Address.types.PUBKEYHASH; if (version == null) version = -1; @@ -452,11 +459,11 @@ Address.prototype.fromHash = function fromHash(hash, type, version, network) { } else { assert(Address.isWitness(type), 'Wrong version (non-witness).'); assert(version >= 0 && version <= 16, 'Bad program version.'); - if (version === 0 && type === scriptTypes.WITNESSPUBKEYHASH) + if (version === 0 && type === Address.types.WITNESSPUBKEYHASH) assert(hash.length === 20, 'Hash is the wrong size.'); - else if (version === 0 && type === scriptTypes.WITNESSSCRIPTHASH) + else if (version === 0 && type === Address.types.WITNESSSCRIPTHASH) assert(hash.length === 32, 'Hash is the wrong size.'); - else if (version === 1 && type === scriptTypes.WITNESSSCRIPTHASH) + else if (version === 1 && type === Address.types.WITNESSSCRIPTHASH) assert(hash.length === 32, 'Hash is the wrong size.'); } @@ -493,9 +500,9 @@ Address.fromHash = function fromHash(hash, type, version, network) { Address.prototype.fromData = function fromData(data, type, version, network) { if (typeof type === 'string') - type = scriptTypes[type.toUpperCase()]; + type = Address.types[type.toUpperCase()]; - if (type === scriptTypes.WITNESSSCRIPTHASH) { + if (type === Address.types.WITNESSSCRIPTHASH) { if (version === 0) { assert(Buffer.isBuffer(data)); data = crypto.sha256(data); @@ -505,7 +512,7 @@ Address.prototype.fromData = function fromData(data, type, version, network) { } else { throw new Error('Cannot create from version=' + version); } - } else if (type === scriptTypes.WITNESSPUBKEYHASH) { + } else if (type === Address.types.WITNESSPUBKEYHASH) { if (version !== 0) throw new Error('Cannot create from version=' + version); assert(Buffer.isBuffer(data)); @@ -554,7 +561,7 @@ Address.validate = function validate(address, type) { } if (typeof type === 'string') - type = scriptTypes[type.toUpperCase()]; + type = Address.types[type.toUpperCase()]; if (type && address.type !== type) return false; @@ -604,13 +611,13 @@ Address.getHash = function getHash(data, enc) { Address.getPrefix = function getPrefix(type, network) { var prefixes = network.addressPrefix; switch (type) { - case scriptTypes.PUBKEYHASH: + case Address.types.PUBKEYHASH: return prefixes.pubkeyhash; - case scriptTypes.SCRIPTHASH: + case Address.types.SCRIPTHASH: return prefixes.scripthash; - case scriptTypes.WITNESSPUBKEYHASH: + case Address.types.WITNESSPUBKEYHASH: return prefixes.witnesspubkeyhash; - case scriptTypes.WITNESSSCRIPTHASH: + case Address.types.WITNESSSCRIPTHASH: return prefixes.witnessscripthash; default: return -1; @@ -628,13 +635,13 @@ Address.getType = function getType(prefix, network) { var prefixes = network.addressPrefix; switch (prefix) { case prefixes.pubkeyhash: - return scriptTypes.PUBKEYHASH; + return Address.types.PUBKEYHASH; case prefixes.scripthash: - return scriptTypes.SCRIPTHASH; + return Address.types.SCRIPTHASH; case prefixes.witnesspubkeyhash: - return scriptTypes.WITNESSPUBKEYHASH; + return Address.types.WITNESSPUBKEYHASH; case prefixes.witnessscripthash: - return scriptTypes.WITNESSSCRIPTHASH; + return Address.types.WITNESSSCRIPTHASH; default: return -1; } @@ -648,9 +655,9 @@ Address.getType = function getType(prefix, network) { Address.isWitness = function isWitness(type) { switch (type) { - case scriptTypes.WITNESSPUBKEYHASH: + case Address.types.WITNESSPUBKEYHASH: return true; - case scriptTypes.WITNESSSCRIPTHASH: + case Address.types.WITNESSSCRIPTHASH: return true; default: return false; diff --git a/lib/primitives/block.js b/lib/primitives/block.js index 623876ec..f1b0c6cc 100644 --- a/lib/primitives/block.js +++ b/lib/primitives/block.js @@ -11,8 +11,7 @@ var assert = require('assert'); var util = require('../utils/util'); var encoding = require('../utils/encoding'); var crypto = require('../crypto/crypto'); -var btcutils = require('../btc/utils'); -var constants = require('../protocol/constants'); +var consensus = require('../protocol/consensus'); var AbstractBlock = require('./abstractblock'); var VerifyResult = require('../btc/errors').VerifyResult; var BufferReader = require('../utils/reader'); @@ -188,7 +187,7 @@ Block.prototype.getSizes = function getSizes() { */ Block.prototype.getVirtualSize = function getVirtualSize() { - var scale = constants.WITNESS_SCALE_FACTOR; + var scale = consensus.WITNESS_SCALE_FACTOR; return (this.getWeight() + scale - 1) / scale | 0; }; @@ -200,7 +199,7 @@ Block.prototype.getVirtualSize = function getVirtualSize() { Block.prototype.getWeight = function getWeight() { var sizes = this.getSizes(); var base = sizes.total - sizes.witness; - return base * (constants.WITNESS_SCALE_FACTOR - 1) + sizes.total; + return base * (consensus.WITNESS_SCALE_FACTOR - 1) + sizes.total; }; /** @@ -316,7 +315,7 @@ Block.prototype.createMerkleRoot = function createMerkleRoot(enc) { */ Block.prototype.createWitnessNonce = function createWitnessNonce() { - return util.copy(constants.ZERO_HASH); + return util.copy(encoding.ZERO_HASH); }; /** @@ -333,7 +332,7 @@ Block.prototype.createCommitmentHash = function createCommitmentHash(enc) { assert(nonce, 'No witness nonce present.'); - leaves.push(constants.ZERO_HASH); + leaves.push(encoding.ZERO_HASH); for (i = 1; i < this.txs.length; i++) { tx = this.txs[i]; @@ -443,7 +442,7 @@ Block.prototype.getCommitmentHash = function getCommitmentHash(enc) { Block.prototype._verify = function _verify(ret) { var sigops = 0; - var scale = constants.WITNESS_SCALE_FACTOR; + var scale = consensus.WITNESS_SCALE_FACTOR; var i, tx, merkle; if (!ret) @@ -471,8 +470,8 @@ Block.prototype._verify = function _verify(ret) { // Check base size. if (this.txs.length === 0 - || this.txs.length > constants.block.MAX_SIZE - || this.getBaseSize() > constants.block.MAX_SIZE) { + || this.txs.length > consensus.MAX_BLOCK_SIZE + || this.getBaseSize() > consensus.MAX_BLOCK_SIZE) { ret.reason = 'bad-blk-length'; ret.score = 100; return false; @@ -502,7 +501,7 @@ Block.prototype._verify = function _verify(ret) { // Count legacy sigops (do not count scripthash or witness). sigops += tx.getLegacySigops(); - if (sigops * scale > constants.block.MAX_SIGOPS_COST) { + if (sigops * scale > consensus.MAX_BLOCK_SIGOPS_COST) { ret.reason = 'bad-blk-sigops'; ret.score = 100; return false; @@ -550,14 +549,14 @@ Block.prototype.getReward = function getReward(view, height, network) { assert(typeof height === 'number'); network = Network.get(network); - reward = btcutils.getReward(height, network.halvingInterval); + reward = consensus.getReward(height, network.halvingInterval); for (i = 1; i < this.txs.length; i++) { tx = this.txs[i]; fee = tx.getFee(view); - if (fee < 0 || fee > constants.MAX_MONEY) + if (fee < 0 || fee > consensus.MAX_MONEY) return -1; reward += fee; @@ -569,7 +568,7 @@ Block.prototype.getReward = function getReward(view, height, network) { // MAX_MONEY is 51 bits. The result of // (51 bits + 51 bits) is _never_ greater // than 52 bits. - if (reward < 0 || reward > constants.MAX_MONEY) + if (reward < 0 || reward > consensus.MAX_MONEY) return -1; } diff --git a/lib/primitives/coin.js b/lib/primitives/coin.js index 300630d6..d0cfec34 100644 --- a/lib/primitives/coin.js +++ b/lib/primitives/coin.js @@ -9,7 +9,6 @@ var assert = require('assert'); var util = require('../utils/util'); -var constants = require('../protocol/constants'); var Network = require('../protocol/network'); var Amount = require('../btc/amount'); var Output = require('./output'); @@ -17,6 +16,7 @@ var Script = require('../script/script'); var Network = require('../protocol/network'); var BufferReader = require('../utils/reader'); var StaticWriter = require('../utils/staticwriter'); +var encoding = require('../utils/encoding'); /** * Represents an unspent output. @@ -43,7 +43,7 @@ function Coin(options) { this.value = 0; this.script = new Script(); this.coinbase = true; - this.hash = constants.NULL_HASH; + this.hash = encoding.NULL_HASH; this.index = 0; if (options) diff --git a/lib/primitives/input.js b/lib/primitives/input.js index afa18e4e..79df7172 100644 --- a/lib/primitives/input.js +++ b/lib/primitives/input.js @@ -7,9 +7,8 @@ 'use strict'; -var util = require('../utils/util'); var assert = require('assert'); -var constants = require('../protocol/constants'); +var util = require('../utils/util'); var Network = require('../protocol/network'); var Script = require('../script/script'); var Witness = require('../script/witness'); @@ -99,7 +98,7 @@ Input.prototype.getType = function getType(coin) { else type = this.script.getInputType(); - return constants.scriptTypesByVal[type].toLowerCase(); + return Script.typesByVal[type].toLowerCase(); }; /** @@ -159,7 +158,7 @@ Input.prototype.getSubtype = function getSubtype(coin) { type = redeem.getType(); - return constants.scriptTypesByVal[type].toLowerCase(); + return Script.typesByVal[type].toLowerCase(); }; /** diff --git a/lib/primitives/invitem.js b/lib/primitives/invitem.js index ef986741..ea31f083 100644 --- a/lib/primitives/invitem.js +++ b/lib/primitives/invitem.js @@ -7,7 +7,6 @@ 'use strict'; -var constants = require('../protocol/constants'); var BufferReader = require('../utils/reader'); var StaticWriter = require('../utils/staticwriter'); var util = require('../utils/util'); diff --git a/lib/primitives/keyring.js b/lib/primitives/keyring.js index ffda6de7..cf7cf642 100644 --- a/lib/primitives/keyring.js +++ b/lib/primitives/keyring.js @@ -7,11 +7,10 @@ 'use strict'; -var constants = require('../protocol/constants'); +var assert = require('assert'); var util = require('../utils/util'); var encoding = require('../utils/encoding'); var crypto = require('../crypto/crypto'); -var assert = require('assert'); var networks = require('../protocol/networks'); var Network = require('../protocol/network'); var BufferReader = require('../utils/reader'); @@ -37,7 +36,7 @@ function KeyRing(options, network) { this.network = Network.primary; this.witness = false; this.nested = false; - this.publicKey = constants.ZERO_KEY; + this.publicKey = encoding.ZERO_KEY; this.privateKey = null; this.script = null; @@ -796,7 +795,7 @@ KeyRing.prototype.toJSON = function toJSON() { publicKey: this.publicKey.toString('hex'), script: this.script ? this.script.toRaw().toString('hex') : null, program: this.witness ? this.getProgram().toRaw().toString('hex') : null, - type: constants.scriptTypesByVal[this.getType()].toLowerCase(), + type: Script.typesByVal[this.getType()].toLowerCase(), address: this.getAddress('base58') }; }; diff --git a/lib/primitives/merkleblock.js b/lib/primitives/merkleblock.js index cbee19db..d1b3b8ee 100644 --- a/lib/primitives/merkleblock.js +++ b/lib/primitives/merkleblock.js @@ -7,18 +7,18 @@ 'use strict'; +var assert = require('assert'); var util = require('../utils/util'); var crypto = require('../crypto/crypto'); -var assert = require('assert'); -var constants = require('../protocol/constants'); -var DUMMY = new Buffer([0]); var AbstractBlock = require('./abstractblock'); var VerifyResult = require('../btc/errors').VerifyResult; var BufferReader = require('../utils/reader'); var StaticWriter = require('../utils/staticwriter'); var encoding = require('../utils/encoding'); +var consensus = require('../protocol/consensus'); var Headers = require('./headers'); var TX = require('./tx'); +var DUMMY = new Buffer([0]); /** * Represents a merkle (filtered) block. @@ -206,7 +206,7 @@ MerkleBlock.prototype.extractTree = function extractTree() { if (bitsUsed >= flags.length * 8) { failed = true; - return constants.ZERO_HASH; + return encoding.ZERO_HASH; } parent = (flags[bitsUsed / 8 | 0] >>> (bitsUsed % 8)) & 1; @@ -215,7 +215,7 @@ MerkleBlock.prototype.extractTree = function extractTree() { if (height === 0 || !parent) { if (hashUsed >= hashes.length) { failed = true; - return constants.ZERO_HASH; + return encoding.ZERO_HASH; } hash = hashes[hashUsed++]; if (height === 0 && parent) { @@ -248,7 +248,7 @@ MerkleBlock.prototype.extractTree = function extractTree() { if (totalTX === 0) return; - if (totalTX > constants.block.MAX_SIZE / 60) + if (totalTX > consensus.MAX_BLOCK_SIZE / 60) return; if (hashes.length > totalTX) diff --git a/lib/primitives/mtx.js b/lib/primitives/mtx.js index 92e61168..470086a6 100644 --- a/lib/primitives/mtx.js +++ b/lib/primitives/mtx.js @@ -10,11 +10,7 @@ var assert = require('assert'); var util = require('../utils/util'); var co = require('../utils/co'); -var btcutils = require('../btc/utils'); -var constants = require('../protocol/constants'); var Script = require('../script/script'); -var opcodes = Script.opcodes; -var FundingError = require('../btc/errors').FundingError; var TX = require('./tx'); var Input = require('./input'); var Output = require('./output'); @@ -25,6 +21,10 @@ var KeyRing = require('./keyring'); var Address = require('./address'); var workerPool = require('../workers/workerpool').pool; var encoding = require('../utils/encoding'); +var consensus = require('../protocol/consensus'); +var policy = require('../protocol/policy'); +var Amount = require('../btc/amount'); +var opcodes = Script.opcodes; /** * A mutable transaction object. @@ -63,6 +63,24 @@ function MTX(options) { util.inherits(MTX, TX); +/** + * Minimum fee to start with + * during coin selection. + * @const {Amount} + * @default + */ + +MTX.MIN_FEE = 10000; + +/** + * Maximum fee to allow + * after coin selection. + * @const {Amount} + * @default + */ + +MTX.MAX_FEE = consensus.COIN / 10; + /** * Inject properties from options object. * @private @@ -739,10 +757,10 @@ MTX.prototype.signature = function signature(index, prev, value, key, type, vers var hash; if (type == null) - type = constants.hashType.ALL; + type = Script.hashType.ALL; if (typeof type === 'string') - type = constants.hashType[type.toUpperCase()]; + type = Script.hashType[type.toUpperCase()]; // Get the hash of the current tx, minus the other // inputs, plus the sighash type. @@ -972,7 +990,7 @@ MTX.prototype.signAsync = function signAsync(ring, type) { */ MTX.prototype.estimateSize = co(function* estimateSize(estimate) { - var scale = constants.WITNESS_SCALE_FACTOR; + var scale = consensus.WITNESS_SCALE_FACTOR; var total = 0; var i, input, output, size, prev, coin; @@ -1209,7 +1227,7 @@ MTX.prototype.fund = co(function* fund(coins, options) { change = this.outputs[this.outputs.length - 1]; - if (change.isDust(constants.tx.MIN_RELAY)) { + if (change.isDust(policy.MIN_RELAY)) { // Do nothing. Change is added to fee. this.outputs.pop(); this.changeIndex = -1; @@ -1291,10 +1309,10 @@ MTX.prototype.setSequence = function setSequence(index, locktime, seconds) { this.version = 2; if (seconds) { - locktime >>>= constants.sequence.GRANULARITY; - locktime = constants.sequence.TYPE_FLAG | locktime; + locktime >>>= consensus.SEQUENCE_GRANULARITY; + locktime = consensus.SEQUENCE_TYPE_FLAG | locktime; } else { - locktime = constants.sequence.MASK & locktime; + locktime = consensus.SEQUENCE_MASK & locktime; } input.sequence = locktime; @@ -1426,7 +1444,7 @@ function CoinSelector(tx, options) { this.height = -1; this.confirmations = -1; this.hardFee = -1; - this.rate = constants.tx.MIN_FEE; + this.rate = MTX.MIN_FEE; this.maxFee = -1; this.round = false; this.changeAddress = null; @@ -1566,7 +1584,6 @@ CoinSelector.prototype.isFull = function isFull() { */ CoinSelector.prototype.isSpendable = function isSpendable(coin) { - var maturity = constants.tx.COINBASE_MATURITY; var conf; if (this.height === -1) @@ -1576,7 +1593,7 @@ CoinSelector.prototype.isSpendable = function isSpendable(coin) { if (coin.height === -1) return false; - if (this.height + 1 < coin.height + maturity) + if (this.height + 1 < coin.height + consensus.COINBASE_MATURITY) return false; } @@ -1608,12 +1625,12 @@ CoinSelector.prototype.getFee = function getFee(size) { var fee; if (this.round) - fee = btcutils.getRoundFee(size, this.rate); + fee = policy.getRoundFee(size, this.rate); else - fee = btcutils.getMinFee(size, this.rate); + fee = policy.getMinFee(size, this.rate); - if (fee > constants.tx.MAX_FEE) - fee = constants.tx.MAX_FEE; + if (fee > MTX.MAX_FEE) + fee = MTX.MAX_FEE; return fee; }; @@ -1659,7 +1676,7 @@ CoinSelector.prototype.select = co(function* select(coins) { if (this.hardFee !== -1) this.selectHard(this.hardFee); else - yield this.selectEstimate(constants.tx.MIN_FEE); + yield this.selectEstimate(MTX.MIN_FEE); if (!this.isFull()) { // Still failing to get enough funds. @@ -1696,7 +1713,7 @@ CoinSelector.prototype.selectEstimate = co(function* selectEstimate(fee) { // use a fake p2pkh output to gauge size. script: this.changeAddress ? Script.fromAddress(this.changeAddress) - : Script.fromPubkeyhash(constants.ZERO_HASH160), + : Script.fromPubkeyhash(encoding.ZERO_HASH160), value: 0 }); @@ -1725,13 +1742,47 @@ CoinSelector.prototype.selectHard = function selectHard(fee) { // Initial fee. this.fee = fee; - if (this.fee > constants.tx.MAX_FEE) - this.fee = constants.tx.MAX_FEE; + if (this.fee > MTX.MAX_FEE) + this.fee = MTX.MAX_FEE; // Transfer `total` funds maximum. this.fund(); }; +/** + * An error thrown from the coin selector. + * @exports FundingError + * @constructor + * @extends Error + * @param {String} msg + * @param {Amount} available + * @param {Amount} required + * @property {String} message - Error message. + * @property {Amount} availableFunds + * @property {Amount} requiredFunds + */ + +function FundingError(msg, available, required) { + Error.call(this); + + if (Error.captureStackTrace) + Error.captureStackTrace(this, FundingError); + + this.type = 'FundingError'; + this.message = msg; + this.availableFunds = -1; + this.requiredFunds = -1; + + if (available != null) { + this.message += ' (available=' + Amount.btc(available) + ','; + this.message += ' required=' + Amount.btc(required) + ')'; + this.availableFunds = available; + this.requiredFunds = required; + } +} + +util.inherits(FundingError, Error); + /* * Helpers */ diff --git a/lib/primitives/netaddress.js b/lib/primitives/netaddress.js index f488b5a4..3418c2d7 100644 --- a/lib/primitives/netaddress.js +++ b/lib/primitives/netaddress.js @@ -7,7 +7,7 @@ 'use strict'; var assert = require('assert'); -var constants = require('../protocol/constants'); +var common = require('../net/common'); var Network = require('../protocol/network'); var util = require('../utils/util'); var IP = require('../utils/ip'); @@ -43,6 +43,18 @@ function NetAddress(options) { this.fromOptions(options); } +/** + * Default services for + * unknown outbound peers. + * @const {Number} + * @default + */ + +NetAddress.DEFAULT_SERVICES = 0 + | common.services.NETWORK + | common.services.WITNESS + | common.services.BLOOM; + /** * Inject properties from options object. * @private @@ -89,7 +101,7 @@ NetAddress.fromOptions = function fromOptions(options) { */ NetAddress.prototype.hasNetwork = function hasNetwork() { - return (this.services & constants.services.NETWORK) !== 0; + return (this.services & common.services.NETWORK) !== 0; }; /** @@ -98,7 +110,7 @@ NetAddress.prototype.hasNetwork = function hasNetwork() { */ NetAddress.prototype.hasBloom = function hasBloom() { - return (this.services & constants.services.BLOOM) !== 0; + return (this.services & common.services.BLOOM) !== 0; }; /** @@ -107,7 +119,7 @@ NetAddress.prototype.hasBloom = function hasBloom() { */ NetAddress.prototype.hasUTXO = function hasUTXO() { - return (this.services & constants.services.GETUTXO) !== 0; + return (this.services & common.services.GETUTXO) !== 0; }; /** @@ -116,7 +128,7 @@ NetAddress.prototype.hasUTXO = function hasUTXO() { */ NetAddress.prototype.hasWitness = function hasWitness() { - return (this.services & constants.services.WITNESS) !== 0; + return (this.services & common.services.WITNESS) !== 0; }; /** @@ -218,7 +230,7 @@ NetAddress.prototype.fromHost = function fromHost(host, port, network) { this.host = host; this.port = port || network.port; - this.services = constants.services.NETWORK | constants.services.WITNESS | constants.services.BLOOM; + this.services = NetAddress.DEFAULT_SERVICES; this.ts = network.now(); this.hostname = IP.hostname(this.host, this.port); diff --git a/lib/primitives/outpoint.js b/lib/primitives/outpoint.js index e2687390..a021ddbd 100644 --- a/lib/primitives/outpoint.js +++ b/lib/primitives/outpoint.js @@ -8,9 +8,9 @@ var util = require('../utils/util'); var assert = require('assert'); -var constants = require('../protocol/constants'); var StaticWriter = require('../utils/writer'); var BufferReader = require('../utils/reader'); +var encoding = require('../utils/encoding'); /** * Represents a COutPoint. @@ -26,7 +26,7 @@ function Outpoint(hash, index) { if (!(this instanceof Outpoint)) return new Outpoint(hash, index); - this.hash = hash || constants.NULL_HASH; + this.hash = hash || encoding.NULL_HASH; this.index = index != null ? index : 0xffffffff; } @@ -61,7 +61,7 @@ Outpoint.fromOptions = function fromOptions(options) { */ Outpoint.prototype.isNull = function isNull() { - return this.index === 0xffffffff && this.hash === constants.NULL_HASH; + return this.index === 0xffffffff && this.hash === encoding.NULL_HASH; }; /** diff --git a/lib/primitives/output.js b/lib/primitives/output.js index aa3195b5..b065924e 100644 --- a/lib/primitives/output.js +++ b/lib/primitives/output.js @@ -7,15 +7,15 @@ 'use strict'; +var assert = require('assert'); var util = require('../utils/util'); -var constants = require('../protocol/constants'); -var btcutils = require('../btc/utils'); var Amount = require('../btc/amount'); var Network = require('../protocol/network'); var Script = require('../script/script'); var StaticWriter = require('../utils/staticwriter'); var BufferReader = require('../utils/reader'); -var assert = require('assert'); +var consensus = require('../protocol/consensus'); +var policy = require('../protocol/policy'); /** * Represents a transaction output. @@ -78,7 +78,7 @@ Output.fromOptions = function fromOptions(options) { */ Output.prototype.getType = function getType() { - return constants.scriptTypesByVal[this.script.getType()].toLowerCase(); + return Script.typesByVal[this.script.getType()].toLowerCase(); }; /** @@ -165,12 +165,9 @@ Output.prototype.getJSON = function getJSON(network) { */ Output.prototype.getDustThreshold = function getDustThreshold(rate) { - var scale = constants.WITNESS_SCALE_FACTOR; + var scale = consensus.WITNESS_SCALE_FACTOR; var size; - if (rate == null) - rate = constants.tx.MIN_RELAY; - if (this.script.isUnspendable()) return 0; @@ -183,7 +180,7 @@ Output.prototype.getDustThreshold = function getDustThreshold(rate) { size += 32 + 4 + 1 + 107 + 4; } - return 3 * btcutils.getMinFee(size, rate); + return 3 * policy.getMinFee(size, rate); }; /** diff --git a/lib/primitives/tx.js b/lib/primitives/tx.js index a82c6168..a3532aec 100644 --- a/lib/primitives/tx.js +++ b/lib/primitives/tx.js @@ -12,9 +12,7 @@ var util = require('../utils/util'); var encoding = require('../utils/encoding'); var co = require('../utils/co'); var crypto = require('../crypto/crypto'); -var btcutils = require('../btc/utils'); var Amount = require('../btc/amount'); -var constants = require('../protocol/constants'); var Network = require('../protocol/network'); var Script = require('../script/script'); var BufferReader = require('../utils/reader'); @@ -26,6 +24,8 @@ var Outpoint = require('./outpoint'); var InvItem = require('./invitem'); var workerPool = require('../workers/workerpool').pool; var Bloom = require('../utils/bloom'); +var consensus = require('../protocol/consensus'); +var policy = require('../protocol/policy'); /* * Constants @@ -332,7 +332,7 @@ TX.prototype.getSizes = function getSizes() { */ TX.prototype.getVirtualSize = function getVirtualSize() { - var scale = constants.WITNESS_SCALE_FACTOR; + var scale = consensus.WITNESS_SCALE_FACTOR; return (this.getWeight() + scale - 1) / scale | 0; }; @@ -344,8 +344,8 @@ TX.prototype.getVirtualSize = function getVirtualSize() { */ TX.prototype.getSigopsSize = function getSigopsSize(sigops) { - var scale = constants.WITNESS_SCALE_FACTOR; - var bytes = constants.tx.BYTES_PER_SIGOP; + var scale = consensus.WITNESS_SCALE_FACTOR; + var bytes = policy.BYTES_PER_SIGOP; var weight = Math.max(this.getWeight(), sigops * bytes); return (weight + scale - 1) / scale | 0; }; @@ -359,7 +359,7 @@ TX.prototype.getSigopsSize = function getSigopsSize(sigops) { TX.prototype.getWeight = function getWeight() { var raw = this.getSizes(); var base = raw.total - raw.witness; - return base * (constants.WITNESS_SCALE_FACTOR - 1) + raw.total; + return base * (consensus.WITNESS_SCALE_FACTOR - 1) + raw.total; }; /** @@ -418,7 +418,7 @@ TX.prototype.hasWitness = function hasWitness() { TX.prototype.signatureHash = function signatureHash(index, prev, value, type, version) { if (typeof type === 'string') - type = constants.hashType[type.toUpperCase()]; + type = Script.hashType[type.toUpperCase()]; assert(index >= 0 && index < this.inputs.length); assert(prev instanceof Script); @@ -448,11 +448,11 @@ TX.prototype.signatureHashV0 = function signatureHashV0(index, prev, type) { var i, size, bw, input, output; var hashType = type & 0x1f; - if (hashType === constants.hashType.SINGLE) { + if (hashType === Script.hashType.SINGLE) { // Bitcoind used to return 1 as an error code: // it ended up being treated like a hash. if (index >= this.outputs.length) - return util.copy(constants.ONE_HASH); + return util.copy(encoding.ONE_HASH); } // Remove all code separators. @@ -466,7 +466,7 @@ TX.prototype.signatureHashV0 = function signatureHashV0(index, prev, type) { bw.writeU32(this.version); // Serialize inputs. - if (type & constants.hashType.ANYONECANPAY) { + if (type & Script.hashType.ANYONECANPAY) { bw.writeVarint(1); // Serialize only the current @@ -501,8 +501,8 @@ TX.prototype.signatureHashV0 = function signatureHashV0(index, prev, type) { // Sequences are 0 if NONE or SINGLE. switch (hashType) { - case constants.hashType.NONE: - case constants.hashType.SINGLE: + case Script.hashType.NONE: + case Script.hashType.SINGLE: bw.writeU32(0); break; default: @@ -514,11 +514,11 @@ TX.prototype.signatureHashV0 = function signatureHashV0(index, prev, type) { // Serialize outputs. switch (hashType) { - case constants.hashType.NONE: + case Script.hashType.NONE: // No outputs if NONE. bw.writeVarint(0); break; - case constants.hashType.SINGLE: + case Script.hashType.SINGLE: // Drop all outputs after the // current input index if SINGLE. bw.writeVarint(index + 1); @@ -569,7 +569,7 @@ TX.prototype.hashSize = function hashSize(index, prev, type) { size += 4; - if (type & constants.hashType.ANYONECANPAY) { + if (type & Script.hashType.ANYONECANPAY) { size += 1; size += 36; size += prev.getVarSize(); @@ -583,10 +583,10 @@ TX.prototype.hashSize = function hashSize(index, prev, type) { } switch (type & 0x1f) { - case constants.hashType.NONE: + case Script.hashType.NONE: size += 1; break; - case constants.hashType.SINGLE: + case Script.hashType.SINGLE: size += encoding.sizeVarint(index + 1); size += 9 * index; size += this.outputs[index].getSize(); @@ -616,9 +616,12 @@ TX.prototype.hashSize = function hashSize(index, prev, type) { */ TX.prototype.signatureHashV1 = function signatureHashV1(index, prev, value, type) { - var i, bw, size, input, output, prevouts, sequences, outputs; + var prevouts = encoding.ZERO_HASH; + var sequences = encoding.ZERO_HASH; + var outputs = encoding.ZERO_HASH; + var i, bw, size, input, output; - if (!(type & constants.hashType.ANYONECANPAY)) { + if (!(type & Script.hashType.ANYONECANPAY)) { if (this._hashPrevouts) { prevouts = this._hashPrevouts; } else { @@ -634,13 +637,11 @@ TX.prototype.signatureHashV1 = function signatureHashV1(index, prev, value, type if (!this.mutable) this._hashPrevouts = prevouts; } - } else { - prevouts = util.copy(constants.ZERO_HASH); } - if (!(type & constants.hashType.ANYONECANPAY) - && (type & 0x1f) !== constants.hashType.SINGLE - && (type & 0x1f) !== constants.hashType.NONE) { + if (!(type & Script.hashType.ANYONECANPAY) + && (type & 0x1f) !== Script.hashType.SINGLE + && (type & 0x1f) !== Script.hashType.NONE) { if (this._hashSequence) { sequences = this._hashSequence; } else { @@ -656,12 +657,10 @@ TX.prototype.signatureHashV1 = function signatureHashV1(index, prev, value, type if (!this.mutable) this._hashSequence = sequences; } - } else { - sequences = util.copy(constants.ZERO_HASH); } - if ((type & 0x1f) !== constants.hashType.SINGLE - && (type & 0x1f) !== constants.hashType.NONE) { + if ((type & 0x1f) !== Script.hashType.SINGLE + && (type & 0x1f) !== Script.hashType.NONE) { if (this._hashOutputs) { outputs = this._hashOutputs; } else { @@ -684,11 +683,9 @@ TX.prototype.signatureHashV1 = function signatureHashV1(index, prev, value, type if (!this.mutable) this._hashOutputs = outputs; } - } else if ((type & 0x1f) === constants.hashType.SINGLE && index < this.outputs.length) { + } else if ((type & 0x1f) === Script.hashType.SINGLE && index < this.outputs.length) { output = this.outputs[index]; outputs = crypto.hash256(output.toRaw()); - } else { - outputs = util.copy(constants.ZERO_HASH); } input = this.inputs[index]; @@ -1128,7 +1125,7 @@ TX.prototype.hasCoins = function hasCoins(view) { */ TX.prototype.isFinal = function isFinal(height, ts) { - var threshold = constants.LOCKTIME_THRESHOLD; + var threshold = consensus.LOCKTIME_THRESHOLD; var i, input; if (this.locktime === 0) @@ -1203,17 +1200,17 @@ TX.prototype.getScripthashSigops = function getScripthashSigops(view) { */ TX.prototype.getSigopsCost = function getSigopsCost(view, flags) { - var scale = constants.WITNESS_SCALE_FACTOR; + var scale = consensus.WITNESS_SCALE_FACTOR; var cost = this.getLegacySigops() * scale; var i, input, coin; if (flags == null) - flags = constants.flags.STANDARD_VERIFY_FLAGS; + flags = Script.flags.STANDARD_VERIFY_FLAGS; if (this.isCoinbase()) return cost; - if (flags & constants.flags.VERIFY_P2SH) + if (flags & Script.flags.VERIFY_P2SH) cost += this.getScripthashSigops(view) * scale; for (i = 0; i < this.inputs.length; i++) { @@ -1241,10 +1238,10 @@ TX.prototype.getSigopsCost = function getSigopsCost(view, flags) { */ TX.prototype.getSigops = function getSigops(view, flags) { - var scale = constants.WITNESS_SCALE_FACTOR; + var scale = consensus.WITNESS_SCALE_FACTOR; if (flags == null) - flags = constants.flags.STANDARD_VERIFY_FLAGS; + flags = Script.flags.STANDARD_VERIFY_FLAGS; return (this.getSigopsCost(view, flags) + scale - 1) / scale | 0; }; @@ -1278,7 +1275,7 @@ TX.prototype.isSane = function isSane(ret) { return false; } - if (this.getBaseSize() > constants.block.MAX_SIZE) { + if (this.getBaseSize() > consensus.MAX_BLOCK_SIZE) { ret.reason = 'bad-txns-oversize'; ret.score = 100; return false; @@ -1293,7 +1290,7 @@ TX.prototype.isSane = function isSane(ret) { return false; } - if (output.value > constants.MAX_MONEY) { + if (output.value > consensus.MAX_MONEY) { ret.reason = 'bad-txns-vout-toolarge'; ret.score = 100; return false; @@ -1301,7 +1298,7 @@ TX.prototype.isSane = function isSane(ret) { total += output.value; - if (total < 0 || total > constants.MAX_MONEY) { + if (total < 0 || total > consensus.MAX_MONEY) { ret.reason = 'bad-txns-txouttotal-toolarge'; ret.score = 100; return false; @@ -1362,13 +1359,13 @@ TX.prototype.isStandard = function isStandard(ret) { if (!ret) ret = new VerifyResult(); - if (this.version < 1 || this.version > constants.tx.MAX_VERSION) { + if (this.version < 1 || this.version > policy.MAX_TX_VERSION) { ret.reason = 'version'; ret.score = 0; return false; } - if (this.getWeight() >= constants.tx.MAX_WEIGHT) { + if (this.getWeight() >= policy.MAX_TX_WEIGHT) { ret.reason = 'tx-size'; ret.score = 0; return false; @@ -1404,13 +1401,13 @@ TX.prototype.isStandard = function isStandard(ret) { continue; } - if (output.script.isMultisig() && !constants.tx.BARE_MULTISIG) { + if (output.script.isMultisig() && !policy.BARE_MULTISIG) { ret.reason = 'bare-multisig'; ret.score = 0; return false; } - if (output.isDust(constants.tx.MIN_RELAY)) { + if (output.isDust(policy.MIN_RELAY)) { ret.reason = 'dust'; ret.score = 0; return false; @@ -1436,7 +1433,7 @@ TX.prototype.isStandard = function isStandard(ret) { */ TX.prototype.hasStandardInputs = function hasStandardInputs(view) { - var maxSigops = constants.script.MAX_SCRIPTHASH_SIGOPS; + var maxSigops = policy.MAX_SCRIPTHASH_SIGOPS; var i, input, coin, redeem; if (this.isCoinbase()) @@ -1564,10 +1561,10 @@ TX.prototype.getWitnessStandard = function getWitnessStandard(view) { redeem = input.witness.get(input.witness.length - 1); - if (redeem.length > constants.script.MAX_SIZE) + if (redeem.length > consensus.MAX_SCRIPT_SIZE) return BAD_WITNESS; - if (redeem.length > constants.script.MAX_P2WSH_SIZE) + if (redeem.length > policy.MAX_P2WSH_SIZE) ret = BAD_NONSTD_P2WSH; hash = crypto.sha256(redeem); @@ -1579,14 +1576,14 @@ TX.prototype.getWitnessStandard = function getWitnessStandard(view) { if (input.witness.length - 1 > 604) return BAD_WITNESS; - if (input.witness.length - 1 > constants.script.MAX_P2WSH_STACK) + if (input.witness.length - 1 > policy.MAX_P2WSH_STACK) ret = BAD_NONSTD_P2WSH; for (j = 0; j < input.witness.length; j++) { - if (input.witness.get(j).length > constants.script.MAX_PUSH) + if (input.witness.get(j).length > consensus.MAX_SCRIPT_PUSH) return BAD_WITNESS; - if (input.witness.get(j).length > constants.script.MAX_P2WSH_PUSH) + if (input.witness.get(j).length > policy.MAX_P2WSH_PUSH) ret = BAD_NONSTD_P2WSH; } @@ -1668,7 +1665,7 @@ TX.prototype.checkInputs = function checkInputs(view, height, ret) { } if (coins.coinbase) { - if (height - coins.height < constants.tx.COINBASE_MATURITY) { + if (height - coins.height < consensus.COINBASE_MATURITY) { ret.reason = 'bad-txns-premature-spend-of-coinbase'; ret.score = 0; return false; @@ -1677,7 +1674,7 @@ TX.prototype.checkInputs = function checkInputs(view, height, ret) { coin = view.getOutput(input); - if (coin.value < 0 || coin.value > constants.MAX_MONEY) { + if (coin.value < 0 || coin.value > consensus.MAX_MONEY) { ret.reason = 'bad-txns-inputvalues-outofrange'; ret.score = 100; return false; @@ -1685,7 +1682,7 @@ TX.prototype.checkInputs = function checkInputs(view, height, ret) { total += coin.value; - if (total < 0 || total > constants.MAX_MONEY) { + if (total < 0 || total > consensus.MAX_MONEY) { ret.reason = 'bad-txns-inputvalues-outofrange'; ret.score = 100; return false; @@ -1709,7 +1706,7 @@ TX.prototype.checkInputs = function checkInputs(view, height, ret) { return false; } - if (fee > constants.MAX_MONEY) { + if (fee > consensus.MAX_MONEY) { ret.reason = 'bad-txns-fee-outofrange'; ret.score = 100; return false; @@ -1840,7 +1837,7 @@ TX.prototype.getChainValue = function getChainValue(view, height) { TX.prototype.isFree = function isFree(view, height, size) { var priority = this.getPriority(view, height, size); - return priority > constants.tx.FREE_THRESHOLD; + return priority > policy.FREE_THRESHOLD; }; /** @@ -1856,7 +1853,7 @@ TX.prototype.getMinFee = function getMinFee(size, rate) { if (size == null) size = this.getVirtualSize(); - return btcutils.getMinFee(size, rate); + return policy.getMinFee(size, rate); }; /** @@ -1873,7 +1870,7 @@ TX.prototype.getRoundFee = function getRoundFee(size, rate) { if (size == null) size = this.getVirtualSize(); - return btcutils.getRoundFee(size, rate); + return policy.getRoundFee(size, rate); }; /** @@ -1888,7 +1885,7 @@ TX.prototype.getRate = function getRate(view, size) { if (size == null) size = this.getVirtualSize(); - return btcutils.getRate(size, this.getFee(view)); + return policy.getRate(size, this.getFee(view)); }; /** @@ -2555,6 +2552,72 @@ TX.isTX = function isTX(obj) { && typeof obj.witnessHash === 'function'; }; +/** + * Verify the nLockTime of a transaction. + * @param {Number} index - Index of input being verified. + * @param {Number} locktime - Locktime to verify against (max=u32). + * @returns {Boolean} + */ + +TX.prototype.verifyLocktime = function verifyLocktime(index, locktime) { + var threshold = consensus.LOCKTIME_THRESHOLD; + var input = this.inputs[index]; + + if (!( + (this.locktime < threshold && locktime < threshold) + || (this.locktime >= threshold && locktime >= threshold) + )) { + return false; + } + + if (locktime > this.locktime) + return false; + + if (input.sequence === 0xffffffff) + return false; + + return true; +}; + +/** + * Verify the nSequence locktime of a transaction. + * @param {Number} index - Index of input being verified. + * @param {Number} sequence - Locktime to verify against (max=u32). + * @returns {Boolean} + */ + +TX.prototype.verifySequence = function verifySequence(index, sequence) { + var input = this.inputs[index]; + var mask, seq1, seq2; + + if ((sequence & consensus.SEQUENCE_DISABLE_FLAG) !== 0) + return true; + + if (this.version < 2) + return false; + + if (input.sequence & consensus.SEQUENCE_DISABLE_FLAG) + return false; + + mask = consensus.SEQUENCE_TYPE_FLAG | consensus.SEQUENCE_MASK; + seq1 = input.sequence & mask; + seq2 = sequence & mask; + + if (!( + (seq1 < consensus.SEQUENCE_TYPE_FLAG + && seq2 < consensus.SEQUENCE_TYPE_FLAG) + || (seq1 >= consensus.SEQUENCE_TYPE_FLAG + && seq2 >= consensus.SEQUENCE_TYPE_FLAG) + )) { + return false; + } + + if (seq2 > seq1) + return false; + + return true; +}; + /* * Helpers */ diff --git a/lib/protocol/consensus.js b/lib/protocol/consensus.js new file mode 100644 index 00000000..4d2da34f --- /dev/null +++ b/lib/protocol/consensus.js @@ -0,0 +1,290 @@ +/*! + * consensus.js - consensus constants and helpers for bcoin + * Copyright (c) 2014-2015, Fedor Indutny (MIT License) + * Copyright (c) 2014-2016, Christopher Jeffrey (MIT License). + * https://github.com/bcoin-org/bcoin + */ + +'use strict'; + +var assert = require('assert'); +var BN = require('bn.js'); + +/** + * One bitcoin in satoshis. + * @const {Amount} + * @default + */ + +exports.COIN = 100000000; + +/** + * Maximum amount of money in satoshis: + * `21million * 1btc` (consensus). + * @const {Amount} + * @default + */ + +exports.MAX_MONEY = 21000000 * exports.COIN; + +/** + * Maximum block base size (consensus). + * @const {Number} + * @default + */ + +exports.MAX_BLOCK_SIZE = 1000000; + +/** + * Maximum block serialization size (protocol). + * @const {Number} + * @default + */ + +exports.MAX_RAW_BLOCK_SIZE = 4000000; + +/** + * Maximum block weight (consensus). + * @const {Number} + * @default + */ + +exports.MAX_BLOCK_WEIGHT = 4000000; + +/** + * Maximum block sigops (consensus). + * @const {Number} + * @default + */ + +exports.MAX_BLOCK_SIGOPS = 1000000 / 50; + +/** + * Maximum block sigops cost (consensus). + * @const {Number} + * @default + */ + +exports.MAX_BLOCK_SIGOPS_COST = 80000; + +/** + * Number of blocks before a coinbase + * spend can occur (consensus). + * @const {Number} + * @default + */ + +exports.COINBASE_MATURITY = 100; + +/** + * Amount to multiply base/non-witness sizes by. + * @const {Number} + * @default + */ + +exports.WITNESS_SCALE_FACTOR = 4; + +/** + * nLockTime threshold for differentiating + * between height and time (consensus). + * Tue Nov 5 00:53:20 1985 UTC + * @const {Number} + * @default + */ + +exports.LOCKTIME_THRESHOLD = 500000000; + +/** + * Highest nSequence bit -- disables + * sequence locktimes (consensus). + * @const {Number} + */ + +exports.SEQUENCE_DISABLE_FLAG = (1 << 31) >>> 0; + +/** + * Sequence time: height or time (consensus). + * @const {Number} + * @default + */ + +exports.SEQUENCE_TYPE_FLAG = 1 << 22; + +/** + * Sequence granularity for time (consensus). + * @const {Number} + * @default + */ + +exports.SEQUENCE_GRANULARITY = 9; + +/** + * Sequence mask (consensus). + * @const {Number} + * @default + */ + +exports.SEQUENCE_MASK = 0x0000ffff; + +/** + * Max serialized script size (consensus). + * @const {Number} + * @default + */ + +exports.MAX_SCRIPT_SIZE = 10000; + +/** + * Max stack size during execution (consensus). + * @const {Number} + * @default + */ + +exports.MAX_SCRIPT_STACK = 1000; + +/** + * Max script element size (consensus). + * @const {Number} + * @default + */ + +exports.MAX_SCRIPT_PUSH = 520; + +/** + * Max opcodes executed (consensus). + * @const {Number} + * @default + */ + +exports.MAX_SCRIPT_OPS = 201; + +/** + * Max `n` value for multisig (consensus). + * @const {Number} + * @default + */ + +exports.MAX_MULTISIG_PUBKEYS = 20; + +/** + * The date bip16 (p2sh) was activated (consensus). + * @const {Number} + * @default + */ + +exports.BIP16_TIME = 1333238400; + +/** + * Convert a compact number to a big number. + * Used for `block.bits` -> `target` conversion. + * @param {Number} compact + * @returns {BN} + */ + +exports.fromCompact = function fromCompact(compact) { + var exponent = compact >>> 24; + var negative = (compact >>> 23) & 1; + var mantissa = compact & 0x7fffff; + var num; + + if (compact === 0) + return new BN(0); + + if (exponent <= 3) { + mantissa >>>= 8 * (3 - exponent); + num = new BN(mantissa); + } else { + num = new BN(mantissa); + num.iushln(8 * (exponent - 3)); + } + + if (negative) + num.ineg(); + + return num; +}; + +/** + * Convert a big number to a compact number. + * Used for `target` -> `block.bits` conversion. + * @param {BN} num + * @returns {Number} + */ + +exports.toCompact = function toCompact(num) { + var mantissa, exponent, compact; + + if (num.cmpn(0) === 0) + return 0; + + exponent = num.byteLength(); + + if (exponent <= 3) { + mantissa = num.toNumber(); + mantissa <<= 8 * (3 - exponent); + } else { + mantissa = num.ushrn(8 * (exponent - 3)).toNumber(); + } + + if (mantissa & 0x800000) { + mantissa >>= 8; + exponent++; + } + + compact = (exponent << 24) | mantissa; + + if (num.isNeg()) + compact |= 0x800000; + + compact >>>= 0; + + return compact; +}; + +/** + * Verify proof-of-work. + * @param {Hash} hash + * @param {Number} bits + * @returns {Boolean} + */ + +exports.verifyPOW = function verifyPOW(hash, bits) { + var target = exports.fromCompact(bits); + + if (target.isNeg() || target.cmpn(0) === 0) + return false; + + hash = new BN(hash, 'le'); + + if (hash.cmp(target) > 0) + return false; + + return true; +}; + +/** + * Calculate block subsidy. + * @param {Number} height - Reward era by height. + * @returns {Amount} + */ + +exports.getReward = function getReward(height, interval) { + var halvings = Math.floor(height / interval); + + assert(height >= 0, 'Bad height for reward.'); + + // BIP 42 (well, our own version of it, + // since we can only handle 32 bit shifts). + // https://github.com/bitcoin/bips/blob/master/bip-0042.mediawiki + if (halvings >= 33) + return 0; + + // We need to shift right by `halvings`, + // but 50 btc is a 33 bit number, so we + // cheat. We only start halving once the + // halvings are at least 1. + if (halvings === 0) + return 5000000000; + + return 2500000000 >>> (halvings - 1); +}; diff --git a/lib/protocol/constants.js b/lib/protocol/constants.js index 069ca9c1..2b418e60 100644 --- a/lib/protocol/constants.js +++ b/lib/protocol/constants.js @@ -8,761 +8,22 @@ 'use strict'; /** - * @module constants - */ - -var util = require('../utils/util'); - -/** - * Minimum protocol version we're willing to talk to. - * @const {Number} - * @default - */ - -exports.MIN_VERSION = 70001; - -/** - * BCoin's protocol version. - * @const {Number} - * @default - */ - -exports.VERSION = 70014; - -/** - * Max message size (~4mb with segwit, formerly 2mb) - * @const {Number} - * @default - */ - -exports.MAX_MESSAGE = 4 * 1000 * 1000; - -/** - * Service bits. - * @enum {Number} - * @default - */ - -exports.services = { - /** - * Whether network services are enabled. - */ - - NETWORK: (1 << 0), - - /** - * Whether the peer supports the getutxos packet. - */ - - GETUTXO: (1 << 1), - - /** - * Whether the peer supports BIP37. - */ - - BLOOM: (1 << 2), - - /** - * Whether the peer supports segregated witness. - */ - - WITNESS: (1 << 3) -}; - -/** - * BCoin's services (we support everything). - * @const {Number} - * @default - */ - -exports.LOCAL_SERVICES = 0 - | exports.services.NETWORK - | exports.services.GETUTXO - | exports.services.BLOOM - | exports.services.WITNESS; - -/** - * Script opcodes. - * @enum {Number} - * @default - */ - -exports.opcodes = { - OP_FALSE: 0x00, - OP_0: 0x00, - - OP_PUSHDATA1: 0x4c, - OP_PUSHDATA2: 0x4d, - OP_PUSHDATA4: 0x4e, - - OP_1NEGATE: 0x4f, - - OP_RESERVED: 0x50, - - OP_TRUE: 0x51, - OP_1: 0x51, - OP_2: 0x52, - OP_3: 0x53, - OP_4: 0x54, - OP_5: 0x55, - OP_6: 0x56, - OP_7: 0x57, - OP_8: 0x58, - OP_9: 0x59, - OP_10: 0x5a, - OP_11: 0x5b, - OP_12: 0x5c, - OP_13: 0x5d, - OP_14: 0x5e, - OP_15: 0x5f, - OP_16: 0x60, - - OP_NOP: 0x61, - OP_VER: 0x62, - OP_IF: 0x63, - OP_NOTIF: 0x64, - OP_VERIF: 0x65, - OP_VERNOTIF: 0x66, - OP_ELSE: 0x67, - OP_ENDIF: 0x68, - OP_VERIFY: 0x69, - OP_RETURN: 0x6a, - - OP_TOALTSTACK: 0x6b, - OP_FROMALTSTACK: 0x6c, - OP_2DROP: 0x6d, - OP_2DUP: 0x6e, - OP_3DUP: 0x6f, - OP_2OVER: 0x70, - OP_2ROT: 0x71, - OP_2SWAP: 0x72, - OP_IFDUP: 0x73, - OP_DEPTH: 0x74, - OP_DROP: 0x75, - OP_DUP: 0x76, - OP_NIP: 0x77, - OP_OVER: 0x78, - OP_PICK: 0x79, - OP_ROLL: 0x7a, - OP_ROT: 0x7b, - OP_SWAP: 0x7c, - OP_TUCK: 0x7d, - - OP_CAT: 0x7e, - OP_SUBSTR: 0x7f, - OP_LEFT: 0x80, - OP_RIGHT: 0x81, - OP_SIZE: 0x82, - - OP_INVERT: 0x83, - OP_AND: 0x84, - OP_OR: 0x85, - OP_XOR: 0x86, - OP_EQUAL: 0x87, - OP_EQUALVERIFY: 0x88, - - OP_RESERVED1: 0x89, - OP_RESERVED2: 0x8a, - - OP_1ADD: 0x8b, - OP_1SUB: 0x8c, - OP_2MUL: 0x8d, - OP_2DIV: 0x8e, - OP_NEGATE: 0x8f, - OP_ABS: 0x90, - OP_NOT: 0x91, - OP_0NOTEQUAL: 0x92, - OP_ADD: 0x93, - OP_SUB: 0x94, - OP_MUL: 0x95, - OP_DIV: 0x96, - OP_MOD: 0x97, - OP_LSHIFT: 0x98, - OP_RSHIFT: 0x99, - OP_BOOLAND: 0x9a, - OP_BOOLOR: 0x9b, - OP_NUMEQUAL: 0x9c, - OP_NUMEQUALVERIFY: 0x9d, - OP_NUMNOTEQUAL: 0x9e, - OP_LESSTHAN: 0x9f, - OP_GREATERTHAN: 0xa0, - OP_LESSTHANOREQUAL: 0xa1, - OP_GREATERTHANOREQUAL: 0xa2, - OP_MIN: 0xa3, - OP_MAX: 0xa4, - OP_WITHIN: 0xa5, - - OP_RIPEMD160: 0xa6, - OP_SHA1: 0xa7, - OP_SHA256: 0xa8, - OP_HASH160: 0xa9, - OP_HASH256: 0xaa, - OP_CODESEPARATOR: 0xab, - OP_CHECKSIG: 0xac, - OP_CHECKSIGVERIFY: 0xad, - OP_CHECKMULTISIG: 0xae, - OP_CHECKMULTISIGVERIFY: 0xaf, - - OP_EVAL: 0xb0, - OP_NOP1: 0xb0, - OP_NOP2: 0xb1, - OP_CHECKLOCKTIMEVERIFY: 0xb1, - OP_NOP3: 0xb2, - OP_CHECKSEQUENCEVERIFY: 0xb2, - OP_NOP4: 0xb3, - OP_NOP5: 0xb4, - OP_NOP6: 0xb5, - OP_NOP7: 0xb6, - OP_NOP8: 0xb7, - OP_NOP9: 0xb8, - OP_NOP10: 0xb9, - - OP_PUBKEYHASH: 0xfd, - OP_PUBKEY: 0xfe, - OP_INVALIDOPCODE: 0xff -}; - -/** - * Opcodes by value. - * @const {RevMap} - */ - -exports.opcodesByVal = util.revMap(exports.opcodes); - -/** - * One bitcoin in satoshis. - * @const {Amount} - * @default - */ - -exports.COIN = 100000000; - -/** - * One bitcoin / 100. - * @const {Amount} - * @default - */ - -exports.CENT = 1000000; - -/** - * Maximum amount of money in satoshis (1btc * 21million) - * @const {Amount} - * @default - */ - -exports.MAX_MONEY = 21000000 * exports.COIN; - -/** - * Sighash Types. - * @enum {SighashType} - * @default - */ - -exports.hashType = { - /** - * Sign all outputs. - */ - - ALL: 1, - - /** - * Do not sign outputs (zero sequences). - */ - - NONE: 2, - - /** - * Sign output at the same index (zero sequences). - */ - - SINGLE: 3, - - /** - * Sign only the current input (mask). - */ - - ANYONECANPAY: 0x80 -}; - -/** - * Sighash types by value. - * @const {RevMap} - */ - -exports.hashTypeByVal = util.revMap(exports.hashType); - -/** - * Amount to multiply base/non-witness sizes by. - * @const {Number} - * @default - */ - -exports.WITNESS_SCALE_FACTOR = 4; - -/** - * Block-related constants. - * @enum {Number} - * @default - */ - -exports.block = { - MAX_SIZE: 1000000, - MAX_RAW_SIZE: 4000000, - MAX_WEIGHT: 4000000, - MAX_SIGOPS: 1000000 / 50, - MAX_SIGOPS_COST: 80000, - MEDIAN_TIMESPAN: 11, - BIP16_TIME: 1333238400, - SIGHASH_LIMIT: 1300000000 -}; - -/** - * Map of historical blocks which create duplicate transactions hashes. - * @see https://github.com/bitcoin/bips/blob/master/bip-0030.mediawiki - * @const {Object} - * @default - */ - -exports.bip30 = { - 91842: 'eccae000e3c8e4e093936360431f3b7603c563c1ff6181390a4d0a0000000000', - 91880: '21d77ccb4c08386a04ac0196ae10f6a1d2c2a377558ca190f143070000000000' -}; - -/** - * TX-related constants. - * @enum {Number} - * @default - */ - -exports.tx = { - MAX_VERSION: 2, - MAX_SIZE: 100000, - MAX_WEIGHT: 400000, - MIN_FEE: 10000, - MAX_FEE: exports.COIN / 10, - MIN_RELAY: 1000, - BARE_MULTISIG: true, - FREE_THRESHOLD: exports.COIN * 144 / 250, - MAX_SIGOPS: exports.block.MAX_SIGOPS / 5, - MAX_SIGOPS_COST: exports.block.MAX_SIGOPS_COST / 5, - BYTES_PER_SIGOP: 20, - COINBASE_MATURITY: 100 -}; - -/** - * Script-related constants. - * @enum {Number} - * @default - */ - -exports.script = { - MAX_SIZE: 10000, - MAX_STACK: 1000, - MAX_PUSH: 520, - MAX_OPS: 201, - MAX_MULTISIG_PUBKEYS: 20, - MAX_SCRIPTHASH_SIGOPS: 15, - MAX_OP_RETURN_BYTES: 83, - MAX_OP_RETURN: 80, - BYTES_PER_SIGOP: 20, - MAX_P2WSH_STACK: 100, - MAX_P2WSH_PUSH: 80, - MAX_P2WSH_SIZE: 3600 -}; - -/** - * Mempool-related constants. - * @enum {Number} - * @default - */ - -exports.mempool = { - /** - * Ancestor limit. - */ - - ANCESTOR_LIMIT: 25, - - /** - * Maximum mempool size in bytes. - */ - - MAX_MEMPOOL_SIZE: 100 * 1000000, - - /** - * The time at which transactions - * fall out of the mempool. - */ - - MEMPOOL_EXPIRY: 72 * 60 * 60, - - /** - * Maximum number of orphan transactions. - */ - - MAX_ORPHAN_TX: 100 -}; - -/** - * Reject codes. Note that `internal` and higher - * are not meant for use on the p2p network. - * @enum {Number} - * @default - */ - -exports.reject = { - MALFORMED: 0x01, - INVALID: 0x10, - OBSOLETE: 0x11, - DUPLICATE: 0x12, - NONSTANDARD: 0x40, - DUST: 0x41, - INSUFFICIENTFEE: 0x42, - CHECKPOINT: 0x43, - // Internal codes (NOT FOR USE ON NETWORK) - INTERNAL: 0x100, - HIGHFEE: 0x100, - ALREADYKNOWN: 0x101, - CONFLICT: 0x102 -}; - -/** - * Reject codes by value. - * @const {RevMap} - */ - -exports.rejectByVal = util.revMap(exports.reject); - -/** - * HD-related constants. - * @const {Object} - * @default - */ - -exports.hd = { - HARDENED: 0x80000000, - MAX_INDEX: 0x100000000, - MIN_ENTROPY: 128, - MAX_ENTROPY: 512 -}; - -/** - * nLockTime threshold for differentiating - * between height and time. - * Tue Nov 5 00:53:20 1985 UTC - * @const {Number} - * @default - */ - -exports.LOCKTIME_THRESHOLD = 500000000; - -/** - * Sequence locktime-related constants. - * @enum {Number} - * @default - */ - -exports.sequence = { - /** - * Highest nSequence bit (disables sequence locktimes). - */ - - DISABLE_FLAG: (1 << 31) >>> 0, - - /** - * Type (height or time). - */ - - TYPE_FLAG: 1 << 22, - - /** - * Sequence granularity. - */ - - GRANULARITY: 9, - - /** - * Mask. - */ - - MASK: 0x0000ffff -}; - -/** - * A hash of all zeroes with a `1` at the - * end (used for the SIGHASH_SINGLE bug). - * @const {Buffer} - * @default - */ - -exports.ONE_HASH = new Buffer( - '0100000000000000000000000000000000000000000000000000000000000000', - 'hex' -); - -/** - * A hash of all zeroes. - * @const {Buffer} - * @default - */ - -exports.ZERO_HASH = new Buffer( - '0000000000000000000000000000000000000000000000000000000000000000', - 'hex' -); - -/** - * A hash of all 0xff. - * @const {Buffer} - * @default - */ - -exports.MAX_HASH = new Buffer( - 'ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff', - 'hex' -); - -/** - * A hash of all zeroes. - * @const {String} - * @default - */ - -exports.NULL_HASH = - '0000000000000000000000000000000000000000000000000000000000000000'; - -/** - * A hash of all 0xff. - * @const {String} - * @default - */ - -exports.HIGH_HASH = - 'ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff'; - -/** - * A hash of all zeroes. - * @const {Buffer} - * @default - */ - -exports.ZERO_HASH160 = new Buffer( - '0000000000000000000000000000000000000000', - 'hex' -); - -/** - * A hash of all 0xff. - * @const {String} - * @default - */ - -exports.MAX_HASH160 = new Buffer( - 'ffffffffffffffffffffffffffffffffffffffff', - 'hex' -); - -/** - * A hash of all zeroes. - * @const {String} - * @default - */ - -exports.NULL_HASH160 = '0000000000000000000000000000000000000000'; - -/** - * A hash of all 0xff. - * @const {String} - * @default - */ - -exports.HIGH_HASH160 = 'ffffffffffffffffffffffffffffffffffffffff'; - -/** - * A compressed pubkey of all zeroes. - * @const {Buffer} - * @default - */ - -exports.ZERO_KEY = new Buffer( - '000000000000000000000000000000000000000000000000000000000000000000', - 'hex' -); - -/** - * A 73 byte signature of all zeroes. - * @const {Buffer} - * @default - */ - -exports.ZERO_SIG = new Buffer('' - + '0000000000000000000000000000000000000000000000000000000000000000' - + '0000000000000000000000000000000000000000000000000000000000000000' - + '000000000000000000', - 'hex' -); - -/** - * A 64 byte signature of all zeroes. - * @const {Buffer} - * @default - */ - -exports.ZERO_SIG64 = new Buffer('' - + '0000000000000000000000000000000000000000000000000000000000000000' - + '0000000000000000000000000000000000000000000000000000000000000000', - 'hex' -); - -/** - * 4 zero bytes. - * @const {Buffer} - * @default - */ - -exports.ZERO_U32 = new Buffer('00000000', 'hex'); - -/** - * 8 zero bytes. - * @const {Buffer} - * @default - */ - -exports.ZERO_U64 = new Buffer('0000000000000000', 'hex'); - -/** - * BCoin version. - * @const {String} - * @default - */ - -exports.USER_VERSION = require('../../package.json').version; - -/** - * BCoin user agent: `/bcoin:{version}/`. - * @const {String} - * @default - */ - -exports.USER_AGENT = '/bcoin:' + exports.USER_VERSION + '/'; - -/** - * Amount of time to ban misbheaving peers. - * @const {Number} - * @default - */ - -exports.BAN_TIME = 24 * 60 * 60; - -/** - * Ban score threshold before ban is placed in effect. - * @const {Number} - * @default - */ - -exports.BAN_SCORE = 100; - -/** - * Output script types. + * Locktime flags. * @enum {Number} */ -exports.scriptTypes = { - NONSTANDARD: 0, - PUBKEY: 1, - PUBKEYHASH: 2, - SCRIPTHASH: 3, - MULTISIG: 4, - NULLDATA: 5, - WITNESSMALFORMED: 0x80 | 0, - WITNESSSCRIPTHASH: 0x80 | 1, - WITNESSPUBKEYHASH: 0x80 | 2, - WITNESSMASTHASH: 0x80 | 3 +exports.lockFlags = { + VERIFY_SEQUENCE: 1 << 0, + MEDIAN_TIME_PAST: 1 << 1 }; -/** - * Output script types by value. - * @const {RevMap} - */ - -exports.scriptTypesByVal = util.revMap(exports.scriptTypes); - -/** - * Script and locktime flags. See {@link VerifyFlags}. - * @enum {Number} - */ - -exports.flags = { - VERIFY_NONE: 0, - VERIFY_P2SH: (1 << 0), - VERIFY_STRICTENC: (1 << 1), - VERIFY_DERSIG: (1 << 2), - VERIFY_LOW_S: (1 << 3), - VERIFY_NULLDUMMY: (1 << 4), - VERIFY_SIGPUSHONLY: (1 << 5), - VERIFY_MINIMALDATA: (1 << 6), - VERIFY_DISCOURAGE_UPGRADABLE_NOPS: (1 << 7), - VERIFY_CLEANSTACK: (1 << 8), - VERIFY_CHECKLOCKTIMEVERIFY: (1 << 9), - VERIFY_CHECKSEQUENCEVERIFY: (1 << 10), - VERIFY_WITNESS: (1 << 11), - VERIFY_DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM: (1 << 12), - VERIFY_MINIMALIF: (1 << 13), - VERIFY_NULLFAIL: (1 << 14), - VERIFY_WITNESS_PUBKEYTYPE: (1 << 15), - VERIFY_MAST: (1 << 16), // should be 1 << 13 - VERIFY_SEQUENCE: (1 << 0), - MEDIAN_TIME_PAST: (1 << 1) -}; - -/** - * Consensus verify flags (used for block validation). - * @const {VerifyFlags} - * @default - */ - -exports.flags.MANDATORY_VERIFY_FLAGS = exports.flags.VERIFY_P2SH; - -/** - * Standard verify flags (used for mempool validation). - * @const {VerifyFlags} - * @default - */ - -exports.flags.STANDARD_VERIFY_FLAGS = 0 - | exports.flags.MANDATORY_VERIFY_FLAGS - | exports.flags.VERIFY_DERSIG - | exports.flags.VERIFY_STRICTENC - | exports.flags.VERIFY_MINIMALDATA - | exports.flags.VERIFY_NULLDUMMY - | exports.flags.VERIFY_DISCOURAGE_UPGRADABLE_NOPS - | exports.flags.VERIFY_CLEANSTACK - | exports.flags.VERIFY_MINIMALIF - | exports.flags.VERIFY_NULLFAIL - | exports.flags.VERIFY_CHECKLOCKTIMEVERIFY - | exports.flags.VERIFY_CHECKSEQUENCEVERIFY - | exports.flags.VERIFY_LOW_S - | exports.flags.VERIFY_WITNESS - | exports.flags.VERIFY_DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM - | exports.flags.VERIFY_WITNESS_PUBKEYTYPE; - -/** - * Standard-not-mandatory flags. - * @const {VerifyFlags} - * @default - */ - -exports.flags.UNSTANDARD_VERIFY_FLAGS = - exports.flags.STANDARD_VERIFY_FLAGS & ~exports.flags.MANDATORY_VERIFY_FLAGS; - /** * Consensus locktime flags (used for block validation). * @const {LockFlags} * @default */ -exports.flags.MANDATORY_LOCKTIME_FLAGS = 0; +exports.lockFlags.MANDATORY_LOCKTIME_FLAGS = 0; /** * Standard locktime flags (used for mempool validation). @@ -770,9 +31,9 @@ exports.flags.MANDATORY_LOCKTIME_FLAGS = 0; * @default */ -exports.flags.STANDARD_LOCKTIME_FLAGS = 0 - | exports.flags.VERIFY_SEQUENCE - | exports.flags.MEDIAN_TIME_PAST; +exports.lockFlags.STANDARD_LOCKTIME_FLAGS = 0 + | exports.lockFlags.VERIFY_SEQUENCE + | exports.lockFlags.MEDIAN_TIME_PAST; /** * Versionbits constants. @@ -813,11 +74,3 @@ exports.thresholdStates = { ACTIVE: 3, FAILED: 4 }; - -/** - * The name of our currency. - * @const {String} - * @default - */ - -exports.CURRENCY_UNIT = 'BTC'; diff --git a/lib/protocol/network.js b/lib/protocol/network.js index 85fd3eab..75e21ca4 100644 --- a/lib/protocol/network.js +++ b/lib/protocol/network.js @@ -37,6 +37,7 @@ function Network(options) { this.genesisBlock = options.genesisBlock; this.pow = options.pow; this.block = options.block; + this.bip30 = options.bip30; this.witness = options.witness; this.oldWitness = options.oldWitness; this.activationThreshold = options.activationThreshold; diff --git a/lib/protocol/networks.js b/lib/protocol/networks.js index 0f66125b..1576434a 100644 --- a/lib/protocol/networks.js +++ b/lib/protocol/networks.js @@ -293,6 +293,18 @@ main.block = { slowHeight: 325000 }; +/** + * Map of historical blocks which create duplicate transactions hashes. + * @see https://github.com/bitcoin/bips/blob/master/bip-0030.mediawiki + * @const {Object} + * @default + */ + +main.bip30 = { + 91842: 'eccae000e3c8e4e093936360431f3b7603c563c1ff6181390a4d0a0000000000', + 91880: '21d77ccb4c08386a04ac0196ae10f6a1d2c2a377558ca190f143070000000000' +}; + /** * Whether this is a segwit-enabled network. * @const {Boolean} @@ -568,6 +580,8 @@ testnet.block = { slowHeight: 750000 }; +testnet.bip30 = {}; + testnet.witness = true; testnet.oldWitness = false; @@ -726,6 +740,8 @@ regtest.block = { slowHeight: 0x7fffffff }; +regtest.bip30 = {}; + regtest.witness = false; regtest.oldWitness = false; @@ -885,6 +901,8 @@ segnet3.block = { slowHeight: 0x7fffffff }; +segnet3.bip30 = {}; + segnet3.witness = true; segnet3.oldWitness = true; @@ -1008,6 +1026,8 @@ segnet4.block = { slowHeight: 0x7fffffff }; +segnet4.bip30 = {}; + segnet4.witness = true; segnet4.oldWitness = false; @@ -1159,6 +1179,8 @@ simnet.block = { slowHeight: 0 }; +simnet.bip30 = {}; + simnet.witness = false; simnet.oldWitness = false; diff --git a/lib/protocol/policy.js b/lib/protocol/policy.js new file mode 100644 index 00000000..37bf1095 --- /dev/null +++ b/lib/protocol/policy.js @@ -0,0 +1,235 @@ +/*! + * policy.js - bitcoin constants for bcoin + * Copyright (c) 2014-2015, Fedor Indutny (MIT License) + * Copyright (c) 2014-2016, Christopher Jeffrey (MIT License). + * https://github.com/bcoin-org/bcoin + */ + +'use strict'; + +var consensus = require('./consensus'); + +/** + * Maximum transaction version (policy). + * @const {Number} + * @default + */ + +exports.MAX_TX_VERSION = 2; + +/** + * Maximum transaction base size (policy). + * @const {Number} + * @default + */ + +exports.MAX_TX_SIZE = consensus.MAX_BLOCK_SIZE / 10; + +/** + * Maximum transaction weight (policy). + * @const {Number} + * @default + */ + +exports.MAX_TX_WEIGHT = consensus.MAX_BLOCK_WEIGHT / 10; + +/** + * Maximum number of transaction sigops (policy). + * @const {Number} + * @default + */ + +exports.MAX_TX_SIGOPS = consensus.MAX_BLOCK_SIGOPS / 5; + +/** + * Maximum cost of transaction sigops (policy). + * @const {Number} + * @default + */ + +exports.MAX_TX_SIGOPS_COST = consensus.MAX_BLOCK_SIGOPS_COST / 5; + +/** + * How much weight a sigop should + * add to virtual size (policy). + * @const {Number} + * @default + */ + +exports.BYTES_PER_SIGOP = 20; + +/** + * Minimum relay fee rate (policy). + * @const {Rate} + */ + +exports.MIN_RELAY = 1000; + +/** + * Whether bare multisig outputs + * should be relayed (policy). + * @const {Boolean} + * @default + */ + +exports.BARE_MULTISIG = true; + +/** + * Priority threshold for + * free transactions (policy). + * @const {Number} + * @default + */ + +exports.FREE_THRESHOLD = consensus.COIN * 144 / 250; + +/** + * Max sigops per redeem script (policy). + * @const {Number} + * @default + */ + +exports.MAX_SCRIPTHASH_SIGOPS = 15; + +/** + * Max serialized nulldata size (policy). + * @const {Number} + * @default + */ + +exports.MAX_OP_RETURN_BYTES = 83; + +/** + * Max pushdata size in nulldata (policy). + * @const {Number} + * @default + */ + +exports.MAX_OP_RETURN = 80; + +/** + * Max p2wsh stack size. Used for + * witness malleation checks (policy). + * @const {Number} + * @default + */ + +exports.MAX_P2WSH_STACK = 100; + +/** + * Max p2wsh push size. Used for + * witness malleation checks (policy). + * @const {Number} + * @default + */ + +exports.MAX_P2WSH_PUSH = 80; + +/** + * Max serialized p2wsh size. Used for + * witness malleation checks (policy). + * @const {Number} + * @default + */ + +exports.MAX_P2WSH_SIZE = 3600; + +/** + * Mempool ancestor limit. + * @const {Number} + * @default + */ + +exports.ANCESTOR_LIMIT = 25; + +/** + * Block weight to be reached before + * rejecting free transactions during + * mining. + * @const {Number} + * @default + */ + +exports.MIN_BLOCK_WEIGHT = 0; + +/** + * Maximum block weight to be mined. + * @const {Number} + * @default + */ + +exports.MAX_BLOCK_WEIGHT = 750000 * consensus.WITNESS_SCALE_FACTOR; + +/** + * Bottom priority threshold to be seen + * before ignoring priority transactions + * during mining. + * @const {Number} + * @default + */ + +exports.MIN_BLOCK_PRIORITY = 50000 * consensus.WITNESS_SCALE_FACTOR; + +/** + * Weight to be reached before ignoring + * priority transactions during mining. + * @const {Number} + * @default + */ + +exports.PRIORITY_BLOCK_WEIGHT = exports.FREE_THRESHOLD; + +/** + * Calculate minimum fee based on rate and size. + * @param {Number?} size + * @param {Rate?} rate - Rate of satoshi per kB. + * @returns {Amount} fee + */ + +exports.getMinFee = function getMinFee(size, rate) { + var fee; + + if (rate == null) + rate = exports.MIN_RELAY; + + fee = Math.floor(rate * size / 1000); + + if (fee === 0 && rate > 0) + fee = rate; + + return fee; +}; + +/** + * Calculate the minimum fee in order for the transaction + * to be relayable, but _round to the nearest kilobyte + * when taking into account size. + * @param {Number?} size + * @param {Rate?} rate - Rate of satoshi per kB. + * @returns {Amount} fee + */ + +exports.getRoundFee = function getRoundFee(size, rate) { + var fee; + + if (rate == null) + rate = exports.MIN_RELAY; + + fee = rate * Math.ceil(size / 1000); + + if (fee === 0 && rate > 0) + fee = rate; + + return fee; +}; + +/** + * Calculate a fee rate based on size and fees. + * @param {Number} size + * @param {Amount} fee + * @returns {Rate} + */ + +exports.getRate = function getRate(size, fee) { + return Math.floor(fee * 1000 / size); +}; diff --git a/lib/script/encoding.js b/lib/script/encoding.js index e16784a9..5c9b8f62 100644 --- a/lib/script/encoding.js +++ b/lib/script/encoding.js @@ -7,13 +7,308 @@ 'use strict'; -var BN = require('bn.js'); -var constants = require('../protocol/constants'); -var util = require('../utils/util'); var assert = require('assert'); -var opcodes = constants.opcodes; -var STACK_FALSE = new Buffer(0); -var ScriptError = require('../btc/errors').ScriptError; +var BN = require('bn.js'); +var util = require('../utils/util'); + +/** + * Script opcodes. + * @enum {Number} + * @default + */ + +exports.opcodes = { + OP_FALSE: 0x00, + OP_0: 0x00, + + OP_PUSHDATA1: 0x4c, + OP_PUSHDATA2: 0x4d, + OP_PUSHDATA4: 0x4e, + + OP_1NEGATE: 0x4f, + + OP_RESERVED: 0x50, + + OP_TRUE: 0x51, + OP_1: 0x51, + OP_2: 0x52, + OP_3: 0x53, + OP_4: 0x54, + OP_5: 0x55, + OP_6: 0x56, + OP_7: 0x57, + OP_8: 0x58, + OP_9: 0x59, + OP_10: 0x5a, + OP_11: 0x5b, + OP_12: 0x5c, + OP_13: 0x5d, + OP_14: 0x5e, + OP_15: 0x5f, + OP_16: 0x60, + + OP_NOP: 0x61, + OP_VER: 0x62, + OP_IF: 0x63, + OP_NOTIF: 0x64, + OP_VERIF: 0x65, + OP_VERNOTIF: 0x66, + OP_ELSE: 0x67, + OP_ENDIF: 0x68, + OP_VERIFY: 0x69, + OP_RETURN: 0x6a, + + OP_TOALTSTACK: 0x6b, + OP_FROMALTSTACK: 0x6c, + OP_2DROP: 0x6d, + OP_2DUP: 0x6e, + OP_3DUP: 0x6f, + OP_2OVER: 0x70, + OP_2ROT: 0x71, + OP_2SWAP: 0x72, + OP_IFDUP: 0x73, + OP_DEPTH: 0x74, + OP_DROP: 0x75, + OP_DUP: 0x76, + OP_NIP: 0x77, + OP_OVER: 0x78, + OP_PICK: 0x79, + OP_ROLL: 0x7a, + OP_ROT: 0x7b, + OP_SWAP: 0x7c, + OP_TUCK: 0x7d, + + OP_CAT: 0x7e, + OP_SUBSTR: 0x7f, + OP_LEFT: 0x80, + OP_RIGHT: 0x81, + OP_SIZE: 0x82, + + OP_INVERT: 0x83, + OP_AND: 0x84, + OP_OR: 0x85, + OP_XOR: 0x86, + OP_EQUAL: 0x87, + OP_EQUALVERIFY: 0x88, + + OP_RESERVED1: 0x89, + OP_RESERVED2: 0x8a, + + OP_1ADD: 0x8b, + OP_1SUB: 0x8c, + OP_2MUL: 0x8d, + OP_2DIV: 0x8e, + OP_NEGATE: 0x8f, + OP_ABS: 0x90, + OP_NOT: 0x91, + OP_0NOTEQUAL: 0x92, + OP_ADD: 0x93, + OP_SUB: 0x94, + OP_MUL: 0x95, + OP_DIV: 0x96, + OP_MOD: 0x97, + OP_LSHIFT: 0x98, + OP_RSHIFT: 0x99, + OP_BOOLAND: 0x9a, + OP_BOOLOR: 0x9b, + OP_NUMEQUAL: 0x9c, + OP_NUMEQUALVERIFY: 0x9d, + OP_NUMNOTEQUAL: 0x9e, + OP_LESSTHAN: 0x9f, + OP_GREATERTHAN: 0xa0, + OP_LESSTHANOREQUAL: 0xa1, + OP_GREATERTHANOREQUAL: 0xa2, + OP_MIN: 0xa3, + OP_MAX: 0xa4, + OP_WITHIN: 0xa5, + + OP_RIPEMD160: 0xa6, + OP_SHA1: 0xa7, + OP_SHA256: 0xa8, + OP_HASH160: 0xa9, + OP_HASH256: 0xaa, + OP_CODESEPARATOR: 0xab, + OP_CHECKSIG: 0xac, + OP_CHECKSIGVERIFY: 0xad, + OP_CHECKMULTISIG: 0xae, + OP_CHECKMULTISIGVERIFY: 0xaf, + + OP_EVAL: 0xb0, + OP_NOP1: 0xb0, + OP_NOP2: 0xb1, + OP_CHECKLOCKTIMEVERIFY: 0xb1, + OP_NOP3: 0xb2, + OP_CHECKSEQUENCEVERIFY: 0xb2, + OP_NOP4: 0xb3, + OP_NOP5: 0xb4, + OP_NOP6: 0xb5, + OP_NOP7: 0xb6, + OP_NOP8: 0xb7, + OP_NOP9: 0xb8, + OP_NOP10: 0xb9, + + OP_PUBKEYHASH: 0xfd, + OP_PUBKEY: 0xfe, + OP_INVALIDOPCODE: 0xff +}; + +/** + * Opcodes by value. + * @const {RevMap} + */ + +exports.opcodesByVal = util.revMap(exports.opcodes); + +/** + * Script and locktime flags. See {@link VerifyFlags}. + * @enum {Number} + */ + +exports.flags = { + VERIFY_NONE: 0, + VERIFY_P2SH: 1 << 0, + VERIFY_STRICTENC: 1 << 1, + VERIFY_DERSIG: 1 << 2, + VERIFY_LOW_S: 1 << 3, + VERIFY_NULLDUMMY: 1 << 4, + VERIFY_SIGPUSHONLY: 1 << 5, + VERIFY_MINIMALDATA: 1 << 6, + VERIFY_DISCOURAGE_UPGRADABLE_NOPS: 1 << 7, + VERIFY_CLEANSTACK: 1 << 8, + VERIFY_CHECKLOCKTIMEVERIFY: 1 << 9, + VERIFY_CHECKSEQUENCEVERIFY: 1 << 10, + VERIFY_WITNESS: 1 << 11, + VERIFY_DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM: 1 << 12, + VERIFY_MINIMALIF: 1 << 13, + VERIFY_NULLFAIL: 1 << 14, + VERIFY_WITNESS_PUBKEYTYPE: 1 << 15, + VERIFY_MAST: 1 << 16 +}; + +/** + * Consensus verify flags (used for block validation). + * @const {VerifyFlags} + * @default + */ + +exports.flags.MANDATORY_VERIFY_FLAGS = exports.flags.VERIFY_P2SH; + +/** + * Standard verify flags (used for mempool validation). + * @const {VerifyFlags} + * @default + */ + +exports.flags.STANDARD_VERIFY_FLAGS = 0 + | exports.flags.MANDATORY_VERIFY_FLAGS + | exports.flags.VERIFY_DERSIG + | exports.flags.VERIFY_STRICTENC + | exports.flags.VERIFY_MINIMALDATA + | exports.flags.VERIFY_NULLDUMMY + | exports.flags.VERIFY_DISCOURAGE_UPGRADABLE_NOPS + | exports.flags.VERIFY_CLEANSTACK + | exports.flags.VERIFY_MINIMALIF + | exports.flags.VERIFY_NULLFAIL + | exports.flags.VERIFY_CHECKLOCKTIMEVERIFY + | exports.flags.VERIFY_CHECKSEQUENCEVERIFY + | exports.flags.VERIFY_LOW_S + | exports.flags.VERIFY_WITNESS + | exports.flags.VERIFY_DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM + | exports.flags.VERIFY_WITNESS_PUBKEYTYPE; + +/** + * Standard flags without mandatory bits. + * @const {VerifyFlags} + * @default + */ + +exports.flags.UNSTANDARD_VERIFY_FLAGS = + exports.flags.STANDARD_VERIFY_FLAGS & ~exports.flags.MANDATORY_VERIFY_FLAGS; + +/** + * Sighash Types. + * @enum {SighashType} + * @default + */ + +exports.hashType = { + /* + * Sign all outputs. + */ + + ALL: 1, + + /* + * Do not sign outputs (zero sequences). + */ + + NONE: 2, + + /* + * Sign output at the same index (zero sequences). + */ + + SINGLE: 3, + + /* + * Sign only the current input (mask). + */ + + ANYONECANPAY: 0x80 +}; + +/** + * Sighash types by value. + * @const {RevMap} + */ + +exports.hashTypeByVal = util.revMap(exports.hashType); + +/** + * Output script types. + * @enum {Number} + */ + +exports.types = { + NONSTANDARD: 0, + PUBKEY: 1, + PUBKEYHASH: 2, + SCRIPTHASH: 3, + MULTISIG: 4, + NULLDATA: 5, + WITNESSMALFORMED: 0x80 | 0, + WITNESSSCRIPTHASH: 0x80 | 1, + WITNESSPUBKEYHASH: 0x80 | 2, + WITNESSMASTHASH: 0x80 | 3 +}; + +/** + * Output script types by value. + * @const {RevMap} + */ + +exports.typesByVal = util.revMap(exports.types); + +/** + * False stack return value. + * @const {Buffer} + */ + +exports.STACK_FALSE = new Buffer([]); + +/** + * True stack return value. + * @const {Buffer} + */ + +exports.STACK_TRUE = new Buffer([0x01]); + +/** + * -1 stack return value. + * @const {Buffer} + */ + +exports.STACK_NEGATE = new Buffer([0x81]); /** * Test whether the data element is a ripemd160 hash. @@ -228,7 +523,7 @@ exports.formatCode = function formatCode(code) { while (size.length % 2 !== 0) size = '0' + size; - if (!constants.opcodesByVal[value]) { + if (!exports.opcodesByVal[value]) { value = value.toString(16); if (value.length < 2) value = '0' + value; @@ -237,7 +532,7 @@ exports.formatCode = function formatCode(code) { continue; } - value = constants.opcodesByVal[value]; + value = exports.opcodesByVal[value]; value = value + ' 0x' + size + ' 0x' + data.toString('hex'); out.push(value); continue; @@ -245,8 +540,8 @@ exports.formatCode = function formatCode(code) { assert(typeof value === 'number'); - if (constants.opcodesByVal[value]) { - value = constants.opcodesByVal[value]; + if (exports.opcodesByVal[value]) { + value = exports.opcodesByVal[value]; out.push(value); continue; } @@ -279,7 +574,7 @@ exports.formatItem = function formatItem(data, decode) { var symbol, type; if (data.length <= 4) { - data = exports.num(data, constants.flags.VERIFY_NONE); + data = exports.num(data, exports.flags.VERIFY_NONE); return data.toString(10); } @@ -287,9 +582,9 @@ exports.formatItem = function formatItem(data, decode) { symbol = ''; if (exports.isSignatureEncoding(data)) { type = data[data.length - 1]; - symbol = constants.hashTypeByVal[type & 0x1f] || ''; + symbol = exports.hashTypeByVal[type & 0x1f] || ''; if (symbol) { - if (type & constants.hashType.ANYONECANPAY) + if (type & exports.hashType.ANYONECANPAY) symbol += '|ANYONECANPAY'; symbol = '[' + symbol + ']'; } @@ -312,7 +607,7 @@ exports.formatASM = function formatASM(code, decode) { var out = []; var i, op, data, value; - if (code.length > 0 && code[0].value === opcodes.OP_RETURN) + if (code.length > 0 && code[0].value === exports.opcodes.OP_RETURN) decode = false; for (i = 0; i < code.length; i++) { @@ -331,7 +626,7 @@ exports.formatASM = function formatASM(code, decode) { continue; } - value = constants.opcodesByVal[value] || 'OP_UNKNOWN'; + value = exports.opcodesByVal[value] || 'OP_UNKNOWN'; out.push(value); } @@ -374,15 +669,15 @@ exports.num = function num(value, flags, size) { assert(Buffer.isBuffer(value)); if (flags == null) - flags = constants.flags.STANDARD_VERIFY_FLAGS; + flags = exports.flags.STANDARD_VERIFY_FLAGS; if (size == null) size = 4; if (value.length > size) - throw new ScriptError('UNKNOWN_ERROR', 'Script number overflow.'); + throw new exports.ScriptError('UNKNOWN_ERROR', 'Script number overflow.'); - if ((flags & constants.flags.VERIFY_MINIMALDATA) && value.length > 0) { + if ((flags & exports.flags.VERIFY_MINIMALDATA) && value.length > 0) { // If the low bits on the last byte are unset, // fail if the value's second to last byte does // not have the high bit set. A number can't @@ -393,7 +688,7 @@ exports.num = function num(value, flags, size) { // zero). if (!(value[value.length - 1] & 0x7f)) { if (value.length === 1 || !(value[value.length - 2] & 0x80)) { - throw new ScriptError( + throw new exports.ScriptError( 'UNKNOWN_ERROR', 'Non-minimally encoded Script number.'); } @@ -438,7 +733,7 @@ exports.array = function(value) { assert(BN.isBN(value)); if (value.cmpn(0) === 0) - return STACK_FALSE; + return exports.STACK_FALSE; // If the most significant byte is >= 0x80 // and the value is positive, push a new @@ -466,3 +761,53 @@ exports.array = function(value) { return new Buffer(result); }; + +/** + * An error thrown from the scripting system, + * potentially pertaining to Script execution. + * @exports ScriptError + * @constructor + * @extends Error + * @param {String} code - Error code. + * @param {(Number|String)?} op - Opcode. + * @param {Number?} ip - Instruction pointer. + * @property {String} message - Error message. + * @property {String} code - Original code passed in. + * @property {String?} op - Symbolic opcode. + * @property {Number?} ip - Instruction pointer. + */ + +exports.ScriptError = function ScriptError(code, op, ip) { + Error.call(this); + + if (Error.captureStackTrace) + Error.captureStackTrace(this, ScriptError); + + this.type = 'ScriptError'; + this.code = code; + + if (typeof op !== 'string') { + if (op || ip != null) { + code += ' ('; + if (op) { + op = exports.opcodesByVal[op] || op; + code += 'op=' + op; + if (ip != null) + code += ', '; + } + if (ip != null) + code += 'ip=' + ip; + code += ')'; + } + + this.message = code; + this.op = op || ''; + this.ip = ip != null ? ip : -1; + } else { + this.message = op; + this.op = ''; + this.ip = -1; + } +}; + +util.inherits(exports.ScriptError, Error); diff --git a/lib/script/opcode.js b/lib/script/opcode.js index 2e568fcd..8d8f3034 100644 --- a/lib/script/opcode.js +++ b/lib/script/opcode.js @@ -9,12 +9,11 @@ var assert = require('assert'); var BN = require('bn.js'); -var constants = require('../protocol/constants'); var util = require('../utils/util'); var encoding = require('./encoding'); var BufferReader = require('../utils/reader'); var StaticWriter = require('../utils/staticwriter'); -var opcodes = constants.opcodes; +var opcodes = encoding.opcodes; /** * A simple struct which contains @@ -347,7 +346,7 @@ Opcode.fromSymbol = function fromSymbol(name) { if (!util.startsWith(name, 'OP_')) name = 'OP_' + name; - op = constants.opcodes[name]; + op = encoding.opcodes[name]; assert(op != null, 'Unknown opcode.'); return Opcode.fromOp(op); diff --git a/lib/script/program.js b/lib/script/program.js index 9ed9a208..1f87f4d2 100644 --- a/lib/script/program.js +++ b/lib/script/program.js @@ -7,10 +7,10 @@ 'use strict'; -var constants = require('../protocol/constants'); -var util = require('../utils/util'); var assert = require('assert'); -var scriptTypes = constants.scriptTypes; +var util = require('../utils/util'); +var encoding = require('./encoding'); +var scriptTypes = encoding.types; /** * Witness Program @@ -96,7 +96,7 @@ Program.prototype.inspect = function inspect() { return ''; }; diff --git a/lib/script/script.js b/lib/script/script.js index 7d20a2d2..1b243f2d 100644 --- a/lib/script/script.js +++ b/lib/script/script.js @@ -7,20 +7,15 @@ 'use strict'; +var assert = require('assert'); var BN = require('bn.js'); -var constants = require('../protocol/constants'); +var consensus = require('../protocol/consensus'); +var policy = require('../protocol/policy'); var util = require('../utils/util'); var crypto = require('../crypto/crypto'); -var assert = require('assert'); var BufferWriter = require('../utils/writer'); var BufferReader = require('../utils/reader'); var StaticWriter = require('../utils/staticwriter'); -var opcodes = constants.opcodes; -var STACK_TRUE = new Buffer([1]); -var STACK_FALSE = new Buffer(0); -var STACK_NEGATE = new Buffer([0x81]); -var ScriptError = require('../btc/errors').ScriptError; -var scriptTypes = constants.scriptTypes; var Program = require('./program'); var Opcode = require('./opcode'); var Stack = require('./stack'); @@ -29,6 +24,12 @@ var encoding = require('./encoding'); var enc = require('../utils/encoding'); var ec = require('../crypto/ec'); var Address = require('../primitives/address'); +var opcodes = encoding.opcodes; +var scriptTypes = encoding.types; +var ScriptError = encoding.ScriptError; +var STACK_TRUE = encoding.STACK_TRUE; +var STACK_FALSE = encoding.STACK_FALSE; +var STACK_NEGATE = encoding.STACK_NEGATE; /** * Represents a input or output script. @@ -53,10 +54,77 @@ function Script(options) { this.fromOptions(options); } +/** + * Script opcodes. + * @enum {Number} + * @default + */ + +Script.opcodes = encoding.opcodes; + +/** + * Opcodes by value. + * @const {RevMap} + */ + +Script.opcodesByVal = encoding.opcodesByVal; + +/** + * Script and locktime flags. See {@link VerifyFlags}. + * @enum {Number} + */ + +Script.flags = encoding.flags; + +/** + * Sighash Types. + * @enum {SighashType} + * @default + */ + +Script.hashType = encoding.hashType; + +/** + * Sighash types by value. + * @const {RevMap} + */ + +Script.hashTypeByVal = encoding.hashTypeByVal; + +/** + * Output script types. + * @enum {Number} + */ + +Script.types = encoding.types; + +/** + * Output script types by value. + * @const {RevMap} + */ + +Script.typesByVal = encoding.typesByVal; + +/** + * Getter to retrieve code length. + * @function + * @name length(get) + * @memberof Script# + * @returns {Number} + */ + Script.prototype.__defineGetter__('length', function() { return this.code.length; }); +/** + * Setter to set code length. + * @function + * @name length(set) + * @memberof Script# + * @returns {Number} + */ + Script.prototype.__defineSetter__('length', function(length) { return this.code.length = length; }); @@ -234,7 +302,7 @@ Script.prototype.inspect = function inspect() { */ Script.prototype.toString = function toString() { - return Script.format(this.code); + return encoding.formatCode(this.code); }; /** @@ -244,7 +312,7 @@ Script.prototype.toString = function toString() { */ Script.prototype.toASM = function toASM(decode) { - return Script.formatASM(this.code, decode); + return encoding.formatASM(this.code, decode); }; /** @@ -410,9 +478,9 @@ Script.prototype.execute = function execute(stack, flags, tx, index, value, vers var i, j, res, locktime; if (flags == null) - flags = constants.flags.STANDARD_VERIFY_FLAGS; + flags = Script.flags.STANDARD_VERIFY_FLAGS; - if (this.getSize() > constants.script.MAX_SIZE) + if (this.getSize() > consensus.MAX_SCRIPT_SIZE) throw new ScriptError('SCRIPT_SIZE'); for (ip = 0; ip < this.code.length; ip++) { @@ -424,7 +492,7 @@ Script.prototype.execute = function execute(stack, flags, tx, index, value, vers throw new ScriptError('BAD_OPCODE', op, ip); if (data) { - if (data.length > constants.script.MAX_PUSH) + if (data.length > consensus.MAX_SCRIPT_PUSH) throw new ScriptError('PUSH_SIZE', op, ip); // Note that minimaldata is not checked @@ -438,7 +506,7 @@ Script.prototype.execute = function execute(stack, flags, tx, index, value, vers continue; } - if (op > opcodes.OP_16 && ++opCount > constants.script.MAX_OPS) + if (op > opcodes.OP_16 && ++opCount > consensus.MAX_SCRIPT_OPS) throw new ScriptError('OP_COUNT', op, ip); // It's very important to make a distiction @@ -477,7 +545,7 @@ Script.prototype.execute = function execute(stack, flags, tx, index, value, vers val = stack.top(-1); - if (version === 1 && (flags & constants.flags.VERIFY_MINIMALIF)) { + if (version === 1 && (flags & Script.flags.VERIFY_MINIMALIF)) { if (val.length > 1) throw new ScriptError('MINIMALIF'); @@ -569,8 +637,8 @@ Script.prototype.execute = function execute(stack, flags, tx, index, value, vers } case opcodes.OP_CHECKLOCKTIMEVERIFY: { // OP_CHECKLOCKTIMEVERIFY = OP_NOP2 - if (!(flags & constants.flags.VERIFY_CHECKLOCKTIMEVERIFY)) { - if (flags & constants.flags.VERIFY_DISCOURAGE_UPGRADABLE_NOPS) + if (!(flags & Script.flags.VERIFY_CHECKLOCKTIMEVERIFY)) { + if (flags & Script.flags.VERIFY_DISCOURAGE_UPGRADABLE_NOPS) throw new ScriptError('DISCOURAGE_UPGRADABLE_NOPS', op, ip); break; } @@ -588,15 +656,15 @@ Script.prototype.execute = function execute(stack, flags, tx, index, value, vers locktime = locktime.toNumber(); - if (!Script.checkLocktime(locktime, tx, index)) + if (!tx.verifyLocktime(index, locktime)) throw new ScriptError('UNSATISFIED_LOCKTIME', op, ip); break; } case opcodes.OP_CHECKSEQUENCEVERIFY: { // OP_CHECKSEQUENCEVERIFY = OP_NOP3 - if (!(flags & constants.flags.VERIFY_CHECKSEQUENCEVERIFY)) { - if (flags & constants.flags.VERIFY_DISCOURAGE_UPGRADABLE_NOPS) + if (!(flags & Script.flags.VERIFY_CHECKSEQUENCEVERIFY)) { + if (flags & Script.flags.VERIFY_DISCOURAGE_UPGRADABLE_NOPS) throw new ScriptError('DISCOURAGE_UPGRADABLE_NOPS', op, ip); break; } @@ -614,10 +682,7 @@ Script.prototype.execute = function execute(stack, flags, tx, index, value, vers locktime = locktime.toNumber(); - if ((locktime & constants.sequence.DISABLE_FLAG) !== 0) - break; - - if (!Script.checkSequence(locktime, tx, index)) + if (!tx.verifySequence(index, locktime)) throw new ScriptError('UNSATISFIED_LOCKTIME', op, ip); break; @@ -630,7 +695,7 @@ Script.prototype.execute = function execute(stack, flags, tx, index, value, vers case opcodes.OP_NOP8: case opcodes.OP_NOP9: case opcodes.OP_NOP10: { - if (flags & constants.flags.VERIFY_DISCOURAGE_UPGRADABLE_NOPS) + if (flags & Script.flags.VERIFY_DISCOURAGE_UPGRADABLE_NOPS) throw new ScriptError('DISCOURAGE_UPGRADABLE_NOPS', op, ip); break; } @@ -1086,7 +1151,7 @@ Script.prototype.execute = function execute(stack, flags, tx, index, value, vers res = Script.checksig(hash, sig, key, flags); } - if (!res && (flags & constants.flags.VERIFY_NULLFAIL)) { + if (!res && (flags & Script.flags.VERIFY_NULLFAIL)) { if (sig.length !== 0) throw new ScriptError('NULLFAIL', op, ip); } @@ -1116,12 +1181,12 @@ Script.prototype.execute = function execute(stack, flags, tx, index, value, vers n = Script.num(stack.top(-i), flags).toNumber(); ikey2 = n + 2; - if (!(n >= 0 && n <= constants.script.MAX_MULTISIG_PUBKEYS)) + if (!(n >= 0 && n <= consensus.MAX_MULTISIG_PUBKEYS)) throw new ScriptError('PUBKEY_COUNT', op, ip); opCount += n; - if (opCount > constants.script.MAX_OPS) + if (opCount > consensus.MAX_SCRIPT_OPS) throw new ScriptError('OP_COUNT', op, ip); i++; @@ -1177,7 +1242,7 @@ Script.prototype.execute = function execute(stack, flags, tx, index, value, vers } while (i-- > 1) { - if (!res && (flags & constants.flags.VERIFY_NULLFAIL)) { + if (!res && (flags & Script.flags.VERIFY_NULLFAIL)) { if (ikey2 === 0 && stack.top(-1).length !== 0) throw new ScriptError('NULLFAIL', op, ip); } @@ -1189,7 +1254,7 @@ Script.prototype.execute = function execute(stack, flags, tx, index, value, vers if (stack.length < 1) throw new ScriptError('INVALID_STACK_OPERATION', op, ip); - if (flags & constants.flags.VERIFY_NULLDUMMY) { + if (flags & Script.flags.VERIFY_NULLDUMMY) { if (!Script.isDummy(stack.top(-1))) throw new ScriptError('SIG_NULLDUMMY', op, ip); } @@ -1324,7 +1389,7 @@ Script.prototype.execute = function execute(stack, flags, tx, index, value, vers } } - if (stack.length + alt.length > constants.script.MAX_STACK) + if (stack.length + alt.length > consensus.MAX_SCRIPT_STACK) throw new ScriptError('STACK_SIZE', op, ip); if (state.length !== 0) @@ -1333,71 +1398,6 @@ Script.prototype.execute = function execute(stack, flags, tx, index, value, vers return true; }; -/** - * Verify the nLockTime of a transaction. - * @param {Number} locktime - Locktime to verify against (max=u32). - * @param {TX} tx - Transaction to verify. - * @param {Number} index - Index of input being verified (for IsFinal). - * @returns {Boolean} - */ - -Script.checkLocktime = function checkLocktime(locktime, tx, i) { - var threshold = constants.LOCKTIME_THRESHOLD; - - if (!( - (tx.locktime < threshold && locktime < threshold) - || (tx.locktime >= threshold && locktime >= threshold) - )) { - return false; - } - - if (locktime > tx.locktime) - return false; - - if (tx.inputs[i].sequence === 0xffffffff) - return false; - - return true; -}; - -/** - * Verify the nSequence locktime of a transaction. - * @param {Number} sequence - Locktime to verify against (max=u32). - * @param {TX} tx - Transaction to verify. - * @param {Number} index - Index of input being verified. - * @returns {Boolean} - */ - -Script.checkSequence = function checkSequence(sequence, tx, i) { - var txSequence = tx.inputs[i].sequence; - var locktimeMask, txSequenceMasked, sequenceMasked; - - if (tx.version < 2) - return false; - - if (txSequence & constants.sequence.DISABLE_FLAG) - return false; - - locktimeMask = constants.sequence.TYPE_FLAG - | constants.sequence.MASK; - txSequenceMasked = txSequence & locktimeMask; - sequenceMasked = sequence & locktimeMask; - - if (!( - (txSequenceMasked < constants.sequence.TYPE_FLAG - && sequenceMasked < constants.sequence.TYPE_FLAG) - || (txSequenceMasked >= constants.sequence.TYPE_FLAG - && sequenceMasked >= constants.sequence.TYPE_FLAG) - )) { - return false; - } - - if (sequenceMasked > txSequenceMasked) - return false; - - return true; -}; - /** * Cast a big number or Buffer to a bool. * @see CastToBool @@ -1542,9 +1542,9 @@ Script.prototype.indexOf = function indexOf(data) { Script.isMinimal = function isMinimal(data, opcode, flags) { if (flags == null) - flags = constants.flags.STANDARD_VERIFY_FLAGS; + flags = Script.flags.STANDARD_VERIFY_FLAGS; - if (!(flags & constants.flags.VERIFY_MINIMALDATA)) + if (!(flags & Script.flags.VERIFY_MINIMALDATA)) return true; if (!data) @@ -1754,7 +1754,7 @@ Script.fromScripthash = function fromScripthash(hash) { Script.prototype.fromNulldata = function fromNulldata(flags) { assert(Buffer.isBuffer(flags)); - assert(flags.length <= constants.script.MAX_OP_RETURN, 'Nulldata too large.'); + assert(flags.length <= policy.MAX_OP_RETURN, 'Nulldata too large.'); this.push(opcodes.OP_RETURN); this.push(flags); this.compile(); @@ -1953,7 +1953,7 @@ Script.prototype.isStandard = function isStandard() { return true; case scriptTypes.NULLDATA: - if (this.raw.length > constants.script.MAX_OP_RETURN_BYTES) + if (this.raw.length > policy.MAX_OP_RETURN_BYTES) return false; return true; default: @@ -2146,7 +2146,7 @@ Script.prototype.isNulldata = function isNulldata(minimal) { return true; if (minimal) { - if (this.raw.length > constants.script.MAX_OP_RETURN_BYTES) + if (this.raw.length > policy.MAX_OP_RETURN_BYTES) return false; if (this.raw.length === 2) @@ -2306,7 +2306,7 @@ Script.prototype.isWitnessMasthash = function isWitnessMasthash() { */ Script.prototype.isUnspendable = function isUnspendable() { - if (this.raw.length > constants.script.MAX_SIZE) + if (this.raw.length > consensus.MAX_SCRIPT_SIZE) return true; return this.raw.length > 0 && this.raw[0] === opcodes.OP_RETURN; @@ -2478,7 +2478,7 @@ Script.prototype.getCoinbaseHeight = function getCoinbaseHeight() { */ Script.getCoinbaseHeight = function getCoinbaseHeight(raw) { - var flags = constants.flags.STANDARD_VERIFY_FLAGS; + var flags = Script.flags.STANDARD_VERIFY_FLAGS; var data, height, op; if (raw.length === 0) @@ -2694,7 +2694,7 @@ Script.prototype.getNumber = function getNumber(i) { if (!op || !op.data || op.data.length > 5) return; - return Script.num(op.data, constants.flags.VERIFY_NONE, 5); + return Script.num(op.data, Script.flags.VERIFY_NONE, 5); }; /** @@ -2781,17 +2781,17 @@ Script.isDummy = function isDummy(data) { Script.validateKey = function validateKey(key, flags, version) { if (flags == null) - flags = constants.flags.STANDARD_VERIFY_FLAGS; + flags = Script.flags.STANDARD_VERIFY_FLAGS; assert(Buffer.isBuffer(key)); - if (flags & constants.flags.VERIFY_STRICTENC) { + if (flags & Script.flags.VERIFY_STRICTENC) { if (!Script.isKeyEncoding(key)) throw new ScriptError('PUBKEYTYPE'); } if (version === 1) { - if (flags & constants.flags.VERIFY_WITNESS_PUBKEYTYPE) { + if (flags & Script.flags.VERIFY_WITNESS_PUBKEYTYPE) { if (!Script.isCompressedEncoding(key)) throw new ScriptError('WITNESS_PUBKEYTYPE'); } @@ -2834,7 +2834,7 @@ Script.isKeyEncoding = function isKeyEncoding(key) { Script.validateSignature = function validateSignature(sig, flags) { if (flags == null) - flags = constants.flags.STANDARD_VERIFY_FLAGS; + flags = Script.flags.STANDARD_VERIFY_FLAGS; assert(Buffer.isBuffer(sig)); @@ -2842,19 +2842,19 @@ Script.validateSignature = function validateSignature(sig, flags) { if (sig.length === 0) return true; - if ((flags & constants.flags.VERIFY_DERSIG) - || (flags & constants.flags.VERIFY_LOW_S) - || (flags & constants.flags.VERIFY_STRICTENC)) { + if ((flags & Script.flags.VERIFY_DERSIG) + || (flags & Script.flags.VERIFY_LOW_S) + || (flags & Script.flags.VERIFY_STRICTENC)) { if (!Script.isSignatureEncoding(sig)) throw new ScriptError('SIG_DER'); } - if (flags & constants.flags.VERIFY_LOW_S) { + if (flags & Script.flags.VERIFY_LOW_S) { if (!Script.isLowDER(sig)) throw new ScriptError('SIG_HIGH_S'); } - if (flags & constants.flags.VERIFY_STRICTENC) { + if (flags & Script.flags.VERIFY_STRICTENC) { if (!Script.isHashType(sig)) throw new ScriptError('SIG_HASHTYPE'); } @@ -2887,9 +2887,9 @@ Script.isHashType = function isHashType(sig) { if (sig.length === 0) return false; - type = sig[sig.length - 1] & ~constants.hashType.ANYONECANPAY; + type = sig[sig.length - 1] & ~Script.hashType.ANYONECANPAY; - if (!(type >= constants.hashType.ALL && type <= constants.hashType.SINGLE)) + if (!(type >= Script.hashType.ALL && type <= Script.hashType.SINGLE)) return false; return true; @@ -2908,27 +2908,6 @@ Script.isLowDER = function isLowDER(sig) { return ec.isLowS(sig.slice(0, -1)); }; -/** - * Format script code into a human readable-string. - * @param {Array} code - * @returns {String} Human-readable string. - */ - -Script.format = function format(code) { - return encoding.formatCode(code); -}; - -/** - * Format script code into bitcoind asm format. - * @param {Array} code - * @param {Boolean?} decode - Attempt to decode hash types. - * @returns {String} Human-readable string. - */ - -Script.formatASM = function formatASM(code, decode) { - return encoding.formatASM(code, decode); -}; - /** * Test the script to see if it contains only push ops. * Push ops are: OP_1NEGATE, OP_0-OP_16 and all PUSHDATAs. @@ -2985,7 +2964,7 @@ Script.prototype.getSigops = function getSigops(accurate) { if (accurate && lastOp >= opcodes.OP_1 && lastOp <= opcodes.OP_16) total += lastOp - 0x50; else - total += constants.script.MAX_MULTISIG_PUBKEYS; + total += consensus.MAX_MULTISIG_PUBKEYS; break; } @@ -3043,7 +3022,7 @@ Script.witnessSigops = function witnessSigops(program, witness, flags) { var redeem; if (flags == null) - flags = constants.flags.STANDARD_VERIFY_FLAGS; + flags = Script.flags.STANDARD_VERIFY_FLAGS; if (program.version === 0) { if (program.data.length === 20) @@ -3071,12 +3050,12 @@ Script.getWitnessSigops = function getWitnessSigops(input, output, witness, flag var redeem; if (flags == null) - flags = constants.flags.STANDARD_VERIFY_FLAGS; + flags = Script.flags.STANDARD_VERIFY_FLAGS; - if ((flags & constants.flags.VERIFY_WITNESS) === 0) + if ((flags & Script.flags.VERIFY_WITNESS) === 0) return 0; - assert((flags & constants.flags.VERIFY_P2SH) !== 0); + assert((flags & Script.flags.VERIFY_P2SH) !== 0); if (output.isProgram()) return Script.witnessSigops(output.toProgram(), witness, flags); @@ -3203,9 +3182,9 @@ Script.verify = function verify(input, witness, output, tx, i, value, flags) { var stack, copy, raw, redeem, hadWitness; if (flags == null) - flags = constants.flags.STANDARD_VERIFY_FLAGS; + flags = Script.flags.STANDARD_VERIFY_FLAGS; - if (flags & constants.flags.VERIFY_SIGPUSHONLY) { + if (flags & Script.flags.VERIFY_SIGPUSHONLY) { if (!input.isPushOnly()) throw new ScriptError('SIG_PUSHONLY'); } @@ -3217,7 +3196,7 @@ Script.verify = function verify(input, witness, output, tx, i, value, flags) { input.execute(stack, flags, tx, i, value, 0); // Copy the stack for P2SH - if (flags & constants.flags.VERIFY_P2SH) + if (flags & Script.flags.VERIFY_P2SH) copy = stack.clone(); // Execute the previous output script. @@ -3227,7 +3206,7 @@ Script.verify = function verify(input, witness, output, tx, i, value, flags) { if (stack.length === 0 || !Script.bool(stack.top(-1))) throw new ScriptError('EVAL_FALSE'); - if ((flags & constants.flags.VERIFY_WITNESS) && output.isProgram()) { + if ((flags & Script.flags.VERIFY_WITNESS) && output.isProgram()) { hadWitness = true; // Input script must be empty. @@ -3242,7 +3221,7 @@ Script.verify = function verify(input, witness, output, tx, i, value, flags) { } // If the script is P2SH, execute the real output script - if ((flags & constants.flags.VERIFY_P2SH) && output.isScripthash()) { + if ((flags & Script.flags.VERIFY_P2SH) && output.isScripthash()) { // P2SH can only have push ops in the scriptSig if (!input.isPushOnly()) throw new ScriptError('SIG_PUSHONLY'); @@ -3265,7 +3244,7 @@ Script.verify = function verify(input, witness, output, tx, i, value, flags) { if (stack.length === 0 || !Script.bool(stack.top(-1))) throw new ScriptError('EVAL_FALSE'); - if ((flags & constants.flags.VERIFY_WITNESS) && redeem.isProgram()) { + if ((flags & Script.flags.VERIFY_WITNESS) && redeem.isProgram()) { hadWitness = true; // Input script must be exactly one push of the redeem script. @@ -3281,15 +3260,15 @@ Script.verify = function verify(input, witness, output, tx, i, value, flags) { } // Ensure there is nothing left on the stack. - if (flags & constants.flags.VERIFY_CLEANSTACK) { - assert((flags & constants.flags.VERIFY_P2SH) !== 0); + if (flags & Script.flags.VERIFY_CLEANSTACK) { + assert((flags & Script.flags.VERIFY_P2SH) !== 0); if (stack.length !== 1) throw new ScriptError('CLEANSTACK'); } // If we had a witness but no witness program, fail. - if (flags & constants.flags.VERIFY_WITNESS) { - assert((flags & constants.flags.VERIFY_P2SH) !== 0); + if (flags & Script.flags.VERIFY_WITNESS) { + assert((flags & Script.flags.VERIFY_P2SH) !== 0); if (!hadWitness && witness.items.length > 0) throw new ScriptError('WITNESS_UNEXPECTED'); } @@ -3316,7 +3295,7 @@ Script.verifyProgram = function verifyProgram(witness, output, flags, tx, i, val var j, witnessScript, redeem; assert(program, 'verifyProgram called on non-witness-program.'); - assert((flags & constants.flags.VERIFY_WITNESS) !== 0); + assert((flags & Script.flags.VERIFY_WITNESS) !== 0); if (program.version === 0) { if (program.data.length === 32) { @@ -3338,7 +3317,7 @@ Script.verifyProgram = function verifyProgram(witness, output, flags, tx, i, val // Failure on version=0 (bad program data length). throw new ScriptError('WITNESS_PROGRAM_WRONG_LENGTH'); } - } else if ((flags & constants.flags.VERIFY_MAST) && program.version === 1) { + } else if ((flags & Script.flags.VERIFY_MAST) && program.version === 1) { return Script.verifyMast(program, stack, output, flags, tx, i); } else { // Anyone can spend (we can return true here @@ -3348,14 +3327,14 @@ Script.verifyProgram = function verifyProgram(witness, output, flags, tx, i, val // we can use the regalar output script which will // succeed in a block, but fail in the mempool // due to VERIFY_CLEANSTACK. - if (flags & constants.flags.VERIFY_DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM) + if (flags & Script.flags.VERIFY_DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM) throw new ScriptError('DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM'); return true; } // Witnesses still have push limits. for (j = 0; j < stack.length; j++) { - if (stack.get(j).length > constants.script.MAX_PUSH) + if (stack.get(j).length > consensus.MAX_SCRIPT_PUSH) throw new ScriptError('PUSH_SIZE'); } @@ -3391,7 +3370,7 @@ Script.verifyMast = function verifyMast(program, stack, output, flags, tx, i, va var j; assert(program.version === 1); - assert((flags & constants.flags.VERIFY_MAST) !== 0); + assert((flags & Script.flags.VERIFY_MAST) !== 0); if (stack.length < 4) throw new ScriptError('INVALID_MAST_STACK'); @@ -3417,7 +3396,7 @@ Script.verifyMast = function verifyMast(program, stack, output, flags, tx, i, va version += 0x100000000; if (version > 0) { - if (flags & constants.flags.DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM) + if (flags & Script.flags.DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM) throw new ScriptError('DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM'); } @@ -3435,7 +3414,7 @@ Script.verifyMast = function verifyMast(program, stack, output, flags, tx, i, va ops += depth; if (version === 0) { - if (ops > constants.script.MAX_OPS) + if (ops > consensus.MAX_SCRIPT_OPS) throw new ScriptError('OP_COUNT'); } @@ -3471,7 +3450,7 @@ Script.verifyMast = function verifyMast(program, stack, output, flags, tx, i, va for (j = 0; j < subscripts; j++) { script = stack.top(-(4 + j)); if (version === 0) { - if ((scripts.written + script.length) > constants.script.MAX_SIZE) + if ((scripts.written + script.length) > consensus.MAX_SCRIPT_SIZE) throw new ScriptError('SCRIPT_SIZE'); } scriptRoot.writeBytes(crypto.hash256(script)); @@ -3491,7 +3470,7 @@ Script.verifyMast = function verifyMast(program, stack, output, flags, tx, i, va stack.length -= 3 + subscripts; for (j = 0; j < stack.length; j++) { - if (stack.get(j).length > constants.script.MAX_PUSH) + if (stack.get(j).length > consensus.MAX_SCRIPT_PUSH) throw new ScriptError('PUSH_SIZE'); } @@ -3523,19 +3502,19 @@ Script.checksig = function checksig(msg, sig, key, flags) { var high = false; if (flags == null) - flags = constants.flags.STANDARD_VERIFY_FLAGS; + flags = Script.flags.STANDARD_VERIFY_FLAGS; // Attempt to normalize the signature // length before passing to elliptic. // Note: We only do this for historical data! // https://github.com/indutny/elliptic/issues/78 - if (!((flags & constants.flags.VERIFY_DERSIG) - || (flags & constants.flags.VERIFY_LOW_S) - || (flags & constants.flags.VERIFY_STRICTENC))) { + if (!((flags & Script.flags.VERIFY_DERSIG) + || (flags & Script.flags.VERIFY_LOW_S) + || (flags & Script.flags.VERIFY_STRICTENC))) { historical = true; } - if (!(flags & constants.flags.VERIFY_LOW_S)) + if (!(flags & Script.flags.VERIFY_LOW_S)) high = true; return sigcache.verify(msg, sig.slice(0, -1), key, historical, high); @@ -3612,26 +3591,6 @@ Script.fromRaw = function fromRaw(data, enc) { return new Script().fromRaw(data); }; -/** - * Calculate the size (including - * the opcode) of a pushdata. - * @param {Number} num - Pushdata data length. - * @returns {Number} size - */ - -Script.sizePush = function sizePush(num) { - if (num <= 0x4b) - return 1; - - if (num <= 0xff) - return 2; - - if (num <= 0xffff) - return 3; - - return 5; -}; - /** * Test whether an object a Script. * @param {Object} obj @@ -3658,12 +3617,4 @@ function sortKeys(keys) { * Expose */ -exports = Script; - -exports.opcodes = constants.opcodes; -exports.opcodesByVal = constants.opcodesByVal; -exports.types = scriptTypes; -exports.typesByVal = constants.scriptTypesByVal; -exports.flags = constants.flags; - -module.exports = exports; +module.exports = Script; diff --git a/lib/script/scriptnum.js b/lib/script/scriptnum.js index 3c3d40b3..6371da82 100644 --- a/lib/script/scriptnum.js +++ b/lib/script/scriptnum.js @@ -7,7 +7,7 @@ 'use strict'; var assert = require('assert'); -var ScriptError = require('../btc/errors').ScriptError; +var ScriptError = require('./common').ScriptError; var EMPTY_ARRAY = new Buffer(0); /** diff --git a/lib/script/witness.js b/lib/script/witness.js index 2f08c912..e159821d 100644 --- a/lib/script/witness.js +++ b/lib/script/witness.js @@ -7,14 +7,9 @@ 'use strict'; -var BN = require('bn.js'); -var constants = require('../protocol/constants'); -var util = require('../utils/util'); var assert = require('assert'); -var opcodes = constants.opcodes; -var STACK_FALSE = new Buffer(0); -var STACK_NEGATE = new Buffer([0x81]); -var scriptTypes = constants.scriptTypes; +var BN = require('bn.js'); +var util = require('../utils/util'); var Script = require('./script'); var encoding = require('./encoding'); var enc = require('../utils/encoding'); @@ -23,6 +18,10 @@ var BufferReader = require('../utils/reader'); var StaticWriter = require('../utils/staticwriter'); var Address = require('../primitives/address'); var Stack = require('./stack'); +var opcodes = encoding.opcodes; +var scriptTypes = encoding.types; +var STACK_FALSE = encoding.STACK_FALSE; +var STACK_NEGATE = encoding.STACK_NEGATE; /** * Refers to the witness field of segregated witness transactions. @@ -45,10 +44,26 @@ function Witness(options) { this.fromOptions(options); } +/** + * Getter to retrieve items length. + * @function + * @name length(get) + * @memberof Witness# + * @returns {Number} + */ + Witness.prototype.__defineGetter__('length', function() { return this.items.length; }); +/** + * Setter to set items length. + * @function + * @name length(set) + * @memberof Witness# + * @returns {Number} + */ + Witness.prototype.__defineSetter__('length', function(length) { return this.items.length = length; }); @@ -485,7 +500,7 @@ Witness.prototype.getNumber = function getNumber(i) { var item = this.items[i]; if (!item || item.length > 5) return; - return encoding.num(item, constants.flags.VERIFY_NONE, 5); + return encoding.num(item, encoding.flags.VERIFY_NONE, 5); }; /** diff --git a/lib/utils/bloom.js b/lib/utils/bloom.js index 7482da51..258f5739 100644 --- a/lib/utils/bloom.js +++ b/lib/utils/bloom.js @@ -8,7 +8,6 @@ 'use strict'; var assert = require('assert'); -var constants = require('../protocol/constants'); var murmur3 = require('./murmur3'); var BufferReader = require('./reader'); var StaticWriter = require('./staticwriter'); diff --git a/lib/utils/encoding.js b/lib/utils/encoding.js index 64cc27ad..eff488f3 100644 --- a/lib/utils/encoding.js +++ b/lib/utils/encoding.js @@ -42,6 +42,156 @@ encoding.MAX_SAFE_INTEGER = 0x1fffffffffffff; encoding.MAX_SAFE_ADDITION = 0xfffffffffffff; +/** + * An empty buffer. + * @const {Buffer} + * @default + */ + +encoding.DUMMY = new Buffer([0]); + +/** + * A hash of all zeroes with a `1` at the + * end (used for the SIGHASH_SINGLE bug). + * @const {Buffer} + * @default + */ + +encoding.ONE_HASH = new Buffer( + '0100000000000000000000000000000000000000000000000000000000000000', + 'hex' +); + +/** + * A hash of all zeroes. + * @const {Buffer} + * @default + */ + +encoding.ZERO_HASH = new Buffer( + '0000000000000000000000000000000000000000000000000000000000000000', + 'hex' +); + +/** + * A hash of all 0xff. + * @const {Buffer} + * @default + */ + +encoding.MAX_HASH = new Buffer( + 'ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff', + 'hex' +); + +/** + * A hash of all zeroes. + * @const {String} + * @default + */ + +encoding.NULL_HASH = + '0000000000000000000000000000000000000000000000000000000000000000'; + +/** + * A hash of all 0xff. + * @const {String} + * @default + */ + +encoding.HIGH_HASH = + 'ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff'; + +/** + * A hash of all zeroes. + * @const {Buffer} + * @default + */ + +encoding.ZERO_HASH160 = new Buffer( + '0000000000000000000000000000000000000000', + 'hex' +); + +/** + * A hash of all 0xff. + * @const {String} + * @default + */ + +encoding.MAX_HASH160 = new Buffer( + 'ffffffffffffffffffffffffffffffffffffffff', + 'hex' +); + +/** + * A hash of all zeroes. + * @const {String} + * @default + */ + +encoding.NULL_HASH160 = '0000000000000000000000000000000000000000'; + +/** + * A hash of all 0xff. + * @const {String} + * @default + */ + +encoding.HIGH_HASH160 = 'ffffffffffffffffffffffffffffffffffffffff'; + +/** + * A compressed pubkey of all zeroes. + * @const {Buffer} + * @default + */ + +encoding.ZERO_KEY = new Buffer( + '000000000000000000000000000000000000000000000000000000000000000000', + 'hex' +); + +/** + * A 73 byte signature of all zeroes. + * @const {Buffer} + * @default + */ + +encoding.ZERO_SIG = new Buffer('' + + '0000000000000000000000000000000000000000000000000000000000000000' + + '0000000000000000000000000000000000000000000000000000000000000000' + + '000000000000000000', + 'hex' +); + +/** + * A 64 byte signature of all zeroes. + * @const {Buffer} + * @default + */ + +encoding.ZERO_SIG64 = new Buffer('' + + '0000000000000000000000000000000000000000000000000000000000000000' + + '0000000000000000000000000000000000000000000000000000000000000000', + 'hex' +); + +/** + * 4 zero bytes. + * @const {Buffer} + * @default + */ + +encoding.ZERO_U32 = new Buffer('00000000', 'hex'); + +/** + * 8 zero bytes. + * @const {Buffer} + * @default + */ + +encoding.ZERO_U64 = new Buffer('0000000000000000', 'hex'); + /** * Read uint64 as a js number. * @private diff --git a/lib/wallet/common.js b/lib/wallet/common.js index 9b6acbed..1eee4b20 100644 --- a/lib/wallet/common.js +++ b/lib/wallet/common.js @@ -24,3 +24,79 @@ common.isName = function isName(key) { return key.length >= 1 && key.length <= 40; }; + +/** + * Sort an array of transactions in dependency order. + * @param {TX[]} txs + * @returns {TX[]} + */ + +common.sortTX = function sortTX(txs) { + var depMap = {}; + var count = {}; + var result = []; + var top = []; + var map = txs; + var i, j, tx, hash, input; + var prev, hasDeps, deps; + + if (Array.isArray(txs)) { + map = {}; + for (i = 0; i < txs.length; i++) { + tx = txs[i]; + hash = tx.hash('hex'); + map[hash] = tx; + } + } + + for (i = 0; i < txs.length; i++) { + tx = txs[i]; + hash = tx.hash('hex'); + hasDeps = false; + + count[hash] = 0; + + for (j = 0; j < tx.inputs.length; j++) { + input = tx.inputs[j]; + prev = input.prevout.hash; + + if (!map[prev]) + continue; + + count[hash] += 1; + hasDeps = true; + + if (!depMap[prev]) + depMap[prev] = []; + + depMap[prev].push(tx); + } + + if (hasDeps) + continue; + + top.push(tx); + } + + for (i = 0; i < top.length; i++) { + tx = top[i]; + hash = tx.hash('hex'); + + result.push(tx); + + deps = depMap[hash]; + + if (!deps) + continue; + + for (j = 0; j < deps.length; j++) { + tx = deps[j]; + hash = tx.hash('hex'); + + if (--count[hash] === 0) + top.push(tx); + } + } + + return result; +}; diff --git a/lib/wallet/records.js b/lib/wallet/records.js index 327f36a5..6053b965 100644 --- a/lib/wallet/records.js +++ b/lib/wallet/records.js @@ -8,7 +8,7 @@ var assert = require('assert'); var util = require('../utils/util'); -var constants = require('../protocol/constants'); +var encoding = require('../utils/encoding'); var BufferReader = require('../utils/reader'); var StaticWriter = require('../utils/staticwriter'); var TX = require('../primitives/tx'); @@ -23,7 +23,7 @@ function ChainState() { return new ChainState(); this.startHeight = -1; - this.startHash = constants.NULL_HASH; + this.startHash = encoding.NULL_HASH; this.height = -1; this.marked = false; } @@ -101,7 +101,7 @@ function BlockMeta(hash, height, ts) { if (!(this instanceof BlockMeta)) return new BlockMeta(hash, height, ts); - this.hash = hash || constants.NULL_HASH; + this.hash = hash || encoding.NULL_HASH; this.height = height != null ? height : -1; this.ts = ts || 0; } @@ -365,7 +365,7 @@ BlockMapRecord.prototype.remove = function remove(hash, wid) { */ function TXMapRecord(hash, wids) { - this.hash = hash || constants.NULL_HASH; + this.hash = hash || encoding.NULL_HASH; this.wids = wids || []; this.id = TXMapRecord.id++; } @@ -416,7 +416,7 @@ TXMapRecord.fromRaw = function fromRaw(hash, data) { */ function OutpointMapRecord(hash, index, wids) { - this.hash = hash || constants.NULL_HASH; + this.hash = hash || encoding.NULL_HASH; this.index = index != null ? index : -1; this.wids = wids || []; } @@ -465,7 +465,7 @@ OutpointMapRecord.fromRaw = function fromRaw(hash, index, data) { */ function PathMapRecord(hash, wids) { - this.hash = hash || constants.NULL_HASH; + this.hash = hash || encoding.NULL_HASH; this.wids = wids || []; } diff --git a/lib/wallet/txdb.js b/lib/wallet/txdb.js index ad9b58a2..a6b4cd25 100644 --- a/lib/wallet/txdb.js +++ b/lib/wallet/txdb.js @@ -11,16 +11,17 @@ var util = require('../utils/util'); var LRU = require('../utils/lru'); var co = require('../utils/co'); var assert = require('assert'); -var constants = require('../protocol/constants'); var BufferReader = require('../utils/reader'); var StaticWriter = require('../utils/staticwriter'); -var btcutils = require('../btc/utils'); var Amount = require('../btc/amount'); var CoinView = require('../coins/coinview'); var Coin = require('../primitives/coin'); var Outpoint = require('../primitives/outpoint'); var records = require('./records'); var layout = require('./layout').txdb; +var encoding = require('../utils/encoding'); +var policy = require('../protocol/policy'); +var Script = require('../script/script'); var BlockMapRecord = records.BlockMapRecord; var OutpointMapRecord = records.OutpointMapRecord; var TXRecord = records.TXRecord; @@ -1556,7 +1557,7 @@ TXDB.prototype.removeConflicts = co(function* removeConflicts(tx, conf) { */ TXDB.prototype.verifyInput = co(function* verifyInput(tx, index, coin) { - var flags = constants.flags.MANDATORY_VERIFY_FLAGS; + var flags = Script.flags.MANDATORY_VERIFY_FLAGS; if (!this.options.verify) return true; return yield tx.verifyInputAsync(index, coin, flags); @@ -1672,8 +1673,8 @@ TXDB.prototype.getLocked = function getLocked() { TXDB.prototype.getAccountHistoryHashes = function getHistoryHashes(account) { return this.keys({ - gte: layout.T(account, constants.NULL_HASH), - lte: layout.T(account, constants.HIGH_HASH), + gte: layout.T(account, encoding.NULL_HASH), + lte: layout.T(account, encoding.HIGH_HASH), parse: function(key) { key = layout.Tt(key); return key[1]; @@ -1692,8 +1693,8 @@ TXDB.prototype.getHistoryHashes = function getHistoryHashes(account) { return this.getAccountHistoryHashes(account); return this.keys({ - gte: layout.t(constants.NULL_HASH), - lte: layout.t(constants.HIGH_HASH), + gte: layout.t(encoding.NULL_HASH), + lte: layout.t(encoding.HIGH_HASH), parse: layout.tt }); }; @@ -1706,8 +1707,8 @@ TXDB.prototype.getHistoryHashes = function getHistoryHashes(account) { TXDB.prototype.getAccountPendingHashes = function getAccountPendingHashes(account) { return this.keys({ - gte: layout.P(account, constants.NULL_HASH), - lte: layout.P(account, constants.HIGH_HASH), + gte: layout.P(account, encoding.NULL_HASH), + lte: layout.P(account, encoding.HIGH_HASH), parse: function(key) { key = layout.Pp(key); return key[1]; @@ -1726,8 +1727,8 @@ TXDB.prototype.getPendingHashes = function getPendingHashes(account) { return this.getAccountPendingHashes(account); return this.keys({ - gte: layout.p(constants.NULL_HASH), - lte: layout.p(constants.HIGH_HASH), + gte: layout.p(encoding.NULL_HASH), + lte: layout.p(encoding.HIGH_HASH), parse: layout.pp }); }; @@ -1740,8 +1741,8 @@ TXDB.prototype.getPendingHashes = function getPendingHashes(account) { TXDB.prototype.getAccountOutpoints = function getAccountOutpoints(account) { return this.keys({ - gte: layout.C(account, constants.NULL_HASH, 0), - lte: layout.C(account, constants.HIGH_HASH, 0xffffffff), + gte: layout.C(account, encoding.NULL_HASH, 0), + lte: layout.C(account, encoding.HIGH_HASH, 0xffffffff), parse: function(key) { key = layout.Cc(key); return new Outpoint(key[1], key[2]); @@ -1760,8 +1761,8 @@ TXDB.prototype.getOutpoints = function getOutpoints(account) { return this.getAccountOutpoints(account); return this.keys({ - gte: layout.c(constants.NULL_HASH, 0), - lte: layout.c(constants.HIGH_HASH, 0xffffffff), + gte: layout.c(encoding.NULL_HASH, 0), + lte: layout.c(encoding.HIGH_HASH, 0xffffffff), parse: function(key) { key = layout.cc(key); return new Outpoint(key[0], key[1]); @@ -1785,8 +1786,8 @@ TXDB.prototype.getAccountHeightRangeHashes = function getAccountHeightRangeHashe var end = options.end || 0xffffffff; return this.keys({ - gte: layout.H(account, start, constants.NULL_HASH), - lte: layout.H(account, end, constants.HIGH_HASH), + gte: layout.H(account, start, encoding.NULL_HASH), + lte: layout.H(account, end, encoding.HIGH_HASH), limit: options.limit, reverse: options.reverse, parse: function(key) { @@ -1822,8 +1823,8 @@ TXDB.prototype.getHeightRangeHashes = function getHeightRangeHashes(account, opt end = options.end || 0xffffffff; return this.keys({ - gte: layout.h(start, constants.NULL_HASH), - lte: layout.h(end, constants.HIGH_HASH), + gte: layout.h(start, encoding.NULL_HASH), + lte: layout.h(end, encoding.HIGH_HASH), limit: options.limit, reverse: options.reverse, parse: function(key) { @@ -1859,8 +1860,8 @@ TXDB.prototype.getAccountRangeHashes = function getAccountRangeHashes(account, o var end = options.end || 0xffffffff; return this.keys({ - gte: layout.M(account, start, constants.NULL_HASH), - lte: layout.M(account, end, constants.HIGH_HASH), + gte: layout.M(account, start, encoding.NULL_HASH), + lte: layout.M(account, end, encoding.HIGH_HASH), limit: options.limit, reverse: options.reverse, parse: function(key) { @@ -1896,8 +1897,8 @@ TXDB.prototype.getRangeHashes = function getRangeHashes(account, options) { end = options.end || 0xffffffff; return this.keys({ - gte: layout.m(start, constants.NULL_HASH), - lte: layout.m(end, constants.HIGH_HASH), + gte: layout.m(start, encoding.NULL_HASH), + lte: layout.m(end, encoding.HIGH_HASH), limit: options.limit, reverse: options.reverse, parse: function(key) { @@ -1971,8 +1972,8 @@ TXDB.prototype.getHistory = function getHistory(account) { // Fast case return this.values({ - gte: layout.t(constants.NULL_HASH), - lte: layout.t(constants.HIGH_HASH), + gte: layout.t(encoding.NULL_HASH), + lte: layout.t(encoding.HIGH_HASH), parse: TXRecord.fromRaw }); }; @@ -2040,8 +2041,8 @@ TXDB.prototype.getCredits = function getCredits(account) { // Fast case return this.range({ - gte: layout.c(constants.NULL_HASH, 0x00000000), - lte: layout.c(constants.HIGH_HASH, 0xffffffff), + gte: layout.c(encoding.NULL_HASH, 0x00000000), + lte: layout.c(encoding.HIGH_HASH, 0xffffffff), parse: function(key, value) { var parts = layout.cc(key); var hash = parts[0]; @@ -3029,7 +3030,7 @@ Details.prototype.getFee = function getFee() { */ Details.prototype.getRate = function getRate(fee) { - return btcutils.getRate(this.vsize, fee); + return policy.getRate(this.vsize, fee); }; /** @@ -3127,7 +3128,7 @@ function BlockRecord(hash, height, ts) { if (!(this instanceof BlockRecord)) return new BlockRecord(hash, height, ts); - this.hash = hash || constants.NULL_HASH; + this.hash = hash || encoding.NULL_HASH; this.height = height != null ? height : -1; this.ts = ts || 0; this.hashes = []; diff --git a/lib/wallet/wallet.js b/lib/wallet/wallet.js index 061057c2..c54c0613 100644 --- a/lib/wallet/wallet.js +++ b/lib/wallet/wallet.js @@ -9,14 +9,12 @@ var assert = require('assert'); var EventEmitter = require('events').EventEmitter; -var constants = require('../protocol/constants'); var Network = require('../protocol/network'); var util = require('../utils/util'); var encoding = require('../utils/encoding'); var Locker = require('../utils/locker'); var co = require('../utils/co'); var crypto = require('../crypto/crypto'); -var btcutils = require('../btc/utils'); var BufferReader = require('../utils/reader'); var StaticWriter = require('../utils/staticwriter'); var base58 = require('../utils/base58'); @@ -31,6 +29,8 @@ var Output = require('../primitives/output'); var Account = require('./account'); var MasterKey = require('./masterkey'); var LRU = require('../utils/lru'); +var policy = require('../protocol/policy'); +var consensus = require('../protocol/consensus'); /** * BIP44 Wallet @@ -81,7 +81,7 @@ function Wallet(db, options) { this.initialized = false; this.watchOnly = false; this.accountDepth = 0; - this.token = constants.ZERO_HASH; + this.token = encoding.ZERO_HASH; this.tokenDepth = 0; this.master = new MasterKey(); @@ -1473,7 +1473,7 @@ Wallet.prototype.getAccountByAddress = co(function* getAccountByAddress(address) */ Wallet.prototype.estimate = co(function* estimate(prev) { - var scale = constants.WITNESS_SCALE_FACTOR; + var scale = consensus.WITNESS_SCALE_FACTOR; var address = prev.getAddress(); var account = yield this.getAccountByAddress(address); var size = 0; @@ -1630,15 +1630,15 @@ Wallet.prototype._send = co(function* send(options, passphrase) { if (!mtx.isSigned()) throw new Error('TX could not be fully signed.'); - assert(mtx.getFee() <= constants.tx.MAX_FEE, 'TX exceeds maxfee.'); + assert(mtx.getFee() <= MTX.MAX_FEE, 'TX exceeds maxfee.'); tx = mtx.toTX(); // Sanity checks. - if (tx.getSigopsCost(mtx.view) > constants.tx.MAX_SIGOPS_COST) + if (tx.getSigopsCost(mtx.view) > policy.MAX_TX_SIGOPS_COST) throw new Error('TX exceeds policy sigops.'); - if (tx.getWeight() > constants.tx.MAX_WEIGHT) + if (tx.getWeight() > policy.MAX_TX_WEIGHT) throw new Error('TX exceeds policy weight.'); yield this.db.addTX(tx); @@ -1682,8 +1682,8 @@ Wallet.prototype.increaseFee = co(function* increaseFee(hash, rate, passphrase) oldFee = tx.getFee(view); fee = tx.getMinFee(null, rate); - if (fee > constants.tx.MAX_FEE) - fee = constants.tx.MAX_FEE; + if (fee > MTX.MAX_FEE) + fee = MTX.MAX_FEE; if (oldFee >= fee) throw new Error('Fee is not increasing.'); @@ -1758,7 +1758,7 @@ Wallet.prototype.resend = co(function* resend() { if (txs.length > 0) this.logger.info('Rebroadcasting %d transactions.', txs.length); - txs = btcutils.sortTX(txs); + txs = common.sortTX(txs); for (i = 0; i < txs.length; i++) yield this.db.send(txs[i]); diff --git a/lib/wallet/walletdb.js b/lib/wallet/walletdb.js index 6a65020a..c536bb08 100644 --- a/lib/wallet/walletdb.js +++ b/lib/wallet/walletdb.js @@ -15,8 +15,6 @@ var Locker = require('../utils/locker'); var LRU = require('../utils/lru'); var encoding = require('../utils/encoding'); var crypto = require('../crypto/crypto'); -var btcutils = require('../btc/utils'); -var constants = require('../protocol/constants'); var Network = require('../protocol/network'); var Path = require('./path'); var common = require('./common'); @@ -299,8 +297,8 @@ WalletDB.prototype.watch = co(function* watch() { var iter, item, data, outpoint, items; iter = this.db.iterator({ - gte: layout.p(constants.NULL_HASH), - lte: layout.p(constants.HIGH_HASH) + gte: layout.p(encoding.NULL_HASH), + lte: layout.p(encoding.HIGH_HASH) }); for (;;) { @@ -321,8 +319,8 @@ WalletDB.prototype.watch = co(function* watch() { } iter = this.db.iterator({ - gte: layout.o(constants.NULL_HASH, 0), - lte: layout.o(constants.HIGH_HASH, 0xffffffff) + gte: layout.o(encoding.NULL_HASH, 0), + lte: layout.o(encoding.HIGH_HASH, 0xffffffff) }); for (;;) { @@ -1241,8 +1239,8 @@ WalletDB.prototype.hasPath = function hasPath(wid, hash) { WalletDB.prototype.getHashes = function getHashes() { return this.db.keys({ - gte: layout.p(constants.NULL_HASH), - lte: layout.p(constants.HIGH_HASH), + gte: layout.p(encoding.NULL_HASH), + lte: layout.p(encoding.HIGH_HASH), parse: layout.pp }); }; @@ -1254,8 +1252,8 @@ WalletDB.prototype.getHashes = function getHashes() { WalletDB.prototype.getOutpoints = function getOutpoints() { return this.db.keys({ - gte: layout.o(constants.NULL_HASH, 0), - lte: layout.o(constants.HIGH_HASH, 0xffffffff), + gte: layout.o(encoding.NULL_HASH, 0), + lte: layout.o(encoding.HIGH_HASH, 0xffffffff), parse: function(key) { var items = layout.oo(key); return new Outpoint(items[0], items[1]); @@ -1271,8 +1269,8 @@ WalletDB.prototype.getOutpoints = function getOutpoints() { WalletDB.prototype.getWalletHashes = function getWalletHashes(wid) { return this.db.keys({ - gte: layout.P(wid, constants.NULL_HASH), - lte: layout.P(wid, constants.HIGH_HASH), + gte: layout.P(wid, encoding.NULL_HASH), + lte: layout.P(wid, encoding.HIGH_HASH), parse: layout.Pp }); }; @@ -1286,8 +1284,8 @@ WalletDB.prototype.getWalletHashes = function getWalletHashes(wid) { WalletDB.prototype.getAccountHashes = function getAccountHashes(wid, account) { return this.db.keys({ - gte: layout.r(wid, account, constants.NULL_HASH), - lte: layout.r(wid, account, constants.HIGH_HASH), + gte: layout.r(wid, account, encoding.NULL_HASH), + lte: layout.r(wid, account, encoding.HIGH_HASH), parse: layout.rr }); }; @@ -1302,8 +1300,8 @@ WalletDB.prototype.getWalletPaths = co(function* getWalletPaths(wid) { var i, item, items, hash, path; items = yield this.db.range({ - gte: layout.P(wid, constants.NULL_HASH), - lte: layout.P(wid, constants.HIGH_HASH) + gte: layout.P(wid, encoding.NULL_HASH), + lte: layout.P(wid, encoding.HIGH_HASH) }); for (i = 0; i < items.length; i++) { @@ -1492,7 +1490,7 @@ WalletDB.prototype.resend = co(function* resend() { txs.push(wtx.tx); } - txs = btcutils.sortTX(txs); + txs = common.sortTX(txs); for (i = 0; i < txs.length; i++) yield this.send(txs[i]); diff --git a/lib/wallet/walletkey.js b/lib/wallet/walletkey.js index 1090225e..cf069ac0 100644 --- a/lib/wallet/walletkey.js +++ b/lib/wallet/walletkey.js @@ -7,10 +7,10 @@ 'use strict'; -var constants = require('../protocol/constants'); var KeyRing = require('../primitives/keyring'); var util = require('../utils/util'); var Path = require('./path'); +var Script = require('../script/script'); /** * Represents a key ring which amounts to an address. @@ -132,7 +132,7 @@ WalletKey.prototype.toJSON = function toJSON() { publicKey: this.publicKey.toString('hex'), script: this.script ? this.script.toRaw().toString('hex') : null, program: this.witness ? this.getProgram().toRaw().toString('hex') : null, - type: constants.scriptTypesByVal[this.getType()].toLowerCase(), + type: Script.typesByVal[this.getType()].toLowerCase(), address: this.getAddress('base58') }; }; diff --git a/migrate/chaindb1to2.js b/migrate/chaindb1to2.js index 9b5b868f..6decd28c 100644 --- a/migrate/chaindb1to2.js +++ b/migrate/chaindb1to2.js @@ -1,5 +1,6 @@ var assert = require('assert'); var constants = require('../lib/protocol/constants'); +var encoding = require('../lib/utils/encoding'); var networks = require('../lib/protocol/networks'); var co = require('../lib/utils/co'); var BufferWriter = require('../lib/utils/writer'); @@ -65,8 +66,8 @@ var updateVersion = co(function* updateVersion() { var checkTipIndex = co(function* checkTipIndex() { var keys = yield db.keys({ - gte: pair('p', constants.ZERO_HASH), - lte: pair('p', constants.MAX_HASH) + gte: pair('p', encoding.ZERO_HASH), + lte: pair('p', encoding.MAX_HASH) }); if (keys.length === 0) { @@ -119,8 +120,8 @@ var reserializeCoins = co(function* reserializeCoins() { var i, iter, item, hash, old, coins, coin, output; iter = db.iterator({ - gte: pair('c', constants.ZERO_HASH), - lte: pair('c', constants.MAX_HASH), + gte: pair('c', encoding.ZERO_HASH), + lte: pair('c', encoding.MAX_HASH), values: true }); @@ -171,8 +172,8 @@ var reserializeUndo = co(function* reserializeUndo() { var iter, item, br, undo; iter = db.iterator({ - gte: pair('u', constants.ZERO_HASH), - lte: pair('u', constants.MAX_HASH), + gte: pair('u', encoding.ZERO_HASH), + lte: pair('u', encoding.MAX_HASH), values: true }); diff --git a/migrate/coins-old.js b/migrate/coins-old.js index 80cbb648..f3d3bf77 100644 --- a/migrate/coins-old.js +++ b/migrate/coins-old.js @@ -9,6 +9,7 @@ var assert = require('assert'); var util = require('../lib/utils/util'); var constants = require('../lib/protocol/constants'); +var encoding = require('../lib/utils/encoding'); var Coin = require('../lib/primitives/coin'); var Output = require('../lib/primitives/output'); var BufferReader = require('../lib/utils/reader'); @@ -35,7 +36,7 @@ function Coins(options) { return new Coins(options); this.version = 1; - this.hash = constants.NULL_HASH; + this.hash = encoding.NULL_HASH; this.height = -1; this.coinbase = true; this.outputs = []; diff --git a/migrate/ensure-tip-index.js b/migrate/ensure-tip-index.js index a2af3590..afb9f5b6 100644 --- a/migrate/ensure-tip-index.js +++ b/migrate/ensure-tip-index.js @@ -1,5 +1,6 @@ var assert = require('assert'); var constants = require('../lib/protocol/constants'); +var encoding = require('../lib/utils/encoding'); var co = require('../lib/utils/co'); var BufferWriter = require('../lib/utils/writer'); var BufferReader = require('../lib/utils/reader'); @@ -62,8 +63,8 @@ function entryFromRaw(data) { function getEntries() { return db.values({ - gte: pair('e', constants.ZERO_HASH), - lte: pair('e', constants.MAX_HASH), + gte: pair('e', encoding.ZERO_HASH), + lte: pair('e', encoding.MAX_HASH), parse: entryFromRaw }); } diff --git a/migrate/walletdb2to3.js b/migrate/walletdb2to3.js index 295213e4..eb3d366e 100644 --- a/migrate/walletdb2to3.js +++ b/migrate/walletdb2to3.js @@ -1,6 +1,7 @@ var bcoin = require('../'); var walletdb = require('../lib/wallet/walletdb'); var constants = require('../lib/protocol/constants'); +var encoding = require('../lib/utils/encoding'); var Path = require('../lib/wallet/path'); var MasterKey = require('../lib/wallet/masterkey'); var Account = require('../lib/wallet/account'); @@ -55,8 +56,8 @@ var updatePathMap = co(function* updatePathMap() { var hash, path, keys, key, ring; iter = db.iterator({ - gte: layout.p(constants.NULL_HASH), - lte: layout.p(constants.HIGH_HASH), + gte: layout.p(encoding.NULL_HASH), + lte: layout.p(encoding.HIGH_HASH), values: true }); @@ -171,8 +172,8 @@ var updateTXMap = co(function* updateTXMap() { var iter, item, wallets; iter = db.iterator({ - gte: layout.e(constants.NULL_HASH), - lte: layout.e(constants.HIGH_HASH), + gte: layout.e(encoding.NULL_HASH), + lte: layout.e(encoding.HIGH_HASH), values: true }); diff --git a/migrate/walletdb3to4.js b/migrate/walletdb3to4.js index b5e90727..9c94c1f3 100644 --- a/migrate/walletdb3to4.js +++ b/migrate/walletdb3to4.js @@ -1,6 +1,7 @@ var assert = require('assert'); var bcoin = require('../'); var constants = require('../lib/protocol/constants'); +var encoding = require('../lib/utils/encoding'); var WalletDB = require('../lib/wallet/walletdb'); var TXDB = require('../lib/wallet/txdb'); var BufferWriter = require('../lib/utils/writer'); @@ -107,7 +108,7 @@ function fromExtended(data, saveCoins) { tx.ts = p.readU32(); tx.ps = p.readU32(); - if (tx.block === constants.NULL_HASH) + if (tx.block === encoding.NULL_HASH) tx.block = null; if (tx.height === 0x7fffffff) diff --git a/migrate/walletdb5to6.js b/migrate/walletdb5to6.js index 7883d3d5..11211683 100644 --- a/migrate/walletdb5to6.js +++ b/migrate/walletdb5to6.js @@ -1,6 +1,7 @@ var assert = require('assert'); var bcoin = require('../'); var constants = require('../lib/protocol/constants'); +var encoding = require('../lib/utils/encoding'); var BufferWriter = require('../lib/utils/writer'); var BufferReader = require('../lib/utils/reader'); var util = require('../lib/utils/util'); @@ -97,8 +98,8 @@ var indexPaths = co(function* indexPaths() { var i, items, item, wid, index, hash; items = yield db.range({ - gte: new Buffer('5000000000' + constants.NULL_HASH, 'hex'), // P - lte: new Buffer('50ffffffff' + constants.HIGH_HASH, 'hex') // P + gte: new Buffer('5000000000' + encoding.NULL_HASH, 'hex'), // P + lte: new Buffer('50ffffffff' + encoding.HIGH_HASH, 'hex') // P }); for (i = 0; i < items.length; i++) { @@ -115,8 +116,8 @@ var patchPathMaps = co(function* patchPathMaps() { var i, items, item, hash, wids; items = yield db.range({ - gte: new Buffer('70' + constants.NULL_HASH, 'hex'), // p - lte: new Buffer('70' + constants.HIGH_HASH, 'hex') // p + gte: new Buffer('70' + encoding.NULL_HASH, 'hex'), // p + lte: new Buffer('70' + encoding.HIGH_HASH, 'hex') // p }); for (i = 0; i < items.length; i++) { diff --git a/scripts/gen.js b/scripts/gen.js index 78bc2370..eadd0012 100644 --- a/scripts/gen.js +++ b/scripts/gen.js @@ -2,12 +2,13 @@ var BN = require('bn.js'); var util = require('../lib/utils/util'); -var constants = require('../lib/protocol/constants'); +var consensus = require('../lib/protocol/consensus'); +var encoding = require('../lib/utils/encoding'); var TX = require('../lib/primitives/tx'); var Block = require('../lib/primitives/block'); var Script = require('../lib/script/script'); var Opcode = require('../lib/script/opcode'); -var opcodes = constants.opcodes; +var opcodes = Script.opcodes; var main, testnet, regtest, segnet3, segnet4, btcd; function createGenesisBlock(options) { @@ -32,14 +33,14 @@ function createGenesisBlock(options) { } if (!reward) - reward = 50 * constants.COIN; + reward = 50 * consensus.COIN; tx = new TX({ version: 1, flag: 1, inputs: [{ prevout: { - hash: constants.NULL_HASH, + hash: encoding.NULL_HASH, index: 0xffffffff }, script: [ @@ -58,7 +59,7 @@ function createGenesisBlock(options) { block = new Block({ version: options.version, - prevBlock: constants.NULL_HASH, + prevBlock: encoding.NULL_HASH, merkleRoot: tx.hash('hex'), ts: options.ts, bits: options.bits, diff --git a/test/block-test.js b/test/block-test.js index 85113a9c..4195906f 100644 --- a/test/block-test.js +++ b/test/block-test.js @@ -2,14 +2,15 @@ var fs = require('fs'); var assert = require('assert'); -var btcutils = require('../lib/btc/utils'); var Bloom = require('../lib/utils/bloom'); var Block = require('../lib/primitives/block'); var Headers = require('../lib/primitives/headers'); var MerkleBlock = require('../lib/primitives/merkleblock'); var CoinView = require('../lib/coins/coinview'); var Coin = require('../lib/primitives/coin'); -var constants = require('../lib/protocol/constants'); +var consensus = require('../lib/protocol/consensus'); +var Script = require('../lib/script/script'); +var encoding = require('../lib/utils/encoding'); var bip152 = require('../lib/net/bip152'); var block300025 = require('./data/block300025.json'); @@ -111,8 +112,8 @@ describe('Block', function() { var reward; for (;;) { - reward = btcutils.getReward(height, 210000); - assert(reward <= constants.COIN * 50); + reward = consensus.getReward(height, 210000); + assert(reward <= consensus.COIN * 50); total += reward; if (reward === 0) break; @@ -172,7 +173,7 @@ describe('Block', function() { assert(!block.hasWitness()); assert.equal(block.getWeight(), 1136924); - flags = constants.flags.VERIFY_P2SH | constants.flags.VERIFY_DERSIG; + flags = Script.flags.VERIFY_P2SH | Script.flags.VERIFY_DERSIG; for (i = 1; i < block.txs.length; i++) { tx = block.txs[i]; @@ -193,7 +194,7 @@ describe('Block', function() { var block2 = new Block(block); var ret = {}; block2.hash(); - block2.merkleRoot = constants.NULL_HASH; + block2.merkleRoot = encoding.NULL_HASH; block2._validHeaders = null; assert(!block2.verify(ret)); assert.equal(ret.reason, 'bad-txnmrklroot'); @@ -206,7 +207,7 @@ describe('Block', function() { var mblock2 = new MerkleBlock(mblock); var ret = {}; mblock2.hash(); - mblock2.merkleRoot = constants.NULL_HASH; + mblock2.merkleRoot = encoding.NULL_HASH; assert(!mblock2.verify(ret)); assert.equal(ret.reason, 'bad-txnmrklroot'); mblock2.merkleRoot = mblock.merkleRoot; diff --git a/test/bloom-test.js b/test/bloom-test.js index ced7076f..f9dc4ae1 100644 --- a/test/bloom-test.js +++ b/test/bloom-test.js @@ -1,7 +1,6 @@ 'use strict'; var assert = require('assert'); -var constants = require('../lib/protocol/constants'); var Bloom = require('../lib/utils/bloom'); var murmur3 = require('../lib/utils/murmur3'); diff --git a/test/chain-test.js b/test/chain-test.js index a8c0c729..91526058 100644 --- a/test/chain-test.js +++ b/test/chain-test.js @@ -2,7 +2,7 @@ var assert = require('assert'); var BN = require('bn.js'); -var constants = require('../lib/protocol/constants'); +var consensus = require('../lib/protocol/consensus'); var co = require('../lib/utils/co'); var Coin = require('../lib/primitives/coin'); var Script = require('../lib/script/script'); @@ -57,7 +57,7 @@ describe('Chain', function() { it('should open chain and miner', cob(function* () { miner.mempool = null; - constants.tx.COINBASE_MATURITY = 0; + consensus.COINBASE_MATURITY = 0; yield node.open(); })); @@ -321,7 +321,7 @@ describe('Chain', function() { redeemer.addOutput({ script: [ Script.array(new BN(1)), - constants.opcodes.OP_CHECKSEQUENCEVERIFY + Script.opcodes.OP_CHECKSEQUENCEVERIFY ], value: 10 * 1e8 }); @@ -351,7 +351,7 @@ describe('Chain', function() { redeemer.addOutput({ script: [ Script.array(new BN(2)), - constants.opcodes.OP_CHECKSEQUENCEVERIFY + Script.opcodes.OP_CHECKSEQUENCEVERIFY ], value: 10 * 1e8 }); @@ -377,7 +377,7 @@ describe('Chain', function() { redeemer.addOutput({ script: [ Script.array(new BN(1)), - constants.opcodes.OP_CHECKSEQUENCEVERIFY + Script.opcodes.OP_CHECKSEQUENCEVERIFY ], value: 10 * 1e8 }); @@ -421,7 +421,7 @@ describe('Chain', function() { redeemer.addOutput({ script: [ Script.array(new BN(2)), - constants.opcodes.OP_CHECKSEQUENCEVERIFY + Script.opcodes.OP_CHECKSEQUENCEVERIFY ], value: 10 * 1e8 }); @@ -451,7 +451,7 @@ describe('Chain', function() { })); it('should cleanup', cob(function* () { - constants.tx.COINBASE_MATURITY = 100; + consensus.COINBASE_MATURITY = 100; yield node.close(); })); }); diff --git a/test/http-test.js b/test/http-test.js index 6a90bbb1..6cd37c03 100644 --- a/test/http-test.js +++ b/test/http-test.js @@ -1,17 +1,19 @@ 'use strict'; var assert = require('assert'); -var constants = require('../lib/protocol/constants'); +var consensus = require('../lib/protocol/consensus'); +var encoding = require('../lib/utils/encoding'); var co = require('../lib/utils/co'); var Amount = require('../lib/btc/amount'); var MTX = require('../lib/primitives/mtx'); var HTTP = require('../lib/http'); var FullNode = require('../lib/node/fullnode'); +var USER_VERSION = require('../package.json').version; var cob = co.cob; var dummyInput = { prevout: { - hash: constants.NULL_HASH, + hash: encoding.NULL_HASH, index: 0 } }; @@ -36,7 +38,7 @@ describe('HTTP', function() { this.timeout(15000); it('should open node', cob(function* () { - constants.tx.COINBASE_MATURITY = 0; + consensus.COINBASE_MATURITY = 0; yield node.open(); })); @@ -48,8 +50,8 @@ describe('HTTP', function() { it('should get info', cob(function* () { var info = yield wallet.client.getInfo(); assert.equal(info.network, node.network.type); - assert.equal(info.version, constants.USER_VERSION); - assert.equal(info.agent, constants.USER_AGENT); + assert.equal(info.version, USER_VERSION); + assert.equal(info.agent, node.pool.userAgent); assert.equal(typeof info.chain, 'object'); assert.equal(info.chain.height, 0); })); @@ -155,7 +157,7 @@ describe('HTTP', function() { })); it('should cleanup', cob(function* () { - constants.tx.COINBASE_MATURITY = 100; + consensus.COINBASE_MATURITY = 100; yield wallet.close(); yield node.close(); })); diff --git a/test/mempool-test.js b/test/mempool-test.js index 589d0946..8326c219 100644 --- a/test/mempool-test.js +++ b/test/mempool-test.js @@ -1,7 +1,7 @@ 'use strict'; var assert = require('assert'); -var constants = require('../lib/protocol/constants'); +var encoding = require('../lib/utils/encoding'); var crypto = require('../lib/crypto/crypto'); var co = require('../lib/utils/co'); var MempoolEntry = require('../lib/mempool/mempoolentry'); @@ -15,7 +15,7 @@ var Address = require('../lib/primitives/address'); var Script = require('../lib/script/script'); var Witness = require('../lib/script/witness'); var Block = require('../lib/primitives/block'); -var opcodes = constants.opcodes; +var opcodes = Script.opcodes; var cob = co.cob; describe('Mempool', function() { @@ -45,7 +45,7 @@ describe('Mempool', function() { var coin, entry; if (!prevHash) - prevHash = constants.ONE_HASH.toString('hex'); + prevHash = encoding.ONE_HASH.toString('hex'); coin = new Coin({ version: 1, @@ -70,7 +70,7 @@ describe('Mempool', function() { it('should open mempool', cob(function* () { yield mempool.open(); - chain.state.flags |= constants.flags.VERIFY_WITNESS; + chain.state.flags |= Script.flags.VERIFY_WITNESS; })); it('should open walletdb', cob(function* () { @@ -143,7 +143,7 @@ describe('Mempool', function() { yield w.template(fake); // Fake signature - fake.inputs[0].script.set(0, constants.ZERO_SIG); + fake.inputs[0].script.set(0, encoding.ZERO_SIG); fake.inputs[0].script.compile(); fake = fake.toTX(); // balance: 11000 @@ -365,7 +365,7 @@ describe('Mempool', function() { input = { prevout: { - hash: constants.NULL_HASH, + hash: encoding.NULL_HASH, index: 0xffffffff } }; diff --git a/test/protocol-test.js b/test/protocol-test.js index 3f4ce09d..27068b27 100644 --- a/test/protocol-test.js +++ b/test/protocol-test.js @@ -2,7 +2,6 @@ var fs = require('fs'); var assert = require('assert'); -var constants = require('../lib/protocol/constants'); var Network = require('../lib/protocol/network'); var util = require('../lib/utils/util'); var BufferReader = require('../lib/utils/reader'); @@ -38,38 +37,38 @@ describe('Protocol', function() { } v1 = packets.VersionPacket.fromOptions({ - version: constants.VERSION, - services: constants.LOCAL_SERVICES, + version: 300, + services: 1, ts: network.now(), remote: new NetAddress(), local: new NetAddress(), nonce: util.nonce(), - agent: constants.USER_AGENT, + agent: agent, height: 0, noRelay: false }); packetTest('version', v1, function(payload) { - assert.equal(payload.version, constants.VERSION); + assert.equal(payload.version, 300); assert.equal(payload.agent, agent); assert.equal(payload.height, 0); assert.equal(payload.noRelay, false); }); v2 = packets.VersionPacket.fromOptions({ - version: constants.VERSION, - services: constants.LOCAL_SERVICES, + version: 300, + services: 1, ts: network.now(), remote: new NetAddress(), local: new NetAddress(), nonce: util.nonce(), - agent: constants.USER_AGENT, + agent: agent, height: 10, noRelay: true }); packetTest('version', v2, function(payload) { - assert.equal(payload.version, constants.VERSION); + assert.equal(payload.version, 300); assert.equal(payload.agent, agent); assert.equal(payload.height, 10); assert.equal(payload.noRelay, true); @@ -80,13 +79,13 @@ describe('Protocol', function() { hosts = [ new NetAddress({ - services: constants.LOCAL_SERVICES, + services: 1, host: '127.0.0.1', port: 8333, ts: util.now() }), new NetAddress({ - services: constants.LOCAL_SERVICES, + services: 1, host: '::123:456:789a', port: 18333, ts: util.now() @@ -98,12 +97,12 @@ describe('Protocol', function() { assert.equal(payload.items.length, 2); assert.equal(typeof payload.items[0].ts, 'number'); - assert.equal(payload.items[0].services, constants.LOCAL_SERVICES); + assert.equal(payload.items[0].services, 1); assert.equal(payload.items[0].host, hosts[0].host); assert.equal(payload.items[0].port, hosts[0].port); assert.equal(typeof payload.items[1].ts, 'number'); - assert.equal(payload.items[1].services, constants.LOCAL_SERVICES); + assert.equal(payload.items[1].services, 1); assert.equal(payload.items[1].host, hosts[1].host); assert.equal(payload.items[1].port, hosts[1].port); }); diff --git a/test/script-test.js b/test/script-test.js index 061a1183..3dcd3fdc 100644 --- a/test/script-test.js +++ b/test/script-test.js @@ -5,8 +5,8 @@ var Script = require('../lib/script/script'); var Witness = require('../lib/script/witness'); var Stack = require('../lib/script/stack'); var TX = require('../lib/primitives/tx'); -var constants = require('../lib/protocol/constants'); -var opcodes = constants.opcodes; +var encoding = require('../lib/utils/encoding'); +var opcodes = Script.opcodes; var scripts = require('./data/script_tests'); @@ -256,8 +256,8 @@ describe('Script', function() { for (i = 0; i < flags.length; i++) { name = 'VERIFY_' + flags[i]; - assert(constants.flags[name] != null, 'Unknown flag.'); - flag |= constants.flags[name]; + assert(Script.flags[name] != null, 'Unknown flag.'); + flag |= Script.flags[name]; } flags = flag; @@ -273,7 +273,7 @@ describe('Script', function() { flag: 1, inputs: [{ prevout: { - hash: constants.NULL_HASH, + hash: encoding.NULL_HASH, index: 0xffffffff }, script: new Script([opcodes.OP_0, opcodes.OP_0]), diff --git a/test/tx-test.js b/test/tx-test.js index 390d14fc..0d2ff8cb 100644 --- a/test/tx-test.js +++ b/test/tx-test.js @@ -5,7 +5,7 @@ var assert = require('assert'); var util = require('../lib/utils/util'); var encoding = require('../lib/utils/encoding'); var crypto = require('../lib/crypto/crypto'); -var constants = require('../lib/protocol/constants'); +var consensus = require('../lib/protocol/consensus'); var Network = require('../lib/protocol/network'); var TX = require('../lib/primitives/tx'); var Block = require('../lib/primitives/block'); @@ -59,8 +59,8 @@ function parseTest(data) { for (i = 0; i < flags.length; i++) { name = 'VERIFY_' + flags[i]; - assert(constants.flags[name] != null, 'Unknown flag.'); - flag |= constants.flags[name]; + assert(Script.flags[name] != null, 'Unknown flag.'); + flag |= Script.flags[name]; } flags = flag; @@ -163,29 +163,29 @@ describe('TX', function() { it('should verify non-minimal output' + suffix, function() { clearCache(tx1.tx, noCache); - assert(tx1.tx.verify(tx1.view, constants.flags.VERIFY_P2SH)); + assert(tx1.tx.verify(tx1.view, Script.flags.VERIFY_P2SH)); }); it('should verify tx.version == 0' + suffix, function() { clearCache(tx2.tx, noCache); - assert(tx2.tx.verify(tx2.view, constants.flags.VERIFY_P2SH)); + assert(tx2.tx.verify(tx2.view, Script.flags.VERIFY_P2SH)); }); it('should verify sighash_single bug w/ findanddelete' + suffix, function() { clearCache(tx3.tx, noCache); - assert(tx3.tx.verify(tx3.view, constants.flags.VERIFY_P2SH)); + assert(tx3.tx.verify(tx3.view, Script.flags.VERIFY_P2SH)); }); it('should verify high S value with only DERSIG enabled' + suffix, function() { var coin = tx4.view.getOutput(tx4.tx.inputs[0]); - var flags = constants.flags.VERIFY_P2SH | constants.flags.VERIFY_DERSIG; + var flags = Script.flags.VERIFY_P2SH | Script.flags.VERIFY_DERSIG; clearCache(tx4.tx, noCache); assert(tx4.tx.verifyInput(0, coin, flags)); }); it('should verify the coolest tx ever sent' + suffix, function() { clearCache(coolest.tx, noCache); - assert(coolest.tx.verify(coolest.view, constants.flags.VERIFY_NONE)); + assert(coolest.tx.verify(coolest.view, Script.flags.VERIFY_NONE)); }); it('should parse witness tx properly' + suffix, function() { @@ -344,10 +344,10 @@ describe('TX', function() { var tx = new TX({ version: 1, flag: 1, - inputs: [createInput(constants.MAX_MONEY + 1, view)], + inputs: [createInput(consensus.MAX_MONEY + 1, view)], outputs: [{ script: [], - value: constants.MAX_MONEY + value: consensus.MAX_MONEY }], locktime: 0 }); @@ -360,10 +360,10 @@ describe('TX', function() { var tx = new TX({ version: 1, flag: 1, - inputs: [createInput(constants.MAX_MONEY, view)], + inputs: [createInput(consensus.MAX_MONEY, view)], outputs: [{ script: [], - value: constants.MAX_MONEY + value: consensus.MAX_MONEY }], locktime: 0 }); @@ -376,10 +376,10 @@ describe('TX', function() { var tx = new TX({ version: 1, flag: 1, - inputs: [createInput(constants.MAX_MONEY, view)], + inputs: [createInput(consensus.MAX_MONEY, view)], outputs: [{ script: [], - value: constants.MAX_MONEY + 1 + value: consensus.MAX_MONEY + 1 }], locktime: 0 }); @@ -392,10 +392,10 @@ describe('TX', function() { var tx = new TX({ version: 1, flag: 1, - inputs: [createInput(constants.MAX_MONEY, view)], + inputs: [createInput(consensus.MAX_MONEY, view)], outputs: [{ script: [], - value: constants.MAX_MONEY + value: consensus.MAX_MONEY }], locktime: 0 }); @@ -408,7 +408,7 @@ describe('TX', function() { var tx = new TX({ version: 1, flag: 1, - inputs: [createInput(constants.MAX_MONEY + 1, view)], + inputs: [createInput(consensus.MAX_MONEY + 1, view)], outputs: [{ script: [], value: 0 @@ -425,13 +425,13 @@ describe('TX', function() { version: 1, flag: 1, inputs: [ - createInput(Math.floor(constants.MAX_MONEY / 2), view), - createInput(Math.floor(constants.MAX_MONEY / 2), view), - createInput(Math.floor(constants.MAX_MONEY / 2), view) + createInput(Math.floor(consensus.MAX_MONEY / 2), view), + createInput(Math.floor(consensus.MAX_MONEY / 2), view), + createInput(Math.floor(consensus.MAX_MONEY / 2), view) ], outputs: [{ script: [], - value: constants.MAX_MONEY + value: consensus.MAX_MONEY }], locktime: 0 }); @@ -444,19 +444,19 @@ describe('TX', function() { var tx = new TX({ version: 1, flag: 1, - inputs: [createInput(constants.MAX_MONEY, view)], + inputs: [createInput(consensus.MAX_MONEY, view)], outputs: [ { script: [], - value: Math.floor(constants.MAX_MONEY / 2) + value: Math.floor(consensus.MAX_MONEY / 2) }, { script: [], - value: Math.floor(constants.MAX_MONEY / 2) + value: Math.floor(consensus.MAX_MONEY / 2) }, { script: [], - value: Math.floor(constants.MAX_MONEY / 2) + value: Math.floor(consensus.MAX_MONEY / 2) } ], locktime: 0 @@ -471,9 +471,9 @@ describe('TX', function() { version: 1, flag: 1, inputs: [ - createInput(Math.floor(constants.MAX_MONEY / 2), view), - createInput(Math.floor(constants.MAX_MONEY / 2), view), - createInput(Math.floor(constants.MAX_MONEY / 2), view) + createInput(Math.floor(consensus.MAX_MONEY / 2), view), + createInput(Math.floor(consensus.MAX_MONEY / 2), view), + createInput(Math.floor(consensus.MAX_MONEY / 2), view) ], outputs: [{ script: [], @@ -496,7 +496,7 @@ describe('TX', function() { version: 1, flag: 1, inputs: [ - createInput(Math.floor(constants.MAX_MONEY / 2), view) + createInput(Math.floor(consensus.MAX_MONEY / 2), view) ], outputs: [{ script: [], @@ -519,7 +519,7 @@ describe('TX', function() { version: 1, flag: 1, inputs: [ - createInput(Math.floor(constants.MAX_MONEY / 2), view) + createInput(Math.floor(consensus.MAX_MONEY / 2), view) ], outputs: [{ script: [], @@ -555,7 +555,7 @@ describe('TX', function() { inputs: [createInput(util.MAX_SAFE_INTEGER, view)], outputs: [{ script: [], - value: constants.MAX_MONEY + value: consensus.MAX_MONEY }], locktime: 0 }); @@ -568,7 +568,7 @@ describe('TX', function() { var tx = new TX({ version: 1, flag: 1, - inputs: [createInput(constants.MAX_MONEY, view)], + inputs: [createInput(consensus.MAX_MONEY, view)], outputs: [{ script: [], value: util.MAX_SAFE_INTEGER @@ -608,7 +608,7 @@ describe('TX', function() { ], outputs: [{ script: [], - value: constants.MAX_MONEY + value: consensus.MAX_MONEY }], locktime: 0 }); @@ -621,7 +621,7 @@ describe('TX', function() { var tx = new TX({ version: 1, flag: 1, - inputs: [createInput(constants.MAX_MONEY, view)], + inputs: [createInput(consensus.MAX_MONEY, view)], outputs: [ { script: [], diff --git a/test/utils-test.js b/test/utils-test.js index b5b5d940..db687305 100644 --- a/test/utils-test.js +++ b/test/utils-test.js @@ -3,12 +3,12 @@ var assert = require('assert'); var BN = require('bn.js'); var ec = require('../lib/crypto/ec'); -var btcutils = require('../lib/btc/utils'); var base58 = require('../lib/utils/base58'); var encoding = require('../lib/utils/encoding'); var crypto = require('../lib/crypto/crypto'); var schnorr = require('../lib/crypto/schnorr'); var Amount = require('../lib/btc/amount'); +var consensus = require('../lib/protocol/consensus'); describe('Utils', function() { var vectors, signed, unsigned; @@ -53,7 +53,7 @@ describe('Utils', function() { 'hex' ); - assert(btcutils.verifyPOW(hash, bits)); + assert(consensus.verifyPOW(hash, bits)); }); it('should convert satoshi to btc', function() { diff --git a/test/wallet-test.js b/test/wallet-test.js index fbc7afac..5f97669e 100644 --- a/test/wallet-test.js +++ b/test/wallet-test.js @@ -1,7 +1,7 @@ 'use strict'; var assert = require('assert'); -var constants = require('../lib/protocol/constants'); +var consensus = require('../lib/protocol/consensus'); var util = require('../lib/utils/util'); var encoding = require('../lib/utils/encoding'); var crypto = require('../lib/crypto/crypto'); @@ -14,7 +14,7 @@ var KeyRing = require('../lib/primitives/keyring'); var Address = require('../lib/primitives/address'); var Script = require('../lib/script/script'); var HD = require('../lib/hd'); -var scriptTypes = constants.scriptTypes; +var scriptTypes = Script.types; var cob = co.cob; var KEY1 = 'xprv9s21ZrQH143K3Aj6xQBymM31Zb4BVc7wxqfUhMZrzewdDVCt' @@ -40,7 +40,7 @@ function nextBlock(height) { height: height, prevBlock: prev, ts: globalTime + height, - merkleRoot: constants.NULL_HASH, + merkleRoot: encoding.NULL_HASH, nonce: 0, bits: 0 }; @@ -72,7 +72,7 @@ describe('Wallet', function() { this.timeout(5000); it('should open walletdb', cob(function* () { - constants.tx.COINBASE_MATURITY = 0; + consensus.COINBASE_MATURITY = 0; yield walletdb.open(); })); @@ -108,11 +108,11 @@ describe('Wallet', function() { })); p2pkh = co(function* p2pkh(witness, bullshitNesting) { - var flags = constants.flags.STANDARD_VERIFY_FLAGS; + var flags = Script.flags.STANDARD_VERIFY_FLAGS; var w, addr, src, tx; if (witness) - flags |= constants.flags.VERIFY_WITNESS; + flags |= Script.flags.VERIFY_WITNESS; w = yield walletdb.create({ witness: witness }); @@ -261,7 +261,7 @@ describe('Wallet', function() { // Script inputs but do not sign yield w.template(fake); // Fake signature - fake.inputs[0].script.set(0, constants.ZERO_SIG); + fake.inputs[0].script.set(0, encoding.ZERO_SIG); fake.inputs[0].script.compile(); // balance: 11000 fake = fake.toTX(); @@ -410,7 +410,7 @@ describe('Wallet', function() { // Script inputs but do not sign // yield w.template(fake); // Fake signature - // fake.inputs[0].script.set(0, constants.ZERO_SIG); + // fake.inputs[0].script.set(0, encoding.ZERO_SIG); // fake.inputs[0].script.compile(); // balance: 11000 // fake = fake.toTX(); @@ -533,7 +533,7 @@ describe('Wallet', function() { .addOutput(w1.getAddress(), 5460) .addOutput(w1.getAddress(), 5460); - t1.addInput(dummy(constants.NULL_HASH)); + t1.addInput(dummy(encoding.NULL_HASH)); t1 = t1.toTX(); yield walletdb.addTX(t1); @@ -613,7 +613,7 @@ describe('Wallet', function() { tx.addOutput(to.getAddress(), 5460); cost = tx.getOutputValue(); - total = cost * constants.tx.MIN_FEE; + total = cost * MTX.MIN_FEE; coins1 = yield w1.getCoins(); coins2 = yield w2.getCoins(); @@ -627,10 +627,6 @@ describe('Wallet', function() { tx.addInput(coins2[0]); left = tx.getInputValue() - total; - if (left < constants.tx.DUST_THRESHOLD) { - tx.outputs[tx.outputs.length - 2].value += left; - left = 0; - } if (left === 0) tx.outputs.pop(); else @@ -663,14 +659,14 @@ describe('Wallet', function() { })); multisig = co(function* multisig(witness, bullshitNesting, cb) { - var flags = constants.flags.STANDARD_VERIFY_FLAGS; + var flags = Script.flags.STANDARD_VERIFY_FLAGS; var options, w1, w2, w3, receive, b58, addr, paddr, utx, send, change; var rec = bullshitNesting ? 'nested' : 'receive'; var depth = bullshitNesting ? 'nestedDepth' : 'receiveDepth'; var view, block; if (witness) - flags |= constants.flags.VERIFY_WITNESS; + flags |= Script.flags.VERIFY_WITNESS; // Create 3 2-of-3 wallets with our pubkeys as "shared keys" options = { @@ -1345,6 +1341,6 @@ describe('Wallet', function() { })); it('should cleanup', function() { - constants.tx.COINBASE_MATURITY = 100; + consensus.COINBASE_MATURITY = 100; }); });