2017-01-06 09:57:55 -08:00
|
|
|
/*!
|
|
|
|
|
* consensus.js - consensus constants and helpers for bcoin
|
|
|
|
|
* Copyright (c) 2014-2015, Fedor Indutny (MIT License)
|
2017-02-03 22:47:26 -08:00
|
|
|
* Copyright (c) 2014-2017, Christopher Jeffrey (MIT License).
|
2017-01-06 09:57:55 -08:00
|
|
|
* https://github.com/bcoin-org/bcoin
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
'use strict';
|
|
|
|
|
|
2017-02-03 22:47:26 -08:00
|
|
|
/**
|
|
|
|
|
* @module protocol/consensus
|
|
|
|
|
*/
|
|
|
|
|
|
2017-06-29 20:54:07 -07:00
|
|
|
const assert = require('assert');
|
2017-11-17 00:49:02 -08:00
|
|
|
const BN = require('bn.js');
|
2018-01-02 20:24:56 -08:00
|
|
|
const {Cuckoo, Solution, codes} = require('thc/cuckoo');
|
2017-01-06 09:57:55 -08:00
|
|
|
|
|
|
|
|
/**
|
2018-01-05 03:17:44 -08:00
|
|
|
* Coin exponent.
|
|
|
|
|
* @const {Number}
|
|
|
|
|
* @default
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
exports.EXP = 6;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* One handshake in dollarydoos.
|
2017-01-06 09:57:55 -08:00
|
|
|
* @const {Amount}
|
|
|
|
|
* @default
|
|
|
|
|
*/
|
|
|
|
|
|
2018-01-05 03:17:44 -08:00
|
|
|
exports.COIN = Math.pow(10, exports.EXP);
|
2017-01-06 09:57:55 -08:00
|
|
|
|
|
|
|
|
/**
|
2018-01-05 03:17:44 -08:00
|
|
|
* Maximum amount of subsidies in dollarydoos (consensus).
|
2017-01-06 09:57:55 -08:00
|
|
|
* @const {Amount}
|
|
|
|
|
* @default
|
|
|
|
|
*/
|
|
|
|
|
|
2018-01-05 03:17:44 -08:00
|
|
|
exports.MAX_SUBSIDY = 1.4e9 * exports.COIN;
|
2017-01-06 09:57:55 -08:00
|
|
|
|
2017-01-06 21:14:11 -08:00
|
|
|
/**
|
2018-01-05 03:17:44 -08:00
|
|
|
* Maximum amount of money in dollarydoos (consensus).
|
2017-01-06 21:14:11 -08:00
|
|
|
* @const {Amount}
|
|
|
|
|
* @default
|
|
|
|
|
*/
|
|
|
|
|
|
2018-01-05 03:17:44 -08:00
|
|
|
exports.MAX_MONEY = exports.MAX_SUBSIDY;
|
2017-01-06 21:14:11 -08:00
|
|
|
|
|
|
|
|
/**
|
2018-01-05 03:17:44 -08:00
|
|
|
* Base block subsidy (consensus).
|
2017-01-06 21:14:11 -08:00
|
|
|
* @const {Amount}
|
|
|
|
|
* @default
|
|
|
|
|
*/
|
|
|
|
|
|
2018-01-05 03:17:44 -08:00
|
|
|
exports.BASE_REWARD = 80 * exports.COIN;
|
2017-01-06 21:14:11 -08:00
|
|
|
|
2017-01-06 09:57:55 -08:00
|
|
|
/**
|
|
|
|
|
* 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 cost (consensus).
|
|
|
|
|
* @const {Number}
|
|
|
|
|
* @default
|
|
|
|
|
*/
|
|
|
|
|
|
2018-01-02 20:24:56 -08:00
|
|
|
exports.MAX_BLOCK_SIGOPS = 80000;
|
2017-01-06 09:57:55 -08:00
|
|
|
|
2017-11-01 16:58:39 -07:00
|
|
|
/**
|
|
|
|
|
* Size of set to pick median time from.
|
|
|
|
|
* @const {Number}
|
|
|
|
|
* @default
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
exports.MEDIAN_TIMESPAN = 11;
|
|
|
|
|
|
2017-01-06 09:57:55 -08:00
|
|
|
/**
|
|
|
|
|
* Number of blocks before a coinbase
|
|
|
|
|
* spend can occur (consensus).
|
|
|
|
|
* @const {Number}
|
|
|
|
|
* @default
|
|
|
|
|
*/
|
|
|
|
|
|
2018-01-05 03:17:44 -08:00
|
|
|
// exports.COINBASE_MATURITY = 400;
|
|
|
|
|
exports.COINBASE_MATURITY = 25;
|
2017-01-06 09:57:55 -08:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 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;
|
|
|
|
|
|
2017-12-05 03:14:34 -08:00
|
|
|
/**
|
|
|
|
|
* A hash of all zeroes.
|
|
|
|
|
* @const {Buffer}
|
|
|
|
|
* @default
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
exports.ZERO_HASH = Buffer.alloc(32, 0x00);
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* A hash of all zeroes.
|
|
|
|
|
* @const {String}
|
|
|
|
|
* @default
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
exports.NULL_HASH =
|
|
|
|
|
'0000000000000000000000000000000000000000000000000000000000000000';
|
|
|
|
|
|
2018-01-02 20:24:56 -08:00
|
|
|
/**
|
|
|
|
|
* Block header size.
|
|
|
|
|
* @const {Number}
|
|
|
|
|
* @default
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
exports.HEADER_SIZE = 160;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Block header nonce position.
|
|
|
|
|
* @const {Number}
|
|
|
|
|
* @default
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
exports.NONCE_POS = 144;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Block header nonce size.
|
|
|
|
|
* @const {Number}
|
|
|
|
|
* @default
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
exports.NONCE_SIZE = 16;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Block header of all zeroes.
|
|
|
|
|
* @const {Buffer}
|
|
|
|
|
* @default
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
exports.ZERO_HEADER = Buffer.alloc(exports.HEADER_SIZE, 0x00);
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Block header nonce of all zeroes.
|
|
|
|
|
* @const {Buffer}
|
|
|
|
|
* @default
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
exports.ZERO_NONCE = Buffer.alloc(exports.NONCE_SIZE, 0x00);
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Block header solution of all zeroes.
|
|
|
|
|
* @const {Solution}
|
|
|
|
|
* @default
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
exports.ZERO_SOL = new Solution();
|
|
|
|
|
|
2017-01-06 09:57:55 -08:00
|
|
|
/**
|
|
|
|
|
* Convert a compact number to a big number.
|
|
|
|
|
* Used for `block.bits` -> `target` conversion.
|
|
|
|
|
* @param {Number} compact
|
|
|
|
|
* @returns {BN}
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
exports.fromCompact = function fromCompact(compact) {
|
2017-09-06 22:14:04 -07:00
|
|
|
if (compact === 0)
|
|
|
|
|
return new BN(0);
|
|
|
|
|
|
2017-07-27 09:27:00 -07:00
|
|
|
const exponent = compact >>> 24;
|
|
|
|
|
const negative = (compact >>> 23) & 1;
|
2017-09-06 22:14:04 -07:00
|
|
|
|
2017-06-29 20:54:07 -07:00
|
|
|
let mantissa = compact & 0x7fffff;
|
|
|
|
|
let num;
|
2017-01-06 09:57:55 -08:00
|
|
|
|
|
|
|
|
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) {
|
2017-09-06 22:14:04 -07:00
|
|
|
if (num.isZero())
|
2017-01-06 09:57:55 -08:00
|
|
|
return 0;
|
|
|
|
|
|
2017-09-06 22:14:04 -07:00
|
|
|
let exponent = num.byteLength();
|
|
|
|
|
let mantissa;
|
2017-01-06 09:57:55 -08:00
|
|
|
|
|
|
|
|
if (exponent <= 3) {
|
|
|
|
|
mantissa = num.toNumber();
|
|
|
|
|
mantissa <<= 8 * (3 - exponent);
|
|
|
|
|
} else {
|
|
|
|
|
mantissa = num.ushrn(8 * (exponent - 3)).toNumber();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (mantissa & 0x800000) {
|
|
|
|
|
mantissa >>= 8;
|
2018-01-02 20:24:56 -08:00
|
|
|
exponent += 1;
|
2017-01-06 09:57:55 -08:00
|
|
|
}
|
|
|
|
|
|
2017-09-06 22:14:04 -07:00
|
|
|
let compact = (exponent << 24) | mantissa;
|
2017-01-06 09:57:55 -08:00
|
|
|
|
|
|
|
|
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) {
|
2017-07-27 09:27:00 -07:00
|
|
|
const target = exports.fromCompact(bits);
|
2017-01-06 09:57:55 -08:00
|
|
|
|
2017-09-06 22:14:04 -07:00
|
|
|
if (target.isNeg() || target.isZero())
|
2017-01-06 09:57:55 -08:00
|
|
|
return false;
|
|
|
|
|
|
2018-01-04 10:25:43 -08:00
|
|
|
if (target.bitLength() > 256)
|
|
|
|
|
return false;
|
|
|
|
|
|
2017-09-06 22:14:04 -07:00
|
|
|
const num = new BN(hash, 'le');
|
2017-01-06 09:57:55 -08:00
|
|
|
|
2017-09-06 22:14:04 -07:00
|
|
|
if (num.gt(target))
|
2017-01-06 09:57:55 -08:00
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
};
|
|
|
|
|
|
2018-01-02 20:24:56 -08:00
|
|
|
/**
|
|
|
|
|
* Verify cuckoo cycle solution.
|
|
|
|
|
* @param {Buffer} hdr
|
|
|
|
|
* @param {Solution} sol
|
|
|
|
|
* @param {Object} params
|
|
|
|
|
* @returns {Boolean}
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
exports.verifySolution = function verifySolution(hdr, sol, params) {
|
|
|
|
|
const {bits, size, ease} = params;
|
|
|
|
|
const cuckoo = new Cuckoo(bits, size, ease);
|
|
|
|
|
const code = cuckoo.verifyHeader(hdr, sol);
|
|
|
|
|
return code === codes.POW_OK;
|
|
|
|
|
};
|
|
|
|
|
|
2017-01-06 09:57:55 -08:00
|
|
|
/**
|
|
|
|
|
* Calculate block subsidy.
|
|
|
|
|
* @param {Number} height - Reward era by height.
|
|
|
|
|
* @returns {Amount}
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
exports.getReward = function getReward(height, interval) {
|
2018-01-05 03:17:44 -08:00
|
|
|
assert((height >>> 0) === height, 'Bad height for reward.');
|
|
|
|
|
assert((interval >>> 0) === interval);
|
2017-01-06 09:57:55 -08:00
|
|
|
|
2017-09-06 22:14:04 -07:00
|
|
|
const halvings = Math.floor(height / interval);
|
|
|
|
|
|
2018-01-05 03:17:44 -08:00
|
|
|
if (halvings >= 52)
|
2017-01-06 09:57:55 -08:00
|
|
|
return 0;
|
|
|
|
|
|
2018-01-05 03:17:44 -08:00
|
|
|
return Math.floor(exports.BASE_REWARD / Math.pow(2, halvings));
|
2017-01-06 09:57:55 -08:00
|
|
|
};
|
2017-07-21 11:42:09 -07:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Test version bit.
|
|
|
|
|
* @param {Number} version
|
|
|
|
|
* @param {Number} bit
|
|
|
|
|
* @returns {Boolean}
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
exports.hasBit = function hasBit(version, bit) {
|
2018-01-02 20:24:56 -08:00
|
|
|
return (version & (1 << bit)) !== 0;
|
2017-07-21 11:42:09 -07:00
|
|
|
};
|