primitives: classify primitives.

This commit is contained in:
Christopher Jeffrey 2017-11-15 18:54:47 -08:00
parent ca08d6d97e
commit daa55a05bc
No known key found for this signature in database
GPG Key ID: 8962AB9DE6666BBD
27 changed files with 14507 additions and 14404 deletions

File diff suppressed because it is too large Load Diff

View File

@ -19,18 +19,15 @@ const InvItem = require('../primitives/invitem');
const ZERO = new BN(0);
/**
* Chain Entry
* Represents an entry in the chain. Unlike
* other bitcoin fullnodes, we store the
* chainwork _with_ the entry in order to
* avoid reading the entire chain index on
* boot and recalculating the chainworks.
* @alias module:blockchain.ChainEntry
* @constructor
* @param {Object?} options
* @property {Hash} hash
* @property {Number} version - Transaction version. Note that Bcoin reads
* versions as unsigned even though they are signed at the protocol level.
* This value will never be negative.
* @property {Number} version
* @property {Hash} prevBlock
* @property {Hash} merkleRoot
* @property {Number} time
@ -38,25 +35,331 @@ const ZERO = new BN(0);
* @property {Number} nonce
* @property {Number} height
* @property {BN} chainwork
* @property {ReversedHash} rhash - Reversed block hash (uint256le).
* @property {ReversedHash} rhash
*/
function ChainEntry(options) {
if (!(this instanceof ChainEntry))
return new ChainEntry(options);
class ChainEntry {
/**
* Create a chain entry.
* @constructor
* @param {Object?} options
*/
this.hash = encoding.NULL_HASH;
this.version = 1;
this.prevBlock = encoding.NULL_HASH;
this.merkleRoot = encoding.NULL_HASH;
this.time = 0;
this.bits = 0;
this.nonce = 0;
this.height = 0;
this.chainwork = ZERO;
constructor(options) {
this.hash = encoding.NULL_HASH;
this.version = 1;
this.prevBlock = encoding.NULL_HASH;
this.merkleRoot = encoding.NULL_HASH;
this.time = 0;
this.bits = 0;
this.nonce = 0;
this.height = 0;
this.chainwork = ZERO;
if (options)
this.fromOptions(options);
if (options)
this.fromOptions(options);
}
/**
* Inject properties from options.
* @private
* @param {Object} options
*/
fromOptions(options) {
assert(options, 'Block data is required.');
assert(typeof options.hash === 'string');
assert((options.version >>> 0) === options.version);
assert(typeof options.prevBlock === 'string');
assert(typeof options.merkleRoot === 'string');
assert((options.time >>> 0) === options.time);
assert((options.bits >>> 0) === options.bits);
assert((options.nonce >>> 0) === options.nonce);
assert((options.height >>> 0) === options.height);
assert(!options.chainwork || BN.isBN(options.chainwork));
this.hash = options.hash;
this.version = options.version;
this.prevBlock = options.prevBlock;
this.merkleRoot = options.merkleRoot;
this.time = options.time;
this.bits = options.bits;
this.nonce = options.nonce;
this.height = options.height;
this.chainwork = options.chainwork || ZERO;
return this;
}
/**
* Instantiate chainentry from options.
* @param {Object} options
* @param {ChainEntry} prev - Previous entry.
* @returns {ChainEntry}
*/
static fromOptions(options, prev) {
return new this().fromOptions(options, prev);
}
/**
* Calculate the proof: (1 << 256) / (target + 1)
* @returns {BN} proof
*/
getProof() {
const target = consensus.fromCompact(this.bits);
if (target.isNeg() || target.isZero())
return new BN(0);
return ChainEntry.MAX_CHAINWORK.div(target.iaddn(1));
}
/**
* Calculate the chainwork by
* adding proof to previous chainwork.
* @returns {BN} chainwork
*/
getChainwork(prev) {
const proof = this.getProof();
if (!prev)
return proof;
return proof.iadd(prev.chainwork);
}
/**
* Test against the genesis block.
* @returns {Boolean}
*/
isGenesis() {
return this.height === 0;
}
/**
* Test whether the entry contains an unknown version bit.
* @param {Network} network
* @returns {Boolean}
*/
hasUnknown(network) {
const TOP_MASK = consensus.VERSION_TOP_MASK;
const TOP_BITS = consensus.VERSION_TOP_BITS;
const bits = (this.version & TOP_MASK) >>> 0;
if (bits !== TOP_BITS)
return false;
return (this.version & network.unknownBits) !== 0;
}
/**
* Test whether the entry contains a version bit.
* @param {Number} bit
* @returns {Boolean}
*/
hasBit(bit) {
return consensus.hasBit(this.version, bit);
}
/**
* Get little-endian block hash.
* @returns {Hash}
*/
rhash() {
return encoding.revHex(this.hash);
}
/**
* Inject properties from block.
* @private
* @param {Block|MerkleBlock} block
* @param {ChainEntry} prev - Previous entry.
*/
fromBlock(block, prev) {
this.hash = block.hash('hex');
this.version = block.version;
this.prevBlock = block.prevBlock;
this.merkleRoot = block.merkleRoot;
this.time = block.time;
this.bits = block.bits;
this.nonce = block.nonce;
this.height = prev ? prev.height + 1: 0;
this.chainwork = this.getChainwork(prev);
return this;
}
/**
* Instantiate chainentry from block.
* @param {Block|MerkleBlock} block
* @param {ChainEntry} prev - Previous entry.
* @returns {ChainEntry}
*/
static fromBlock(block, prev) {
return new this().fromBlock(block, prev);
}
/**
* Serialize the entry to internal database format.
* @returns {Buffer}
*/
toRaw() {
const bw = new StaticWriter(116);
bw.writeU32(this.version);
bw.writeHash(this.prevBlock);
bw.writeHash(this.merkleRoot);
bw.writeU32(this.time);
bw.writeU32(this.bits);
bw.writeU32(this.nonce);
bw.writeU32(this.height);
bw.writeBytes(this.chainwork.toArrayLike(Buffer, 'le', 32));
return bw.render();
}
/**
* Inject properties from serialized data.
* @private
* @param {Buffer} data
*/
fromRaw(data) {
const br = new BufferReader(data, true);
const hash = hash256.digest(br.readBytes(80));
br.seek(-80);
this.hash = hash.toString('hex');
this.version = br.readU32();
this.prevBlock = br.readHash('hex');
this.merkleRoot = br.readHash('hex');
this.time = br.readU32();
this.bits = br.readU32();
this.nonce = br.readU32();
this.height = br.readU32();
this.chainwork = new BN(br.readBytes(32), 'le');
return this;
}
/**
* Deserialize the entry.
* @param {Buffer} data
* @returns {ChainEntry}
*/
static fromRaw(data) {
return new this().fromRaw(data);
}
/**
* Serialize the entry to an object more
* suitable for JSON serialization.
* @returns {Object}
*/
toJSON() {
return {
hash: encoding.revHex(this.hash),
version: this.version,
prevBlock: encoding.revHex(this.prevBlock),
merkleRoot: encoding.revHex(this.merkleRoot),
time: this.time,
bits: this.bits,
nonce: this.nonce,
height: this.height,
chainwork: this.chainwork.toString('hex', 64)
};
}
/**
* Inject properties from json object.
* @private
* @param {Object} json
*/
fromJSON(json) {
assert(json, 'Block data is required.');
assert(typeof json.hash === 'string');
assert((json.version >>> 0) === json.version);
assert(typeof json.prevBlock === 'string');
assert(typeof json.merkleRoot === 'string');
assert((json.time >>> 0) === json.time);
assert((json.bits >>> 0) === json.bits);
assert((json.nonce >>> 0) === json.nonce);
assert(typeof json.chainwork === 'string');
this.hash = encoding.revHex(json.hash);
this.version = json.version;
this.prevBlock = encoding.revHex(json.prevBlock);
this.merkleRoot = encoding.revHex(json.merkleRoot);
this.time = json.time;
this.bits = json.bits;
this.nonce = json.nonce;
this.height = json.height;
this.chainwork = new BN(json.chainwork, 'hex');
return this;
}
/**
* Instantiate block from jsonified object.
* @param {Object} json
* @returns {ChainEntry}
*/
static fromJSON(json) {
return new this().fromJSON(json);
}
/**
* Convert the entry to a headers object.
* @returns {Headers}
*/
toHeaders() {
return Headers.fromEntry(this);
}
/**
* Convert the entry to an inv item.
* @returns {InvItem}
*/
toInv() {
return new InvItem(InvItem.types.BLOCK, this.hash);
}
/**
* Return a more user-friendly object.
* @returns {Object}
*/
inspect() {
const json = this.toJSON();
json.version = json.version.toString(16);
return json;
}
/**
* Test whether an object is a {@link ChainEntry}.
* @param {Object} obj
* @returns {Boolean}
*/
static isChainEntry(obj) {
return obj instanceof ChainEntry;
}
}
/**
@ -66,307 +369,6 @@ function ChainEntry(options) {
ChainEntry.MAX_CHAINWORK = new BN(1).ushln(256);
/**
* Inject properties from options.
* @private
* @param {Object} options
*/
ChainEntry.prototype.fromOptions = function fromOptions(options) {
assert(options, 'Block data is required.');
assert(typeof options.hash === 'string');
assert((options.version >>> 0) === options.version);
assert(typeof options.prevBlock === 'string');
assert(typeof options.merkleRoot === 'string');
assert((options.time >>> 0) === options.time);
assert((options.bits >>> 0) === options.bits);
assert((options.nonce >>> 0) === options.nonce);
assert((options.height >>> 0) === options.height);
assert(!options.chainwork || BN.isBN(options.chainwork));
this.hash = options.hash;
this.version = options.version;
this.prevBlock = options.prevBlock;
this.merkleRoot = options.merkleRoot;
this.time = options.time;
this.bits = options.bits;
this.nonce = options.nonce;
this.height = options.height;
this.chainwork = options.chainwork || ZERO;
return this;
};
/**
* Instantiate chainentry from options.
* @param {Object} options
* @param {ChainEntry} prev - Previous entry.
* @returns {ChainEntry}
*/
ChainEntry.fromOptions = function fromOptions(options, prev) {
return new ChainEntry().fromOptions(options, prev);
};
/**
* Calculate the proof: (1 << 256) / (target + 1)
* @returns {BN} proof
*/
ChainEntry.prototype.getProof = function getProof() {
const target = consensus.fromCompact(this.bits);
if (target.isNeg() || target.isZero())
return new BN(0);
return ChainEntry.MAX_CHAINWORK.div(target.iaddn(1));
};
/**
* Calculate the chainwork by
* adding proof to previous chainwork.
* @returns {BN} chainwork
*/
ChainEntry.prototype.getChainwork = function getChainwork(prev) {
const proof = this.getProof();
if (!prev)
return proof;
return proof.iadd(prev.chainwork);
};
/**
* Test against the genesis block.
* @returns {Boolean}
*/
ChainEntry.prototype.isGenesis = function isGenesis() {
return this.height === 0;
};
/**
* Test whether the entry contains an unknown version bit.
* @param {Network} network
* @returns {Boolean}
*/
ChainEntry.prototype.hasUnknown = function hasUnknown(network) {
const TOP_MASK = consensus.VERSION_TOP_MASK;
const TOP_BITS = consensus.VERSION_TOP_BITS;
const bits = (this.version & TOP_MASK) >>> 0;
if (bits !== TOP_BITS)
return false;
return (this.version & network.unknownBits) !== 0;
};
/**
* Test whether the entry contains a version bit.
* @param {Number} bit
* @returns {Boolean}
*/
ChainEntry.prototype.hasBit = function hasBit(bit) {
return consensus.hasBit(this.version, bit);
};
/**
* Get little-endian block hash.
* @returns {Hash}
*/
ChainEntry.prototype.rhash = function rhash() {
return encoding.revHex(this.hash);
};
/**
* Inject properties from block.
* @private
* @param {Block|MerkleBlock} block
* @param {ChainEntry} prev - Previous entry.
*/
ChainEntry.prototype.fromBlock = function fromBlock(block, prev) {
this.hash = block.hash('hex');
this.version = block.version;
this.prevBlock = block.prevBlock;
this.merkleRoot = block.merkleRoot;
this.time = block.time;
this.bits = block.bits;
this.nonce = block.nonce;
this.height = prev ? prev.height + 1: 0;
this.chainwork = this.getChainwork(prev);
return this;
};
/**
* Instantiate chainentry from block.
* @param {Block|MerkleBlock} block
* @param {ChainEntry} prev - Previous entry.
* @returns {ChainEntry}
*/
ChainEntry.fromBlock = function fromBlock(block, prev) {
return new ChainEntry().fromBlock(block, prev);
};
/**
* Serialize the entry to internal database format.
* @returns {Buffer}
*/
ChainEntry.prototype.toRaw = function toRaw() {
const bw = new StaticWriter(116);
bw.writeU32(this.version);
bw.writeHash(this.prevBlock);
bw.writeHash(this.merkleRoot);
bw.writeU32(this.time);
bw.writeU32(this.bits);
bw.writeU32(this.nonce);
bw.writeU32(this.height);
bw.writeBytes(this.chainwork.toArrayLike(Buffer, 'le', 32));
return bw.render();
};
/**
* Inject properties from serialized data.
* @private
* @param {Buffer} data
*/
ChainEntry.prototype.fromRaw = function fromRaw(data) {
const br = new BufferReader(data, true);
const hash = hash256.digest(br.readBytes(80));
br.seek(-80);
this.hash = hash.toString('hex');
this.version = br.readU32();
this.prevBlock = br.readHash('hex');
this.merkleRoot = br.readHash('hex');
this.time = br.readU32();
this.bits = br.readU32();
this.nonce = br.readU32();
this.height = br.readU32();
this.chainwork = new BN(br.readBytes(32), 'le');
return this;
};
/**
* Deserialize the entry.
* @param {Buffer} data
* @returns {ChainEntry}
*/
ChainEntry.fromRaw = function fromRaw(data) {
return new ChainEntry().fromRaw(data);
};
/**
* Serialize the entry to an object more
* suitable for JSON serialization.
* @returns {Object}
*/
ChainEntry.prototype.toJSON = function toJSON() {
return {
hash: encoding.revHex(this.hash),
version: this.version,
prevBlock: encoding.revHex(this.prevBlock),
merkleRoot: encoding.revHex(this.merkleRoot),
time: this.time,
bits: this.bits,
nonce: this.nonce,
height: this.height,
chainwork: this.chainwork.toString('hex', 64)
};
};
/**
* Inject properties from json object.
* @private
* @param {Object} json
*/
ChainEntry.prototype.fromJSON = function fromJSON(json) {
assert(json, 'Block data is required.');
assert(typeof json.hash === 'string');
assert((json.version >>> 0) === json.version);
assert(typeof json.prevBlock === 'string');
assert(typeof json.merkleRoot === 'string');
assert((json.time >>> 0) === json.time);
assert((json.bits >>> 0) === json.bits);
assert((json.nonce >>> 0) === json.nonce);
assert(typeof json.chainwork === 'string');
this.hash = encoding.revHex(json.hash);
this.version = json.version;
this.prevBlock = encoding.revHex(json.prevBlock);
this.merkleRoot = encoding.revHex(json.merkleRoot);
this.time = json.time;
this.bits = json.bits;
this.nonce = json.nonce;
this.height = json.height;
this.chainwork = new BN(json.chainwork, 'hex');
return this;
};
/**
* Instantiate block from jsonified object.
* @param {Object} json
* @returns {ChainEntry}
*/
ChainEntry.fromJSON = function fromJSON(json) {
return new ChainEntry().fromJSON(json);
};
/**
* Convert the entry to a headers object.
* @returns {Headers}
*/
ChainEntry.prototype.toHeaders = function toHeaders() {
return Headers.fromEntry(this);
};
/**
* Convert the entry to an inv item.
* @returns {InvItem}
*/
ChainEntry.prototype.toInv = function toInv() {
return new InvItem(InvItem.types.BLOCK, this.hash);
};
/**
* Return a more user-friendly object.
* @returns {Object}
*/
ChainEntry.prototype.inspect = function inspect() {
const json = this.toJSON();
json.version = json.version.toString(16);
return json;
};
/**
* Test whether an object is a {@link ChainEntry}.
* @param {Object} obj
* @returns {Boolean}
*/
ChainEntry.isChainEntry = function isChainEntry(obj) {
return obj instanceof ChainEntry;
};
/*
* Expose
*/

View File

@ -22,9 +22,9 @@ const NUM_FLAGS = 1;
const MAX_HEIGHT = ((1 << (32 - NUM_FLAGS)) >>> 0) - 1;
/**
* Coin Entry
* Represents an unspent output.
* @alias module:coins.CoinEntry
* @constructor
* @property {Number} version - Transaction version.
* @property {Number} height - Transaction height (-1 if unconfirmed).
* @property {Boolean} coinbase - Whether the containing
@ -34,241 +34,245 @@ const MAX_HEIGHT = ((1 << (32 - NUM_FLAGS)) >>> 0) - 1;
* @property {Buffer} raw
*/
function CoinEntry() {
if (!(this instanceof CoinEntry))
return new CoinEntry();
class CoinEntry {
/**
* Create a coin entry.
* @constructor
*/
this.version = 1;
this.height = -1;
this.coinbase = false;
this.output = new Output();
this.spent = false;
this.raw = null;
}
constructor() {
this.version = 1;
this.height = -1;
this.coinbase = false;
this.output = new Output();
this.spent = false;
this.raw = null;
}
/**
* Convert coin entry to an output.
* @returns {Output}
*/
/**
* Convert coin entry to an output.
* @returns {Output}
*/
CoinEntry.prototype.toOutput = function toOutput() {
return this.output;
};
toOutput() {
return this.output;
}
/**
* Convert coin entry to a coin.
* @param {Outpoint} prevout
* @returns {Coin}
*/
/**
* Convert coin entry to a coin.
* @param {Outpoint} prevout
* @returns {Coin}
*/
CoinEntry.prototype.toCoin = function toCoin(prevout) {
const coin = new Coin();
coin.version = this.version;
coin.height = this.height;
coin.coinbase = this.coinbase;
coin.script = this.output.script;
coin.value = this.output.value;
coin.hash = prevout.hash;
coin.index = prevout.index;
return coin;
};
toCoin(prevout) {
const coin = new Coin();
coin.version = this.version;
coin.height = this.height;
coin.coinbase = this.coinbase;
coin.script = this.output.script;
coin.value = this.output.value;
coin.hash = prevout.hash;
coin.index = prevout.index;
return coin;
}
/**
* Inject properties from TX.
* @param {TX} tx
* @param {Number} index
*/
/**
* Inject properties from TX.
* @param {TX} tx
* @param {Number} index
*/
CoinEntry.prototype.fromOutput = function fromOutput(output) {
this.output = output;
return this;
};
fromOutput(output) {
this.output = output;
return this;
}
/**
* Instantiate a coin from a TX
* @param {TX} tx
* @param {Number} index - Output index.
* @returns {CoinEntry}
*/
/**
* Instantiate a coin from a TX
* @param {TX} tx
* @param {Number} index - Output index.
* @returns {CoinEntry}
*/
CoinEntry.fromOutput = function fromOutput(output) {
return new CoinEntry().fromOutput(output);
};
static fromOutput(output) {
return new this().fromOutput(output);
}
/**
* Inject properties from TX.
* @param {TX} tx
* @param {Number} index
*/
/**
* Inject properties from TX.
* @param {TX} tx
* @param {Number} index
*/
CoinEntry.prototype.fromCoin = function fromCoin(coin) {
this.version = coin.version;
this.height = coin.height;
this.coinbase = coin.coinbase;
this.output.script = coin.script;
this.output.value = coin.value;
return this;
};
fromCoin(coin) {
this.version = coin.version;
this.height = coin.height;
this.coinbase = coin.coinbase;
this.output.script = coin.script;
this.output.value = coin.value;
return this;
}
/**
* Instantiate a coin from a TX
* @param {TX} tx
* @param {Number} index - Output index.
* @returns {CoinEntry}
*/
/**
* Instantiate a coin from a TX
* @param {TX} tx
* @param {Number} index - Output index.
* @returns {CoinEntry}
*/
CoinEntry.fromCoin = function fromCoin(coin) {
return new CoinEntry().fromCoin(coin);
};
static fromCoin(coin) {
return new this().fromCoin(coin);
}
/**
* Inject properties from TX.
* @param {TX} tx
* @param {Number} index
*/
/**
* Inject properties from TX.
* @param {TX} tx
* @param {Number} index
*/
CoinEntry.prototype.fromTX = function fromTX(tx, index, height) {
assert(typeof index === 'number');
assert(typeof height === 'number');
assert(index >= 0 && index < tx.outputs.length);
this.version = tx.version;
this.height = height;
this.coinbase = tx.isCoinbase();
this.output = tx.outputs[index];
return this;
};
fromTX(tx, index, height) {
assert(typeof index === 'number');
assert(typeof height === 'number');
assert(index >= 0 && index < tx.outputs.length);
this.version = tx.version;
this.height = height;
this.coinbase = tx.isCoinbase();
this.output = tx.outputs[index];
return this;
}
/**
* Instantiate a coin from a TX
* @param {TX} tx
* @param {Number} index - Output index.
* @returns {CoinEntry}
*/
/**
* Instantiate a coin from a TX
* @param {TX} tx
* @param {Number} index - Output index.
* @returns {CoinEntry}
*/
CoinEntry.fromTX = function fromTX(tx, index, height) {
return new CoinEntry().fromTX(tx, index, height);
};
static fromTX(tx, index, height) {
return new this().fromTX(tx, index, height);
}
/**
* Calculate size of coin.
* @returns {Number}
*/
/**
* Calculate size of coin.
* @returns {Number}
*/
CoinEntry.prototype.getSize = function getSize() {
if (this.raw)
return this.raw.length;
getSize() {
if (this.raw)
return this.raw.length;
let size = 0;
size += encoding.sizeVarint(this.version);
size += 4;
size += compress.size(this.output);
let size = 0;
size += encoding.sizeVarint(this.version);
size += 4;
size += compress.size(this.output);
return size;
};
return size;
}
/**
* Write the coin to a buffer writer.
* @param {BufferWriter} bw
*/
/**
* Write the coin to a buffer writer.
* @param {BufferWriter} bw
*/
toWriter(bw) {
if (this.raw) {
bw.writeBytes(this.raw);
return bw;
}
let height = this.height;
let field = 0;
if (this.coinbase)
field |= 1;
if (height === -1)
height = MAX_HEIGHT;
field |= height << NUM_FLAGS;
bw.writeVarint(this.version);
bw.writeU32(field);
compress.pack(this.output, bw);
CoinEntry.prototype.toWriter = function toWriter(bw) {
if (this.raw) {
bw.writeBytes(this.raw);
return bw;
}
let height = this.height;
let field = 0;
/**
* Serialize the coin.
* @returns {Buffer}
*/
if (this.coinbase)
field |= 1;
toRaw() {
if (this.raw)
return this.raw;
if (height === -1)
height = MAX_HEIGHT;
const size = this.getSize();
const bw = new StaticWriter(size);
field |= height << NUM_FLAGS;
this.toWriter(bw);
bw.writeVarint(this.version);
bw.writeU32(field);
compress.pack(this.output, bw);
this.raw = bw.render();
return bw;
};
/**
* Serialize the coin.
* @returns {Buffer}
*/
CoinEntry.prototype.toRaw = function toRaw() {
if (this.raw)
return this.raw;
}
const size = this.getSize();
const bw = new StaticWriter(size);
/**
* Inject properties from serialized buffer writer.
* @private
* @param {BufferReader} br
*/
this.toWriter(bw);
fromReader(br) {
const version = br.readVarint();
const field = br.readU32();
this.raw = bw.render();
let height = field >>> NUM_FLAGS;
return this.raw;
};
if (height === MAX_HEIGHT)
height = -1;
/**
* Inject properties from serialized buffer writer.
* @private
* @param {BufferReader} br
*/
this.version = version;
this.coinbase = (field & 1) !== 0;
this.height = height;
CoinEntry.prototype.fromReader = function fromReader(br) {
const version = br.readVarint();
const field = br.readU32();
compress.unpack(this.output, br);
let height = field >>> NUM_FLAGS;
return this;
}
if (height === MAX_HEIGHT)
height = -1;
/**
* Instantiate a coin from a serialized Buffer.
* @param {Buffer} data
* @returns {CoinEntry}
*/
this.version = version;
this.coinbase = (field & 1) !== 0;
this.height = height;
static fromReader(data) {
return new this().fromReader(data);
}
compress.unpack(this.output, br);
/**
* Inject properties from serialized data.
* @private
* @param {Buffer} data
*/
return this;
};
fromRaw(data) {
this.fromReader(new BufferReader(data));
this.raw = data;
return this;
}
/**
* Instantiate a coin from a serialized Buffer.
* @param {Buffer} data
* @returns {CoinEntry}
*/
/**
* Instantiate a coin from a serialized Buffer.
* @param {Buffer} data
* @returns {CoinEntry}
*/
CoinEntry.fromReader = function fromReader(data) {
return new CoinEntry().fromReader(data);
};
/**
* Inject properties from serialized data.
* @private
* @param {Buffer} data
*/
CoinEntry.prototype.fromRaw = function fromRaw(data) {
this.fromReader(new BufferReader(data));
this.raw = data;
return this;
};
/**
* Instantiate a coin from a serialized Buffer.
* @param {Buffer} data
* @returns {CoinEntry}
*/
CoinEntry.fromRaw = function fromRaw(data) {
return new CoinEntry().fromRaw(data);
};
static fromRaw(data) {
return new this().fromRaw(data);
}
}
/*
* Expose

View File

@ -10,209 +10,213 @@ const assert = require('assert');
const CoinEntry = require('./coinentry');
/**
* Coins
* Represents the outputs for a single transaction.
* @alias module:coins.Coins
* @constructor
* @property {Map[]} outputs - Coins.
*/
function Coins() {
if (!(this instanceof Coins))
return new Coins();
class Coins {
/**
* Create coins.
* @constructor
*/
this.outputs = new Map();
}
/**
* Add a single entry to the collection.
* @param {Number} index
* @param {CoinEntry} coin
* @returns {CoinEntry}
*/
Coins.prototype.add = function add(index, coin) {
assert((index >>> 0) === index);
assert(coin);
this.outputs.set(index, coin);
return coin;
};
/**
* Add a single output to the collection.
* @param {Number} index
* @param {Output} output
* @returns {CoinEntry}
*/
Coins.prototype.addOutput = function addOutput(index, output) {
return this.add(index, CoinEntry.fromOutput(output));
};
/**
* Add an output to the collection by output index.
* @param {TX} tx
* @param {Number} index
* @param {Number} height
* @returns {CoinEntry}
*/
Coins.prototype.addIndex = function addIndex(tx, index, height) {
return this.add(index, CoinEntry.fromTX(tx, index, height));
};
/**
* Add a single coin to the collection.
* @param {Coin} coin
* @returns {CoinEntry}
*/
Coins.prototype.addCoin = function addCoin(coin) {
return this.add(coin.index, CoinEntry.fromCoin(coin));
};
/**
* Test whether the collection has a coin.
* @param {Number} index
* @returns {Boolean}
*/
Coins.prototype.has = function has(index) {
return this.outputs.has(index);
};
/**
* Test whether the collection has an unspent coin.
* @param {Number} index
* @returns {Boolean}
*/
Coins.prototype.isUnspent = function isUnspent(index) {
const coin = this.outputs.get(index);
if (!coin || coin.spent)
return false;
return true;
};
/**
* Get a coin entry.
* @param {Number} index
* @returns {CoinEntry|null}
*/
Coins.prototype.get = function get(index) {
return this.outputs.get(index) || null;
};
/**
* Get an output.
* @param {Number} index
* @returns {Output|null}
*/
Coins.prototype.getOutput = function getOutput(index) {
const coin = this.outputs.get(index);
if (!coin)
return null;
return coin.output;
};
/**
* Get a coin.
* @param {Outpoint} prevout
* @returns {Coin|null}
*/
Coins.prototype.getCoin = function getCoin(prevout) {
const coin = this.outputs.get(prevout.index);
if (!coin)
return null;
return coin.toCoin(prevout);
};
/**
* Spend a coin entry and return it.
* @param {Number} index
* @returns {CoinEntry|null}
*/
Coins.prototype.spend = function spend(index) {
const coin = this.get(index);
if (!coin || coin.spent)
return null;
coin.spent = true;
return coin;
};
/**
* Remove a coin entry and return it.
* @param {Number} index
* @returns {CoinEntry|null}
*/
Coins.prototype.remove = function remove(index) {
const coin = this.get(index);
if (!coin)
return null;
this.outputs.delete(index);
return coin;
};
/**
* Test whether the coins are fully spent.
* @returns {Boolean}
*/
Coins.prototype.isEmpty = function isEmpty() {
return this.outputs.size === 0;
};
/**
* Inject properties from tx.
* @private
* @param {TX} tx
* @param {Number} height
* @returns {Coins}
*/
Coins.prototype.fromTX = function fromTX(tx, height) {
assert(typeof height === 'number');
for (let i = 0; i < tx.outputs.length; i++) {
const output = tx.outputs[i];
if (output.script.isUnspendable())
continue;
const entry = CoinEntry.fromTX(tx, i, height);
this.outputs.set(i, entry);
constructor() {
this.outputs = new Map();
}
return this;
};
/**
* Add a single entry to the collection.
* @param {Number} index
* @param {CoinEntry} coin
* @returns {CoinEntry}
*/
/**
* Instantiate a coins object from a transaction.
* @param {TX} tx
* @param {Number} height
* @returns {Coins}
*/
add(index, coin) {
assert((index >>> 0) === index);
assert(coin);
this.outputs.set(index, coin);
return coin;
}
Coins.fromTX = function fromTX(tx, height) {
return new Coins().fromTX(tx, height);
};
/**
* Add a single output to the collection.
* @param {Number} index
* @param {Output} output
* @returns {CoinEntry}
*/
addOutput(index, output) {
return this.add(index, CoinEntry.fromOutput(output));
}
/**
* Add an output to the collection by output index.
* @param {TX} tx
* @param {Number} index
* @param {Number} height
* @returns {CoinEntry}
*/
addIndex(tx, index, height) {
return this.add(index, CoinEntry.fromTX(tx, index, height));
}
/**
* Add a single coin to the collection.
* @param {Coin} coin
* @returns {CoinEntry}
*/
addCoin(coin) {
return this.add(coin.index, CoinEntry.fromCoin(coin));
}
/**
* Test whether the collection has a coin.
* @param {Number} index
* @returns {Boolean}
*/
has(index) {
return this.outputs.has(index);
}
/**
* Test whether the collection has an unspent coin.
* @param {Number} index
* @returns {Boolean}
*/
isUnspent(index) {
const coin = this.outputs.get(index);
if (!coin || coin.spent)
return false;
return true;
}
/**
* Get a coin entry.
* @param {Number} index
* @returns {CoinEntry|null}
*/
get(index) {
return this.outputs.get(index) || null;
}
/**
* Get an output.
* @param {Number} index
* @returns {Output|null}
*/
getOutput(index) {
const coin = this.outputs.get(index);
if (!coin)
return null;
return coin.output;
}
/**
* Get a coin.
* @param {Outpoint} prevout
* @returns {Coin|null}
*/
getCoin(prevout) {
const coin = this.outputs.get(prevout.index);
if (!coin)
return null;
return coin.toCoin(prevout);
}
/**
* Spend a coin entry and return it.
* @param {Number} index
* @returns {CoinEntry|null}
*/
spend(index) {
const coin = this.get(index);
if (!coin || coin.spent)
return null;
coin.spent = true;
return coin;
}
/**
* Remove a coin entry and return it.
* @param {Number} index
* @returns {CoinEntry|null}
*/
remove(index) {
const coin = this.get(index);
if (!coin)
return null;
this.outputs.delete(index);
return coin;
}
/**
* Test whether the coins are fully spent.
* @returns {Boolean}
*/
isEmpty() {
return this.outputs.size === 0;
}
/**
* Inject properties from tx.
* @private
* @param {TX} tx
* @param {Number} height
* @returns {Coins}
*/
fromTX(tx, height) {
assert(typeof height === 'number');
for (let i = 0; i < tx.outputs.length; i++) {
const output = tx.outputs[i];
if (output.script.isUnspendable())
continue;
const entry = CoinEntry.fromTX(tx, i, height);
this.outputs.set(i, entry);
}
return this;
}
/**
* Instantiate a coins object from a transaction.
* @param {TX} tx
* @param {Number} height
* @returns {Coins}
*/
static fromTX(tx, height) {
return new this().fromTX(tx, height);
}
}
/*
* Expose

File diff suppressed because it is too large Load Diff

View File

@ -12,127 +12,130 @@ const StaticWriter = require('bufio/lib/staticwriter');
const CoinEntry = require('../coins/coinentry');
/**
* UndoCoins
* Undo Coins
* Coins need to be resurrected from somewhere
* during a reorg. The undo coins store all
* spent coins in a single record per block
* (in a compressed format).
* @alias module:coins.UndoCoins
* @constructor
* @property {UndoCoin[]} items
*/
function UndoCoins() {
if (!(this instanceof UndoCoins))
return new UndoCoins();
class UndoCoins {
/**
* Create undo coins.
* @constructor
*/
this.items = [];
constructor() {
this.items = [];
}
/**
* Push coin entry onto undo coin array.
* @param {CoinEntry}
* @returns {Number}
*/
push(coin) {
return this.items.push(coin);
}
/**
* Calculate undo coins size.
* @returns {Number}
*/
getSize() {
let size = 0;
size += 4;
for (const coin of this.items)
size += coin.getSize();
return size;
}
/**
* Serialize all undo coins.
* @returns {Buffer}
*/
toRaw() {
const size = this.getSize();
const bw = new StaticWriter(size);
bw.writeU32(this.items.length);
for (const coin of this.items)
coin.toWriter(bw);
return bw.render();
}
/**
* Inject properties from serialized data.
* @private
* @param {Buffer} data
* @returns {UndoCoins}
*/
fromRaw(data) {
const br = new BufferReader(data);
const count = br.readU32();
for (let i = 0; i < count; i++)
this.items.push(CoinEntry.fromReader(br));
return this;
}
/**
* Instantiate undo coins from serialized data.
* @param {Buffer} data
* @returns {UndoCoins}
*/
static fromRaw(data) {
return new this().fromRaw(data);
}
/**
* Test whether the undo coins have any members.
* @returns {Boolean}
*/
isEmpty() {
return this.items.length === 0;
}
/**
* Render the undo coins.
* @returns {Buffer}
*/
commit() {
const raw = this.toRaw();
this.items.length = 0;
return raw;
}
/**
* Re-apply undo coins to a view, effectively unspending them.
* @param {CoinView} view
* @param {Outpoint} prevout
*/
apply(view, prevout) {
const undo = this.items.pop();
assert(undo);
view.addEntry(prevout, undo);
}
}
/**
* Push coin entry onto undo coin array.
* @param {CoinEntry}
* @returns {Number}
*/
UndoCoins.prototype.push = function push(coin) {
return this.items.push(coin);
};
/**
* Calculate undo coins size.
* @returns {Number}
*/
UndoCoins.prototype.getSize = function getSize() {
let size = 0;
size += 4;
for (const coin of this.items)
size += coin.getSize();
return size;
};
/**
* Serialize all undo coins.
* @returns {Buffer}
*/
UndoCoins.prototype.toRaw = function toRaw() {
const size = this.getSize();
const bw = new StaticWriter(size);
bw.writeU32(this.items.length);
for (const coin of this.items)
coin.toWriter(bw);
return bw.render();
};
/**
* Inject properties from serialized data.
* @private
* @param {Buffer} data
* @returns {UndoCoins}
*/
UndoCoins.prototype.fromRaw = function fromRaw(data) {
const br = new BufferReader(data);
const count = br.readU32();
for (let i = 0; i < count; i++)
this.items.push(CoinEntry.fromReader(br));
return this;
};
/**
* Instantiate undo coins from serialized data.
* @param {Buffer} data
* @returns {UndoCoins}
*/
UndoCoins.fromRaw = function fromRaw(data) {
return new UndoCoins().fromRaw(data);
};
/**
* Test whether the undo coins have any members.
* @returns {Boolean}
*/
UndoCoins.prototype.isEmpty = function isEmpty() {
return this.items.length === 0;
};
/**
* Render the undo coins.
* @returns {Buffer}
*/
UndoCoins.prototype.commit = function commit() {
const raw = this.toRaw();
this.items.length = 0;
return raw;
};
/**
* Re-apply undo coins to a view, effectively unspending them.
* @param {CoinView} view
* @param {Outpoint} prevout
*/
UndoCoins.prototype.apply = function apply(view, prevout) {
const undo = this.items.pop();
assert(undo);
view.addEntry(prevout, undo);
};
/*
* Expose
*/

View File

@ -221,7 +221,7 @@ Miner.prototype.mineBlock = function mineBlock(tip, address) {
*/
Miner.prototype.addAddress = function addAddress(address) {
this.addresses.push(Address(address));
this.addresses.push(new Address(address));
};
/**

View File

@ -512,7 +512,7 @@ BlockTemplate.prototype.getDifficulty = function getDifficulty() {
*/
BlockTemplate.prototype.setAddress = function setAddress(address) {
this.address = Address(address);
this.address = new Address(address);
this.refresh();
};

File diff suppressed because it is too large Load Diff

View File

@ -11,249 +11,252 @@ const assert = require('assert');
const hash256 = require('bcrypto/lib/hash256');
const BufferReader = require('bufio/lib/reader');
const StaticWriter = require('bufio/lib/staticwriter');
const InvItem = require('./invitem');
const encoding = require('bufio/lib/encoding');
const InvItem = require('./invitem');
const consensus = require('../protocol/consensus');
/**
* Abstract Block
* The class which all block-like objects inherit from.
* @alias module:primitives.AbstractBlock
* @constructor
* @abstract
* @property {Number} version - Block version. Note
* that Bcoin reads versions as unsigned despite
* them being signed on the protocol level. This
* number will never be negative.
* @property {Hash} prevBlock - Previous block hash.
* @property {Hash} merkleRoot - Merkle root hash.
* @property {Number} time - Timestamp.
* @property {Number} version
* @property {Hash} prevBlock
* @property {Hash} merkleRoot
* @property {Number} time
* @property {Number} bits
* @property {Number} nonce
*/
function AbstractBlock() {
if (!(this instanceof AbstractBlock))
return new AbstractBlock();
class AbstractBlock {
/**
* Create an abstract block.
* @constructor
*/
this.version = 1;
this.prevBlock = encoding.NULL_HASH;
this.merkleRoot = encoding.NULL_HASH;
this.time = 0;
this.bits = 0;
this.nonce = 0;
constructor() {
this.version = 1;
this.prevBlock = encoding.NULL_HASH;
this.merkleRoot = encoding.NULL_HASH;
this.time = 0;
this.bits = 0;
this.nonce = 0;
this.mutable = false;
this.mutable = false;
this._hash = null;
this._hhash = null;
}
/**
* Inject properties from options object.
* @private
* @param {NakedBlock} options
*/
AbstractBlock.prototype.parseOptions = function parseOptions(options) {
assert(options, 'Block data is required.');
assert((options.version >>> 0) === options.version);
assert(typeof options.prevBlock === 'string');
assert(typeof options.merkleRoot === 'string');
assert((options.time >>> 0) === options.time);
assert((options.bits >>> 0) === options.bits);
assert((options.nonce >>> 0) === options.nonce);
this.version = options.version;
this.prevBlock = options.prevBlock;
this.merkleRoot = options.merkleRoot;
this.time = options.time;
this.bits = options.bits;
this.nonce = options.nonce;
if (options.mutable != null)
this.mutable = Boolean(options.mutable);
return this;
};
/**
* Inject properties from json object.
* @private
* @param {Object} json
*/
AbstractBlock.prototype.parseJSON = function parseJSON(json) {
assert(json, 'Block data is required.');
assert((json.version >>> 0) === json.version);
assert(typeof json.prevBlock === 'string');
assert(typeof json.merkleRoot === 'string');
assert((json.time >>> 0) === json.time);
assert((json.bits >>> 0) === json.bits);
assert((json.nonce >>> 0) === json.nonce);
this.version = json.version;
this.prevBlock = encoding.revHex(json.prevBlock);
this.merkleRoot = encoding.revHex(json.merkleRoot);
this.time = json.time;
this.bits = json.bits;
this.nonce = json.nonce;
return this;
};
/**
* Test whether the block is a memblock.
* @returns {Boolean}
*/
AbstractBlock.prototype.isMemory = function isMemory() {
return false;
};
/**
* Clear any cached values (abstract).
*/
AbstractBlock.prototype._refresh = function _refresh() {
this._hash = null;
this._hhash = null;
};
/**
* Clear any cached values.
*/
AbstractBlock.prototype.refresh = function refresh() {
return this._refresh();
};
/**
* Hash the block headers.
* @param {String?} enc - Can be `'hex'` or `null`.
* @returns {Hash|Buffer} hash
*/
AbstractBlock.prototype.hash = function hash(enc) {
let h = this._hash;
if (!h) {
h = hash256.digest(this.toHead());
if (!this.mutable)
this._hash = h;
this._hash = null;
this._hhash = null;
}
if (enc === 'hex') {
let hex = this._hhash;
if (!hex) {
hex = h.toString('hex');
if (!this.mutable)
this._hhash = hex;
/**
* Inject properties from options object.
* @private
* @param {NakedBlock} options
*/
parseOptions(options) {
assert(options, 'Block data is required.');
assert((options.version >>> 0) === options.version);
assert(typeof options.prevBlock === 'string');
assert(typeof options.merkleRoot === 'string');
assert((options.time >>> 0) === options.time);
assert((options.bits >>> 0) === options.bits);
assert((options.nonce >>> 0) === options.nonce);
this.version = options.version;
this.prevBlock = options.prevBlock;
this.merkleRoot = options.merkleRoot;
this.time = options.time;
this.bits = options.bits;
this.nonce = options.nonce;
if (options.mutable != null) {
assert(typeof options.mutable === 'boolean');
this.mutable = options.mutable;
}
h = hex;
return this;
}
return h;
};
/**
* Inject properties from json object.
* @private
* @param {Object} json
*/
/**
* Serialize the block headers.
* @returns {Buffer}
*/
parseJSON(json) {
assert(json, 'Block data is required.');
assert((json.version >>> 0) === json.version);
assert(typeof json.prevBlock === 'string');
assert(typeof json.merkleRoot === 'string');
assert((json.time >>> 0) === json.time);
assert((json.bits >>> 0) === json.bits);
assert((json.nonce >>> 0) === json.nonce);
AbstractBlock.prototype.toHead = function toHead() {
return this.writeHead(new StaticWriter(80)).render();
};
this.version = json.version;
this.prevBlock = encoding.revHex(json.prevBlock);
this.merkleRoot = encoding.revHex(json.merkleRoot);
this.time = json.time;
this.bits = json.bits;
this.nonce = json.nonce;
/**
* Inject properties from serialized data.
* @private
* @param {Buffer} data
*/
return this;
}
AbstractBlock.prototype.fromHead = function fromHead(data) {
return this.readHead(new BufferReader(data));
};
/**
* Test whether the block is a memblock.
* @returns {Boolean}
*/
/**
* Serialize the block headers.
* @param {BufferWriter} bw
*/
AbstractBlock.prototype.writeHead = function writeHead(bw) {
bw.writeU32(this.version);
bw.writeHash(this.prevBlock);
bw.writeHash(this.merkleRoot);
bw.writeU32(this.time);
bw.writeU32(this.bits);
bw.writeU32(this.nonce);
return bw;
};
/**
* Parse the block headers.
* @param {BufferReader} br
*/
AbstractBlock.prototype.readHead = function readHead(br) {
this.version = br.readU32();
this.prevBlock = br.readHash('hex');
this.merkleRoot = br.readHash('hex');
this.time = br.readU32();
this.bits = br.readU32();
this.nonce = br.readU32();
return this;
};
/**
* Verify the block.
* @returns {Boolean}
*/
AbstractBlock.prototype.verify = function verify() {
if (!this.verifyPOW())
isMemory() {
return false;
}
if (!this.verifyBody())
return false;
/**
* Clear any cached values (abstract).
*/
return true;
};
_refresh() {
this._hash = null;
this._hhash = null;
}
/**
* Verify proof-of-work.
* @returns {Boolean}
*/
/**
* Clear any cached values.
*/
AbstractBlock.prototype.verifyPOW = function verifyPOW() {
return consensus.verifyPOW(this.hash(), this.bits);
};
refresh() {
return this._refresh();
}
/**
* Verify the block.
* @returns {Boolean}
*/
/**
* Hash the block headers.
* @param {String?} enc - Can be `'hex'` or `null`.
* @returns {Hash|Buffer} hash
*/
AbstractBlock.prototype.verifyBody = function verifyBody() {
throw new Error('Abstract method.');
};
hash(enc) {
let h = this._hash;
/**
* Get little-endian block hash.
* @returns {Hash}
*/
if (!h) {
h = hash256.digest(this.toHead());
if (!this.mutable)
this._hash = h;
}
AbstractBlock.prototype.rhash = function rhash() {
return encoding.revHex(this.hash('hex'));
};
if (enc === 'hex') {
let hex = this._hhash;
if (!hex) {
hex = h.toString('hex');
if (!this.mutable)
this._hhash = hex;
}
h = hex;
}
/**
* Convert the block to an inv item.
* @returns {InvItem}
*/
return h;
}
AbstractBlock.prototype.toInv = function toInv() {
return new InvItem(InvItem.types.BLOCK, this.hash('hex'));
};
/**
* Serialize the block headers.
* @returns {Buffer}
*/
toHead() {
return this.writeHead(new StaticWriter(80)).render();
}
/**
* Inject properties from serialized data.
* @private
* @param {Buffer} data
*/
fromHead(data) {
return this.readHead(new BufferReader(data));
}
/**
* Serialize the block headers.
* @param {BufferWriter} bw
*/
writeHead(bw) {
bw.writeU32(this.version);
bw.writeHash(this.prevBlock);
bw.writeHash(this.merkleRoot);
bw.writeU32(this.time);
bw.writeU32(this.bits);
bw.writeU32(this.nonce);
return bw;
}
/**
* Parse the block headers.
* @param {BufferReader} br
*/
readHead(br) {
this.version = br.readU32();
this.prevBlock = br.readHash('hex');
this.merkleRoot = br.readHash('hex');
this.time = br.readU32();
this.bits = br.readU32();
this.nonce = br.readU32();
return this;
}
/**
* Verify the block.
* @returns {Boolean}
*/
verify() {
if (!this.verifyPOW())
return false;
if (!this.verifyBody())
return false;
return true;
}
/**
* Verify proof-of-work.
* @returns {Boolean}
*/
verifyPOW() {
return consensus.verifyPOW(this.hash(), this.bits);
}
/**
* Verify the block.
* @returns {Boolean}
*/
verifyBody() {
throw new Error('Abstract method.');
}
/**
* Get little-endian block hash.
* @returns {Hash}
*/
rhash() {
return encoding.revHex(this.hash('hex'));
}
/**
* Convert the block to an inv item.
* @returns {InvItem}
*/
toInv() {
return new InvItem(InvItem.types.BLOCK, this.hash('hex'));
}
}
/*
* Expose

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -8,427 +8,429 @@
'use strict';
const assert = require('assert');
const BufferReader = require('bufio/lib/reader');
const StaticWriter = require('bufio/lib/staticwriter');
const encoding = require('bufio/lib/encoding');
const Amount = require('../btc/amount');
const Output = require('./output');
const Script = require('../script/script');
const Network = require('../protocol/network');
const BufferReader = require('bufio/lib/reader');
const StaticWriter = require('bufio/lib/staticwriter');
const encoding = require('bufio/lib/encoding');
/**
* Coin
* Represents an unspent output.
* @alias module:primitives.Coin
* @constructor
* @extends Output
* @param {NakedCoin|Coin} options
* @property {Number} version - Transaction version.
* @property {Number} height - Transaction height (-1 if unconfirmed).
* @property {Amount} value - Output value in satoshis.
* @property {Script} script - Output script.
* @property {Boolean} coinbase - Whether the containing
* transaction is a coinbase.
* @property {Hash} hash - Transaction hash.
* @property {Number} index - Output index.
* @property {Number} version
* @property {Number} height
* @property {Amount} value
* @property {Script} script
* @property {Boolean} coinbase
* @property {Hash} hash
* @property {Number} index
*/
function Coin(options) {
if (!(this instanceof Coin))
return new Coin(options);
class Coin extends Output {
/**
* Create a coin.
* @constructor
* @param {Object} options
*/
this.version = 1;
this.height = -1;
this.value = 0;
this.script = new Script();
this.coinbase = false;
this.hash = encoding.NULL_HASH;
this.index = 0;
constructor(options) {
super();
if (options)
this.fromOptions(options);
}
Object.setPrototypeOf(Coin.prototype, Output.prototype);
/**
* Inject options into coin.
* @private
* @param {Object} options
*/
Coin.prototype.fromOptions = function fromOptions(options) {
assert(options, 'Coin data is required.');
if (options.version != null) {
assert((options.version >>> 0) === options.version,
'Version must be a uint32.');
this.version = options.version;
}
if (options.height != null) {
if (options.height !== -1) {
assert((options.height >>> 0) === options.height,
'Height must be a uint32.');
this.height = options.height;
} else {
this.height = -1;
}
}
if (options.value != null) {
assert(Number.isSafeInteger(options.value) && options.value >= 0,
'Value must be a uint64.');
this.value = options.value;
}
if (options.script)
this.script.fromOptions(options.script);
if (options.coinbase != null) {
assert(typeof options.coinbase === 'boolean',
'Coinbase must be a boolean.');
this.coinbase = options.coinbase;
}
if (options.hash != null) {
assert(typeof options.hash === 'string', 'Hash must be a string.');
this.hash = options.hash;
}
if (options.index != null) {
assert((options.index >>> 0) === options.index, 'Index must be a uint32.');
this.index = options.index;
}
return this;
};
/**
* Instantiate Coin from options object.
* @private
* @param {Object} options
*/
Coin.fromOptions = function fromOptions(options) {
return new Coin().fromOptions(options);
};
/**
* Clone the coin.
* @private
* @returns {Coin}
*/
Coin.prototype.clone = function clone() {
assert(false, 'Coins are not cloneable.');
};
/**
* Calculate number of confirmations since coin was created.
* @param {Number?} height - Current chain height. Network
* height is used if not passed in.
* @return {Number}
*/
Coin.prototype.getDepth = function getDepth(height) {
assert(typeof height === 'number', 'Must pass a height.');
if (this.height === -1)
return 0;
if (height === -1)
return 0;
if (height < this.height)
return 0;
return height - this.height + 1;
};
/**
* Serialize coin to a key
* suitable for a hash table.
* @returns {String}
*/
Coin.prototype.toKey = function toKey() {
return this.hash + this.index;
};
/**
* Inject properties from hash table key.
* @private
* @param {String} key
* @returns {Coin}
*/
Coin.prototype.fromKey = function fromKey(key) {
assert(key.length > 64);
this.hash = key.slice(0, 64);
this.index = parseInt(key.slice(64), 10);
return this;
};
/**
* Instantiate coin from hash table key.
* @param {String} key
* @returns {Coin}
*/
Coin.fromKey = function fromKey(key) {
return new Coin().fromKey(key);
};
/**
* Get little-endian hash.
* @returns {Hash}
*/
Coin.prototype.rhash = function rhash() {
return encoding.revHex(this.hash);
};
/**
* Get little-endian hash.
* @returns {Hash}
*/
Coin.prototype.txid = function txid() {
return this.rhash();
};
/**
* Convert the coin to a more user-friendly object.
* @returns {Object}
*/
Coin.prototype.inspect = function inspect() {
return {
type: this.getType(),
version: this.version,
height: this.height,
value: Amount.btc(this.value),
script: this.script,
coinbase: this.coinbase,
hash: this.hash ? encoding.revHex(this.hash) : null,
index: this.index,
address: this.getAddress()
};
};
/**
* Convert the coin to an object suitable
* for JSON serialization.
* @returns {Object}
*/
Coin.prototype.toJSON = function toJSON() {
return this.getJSON();
};
/**
* Convert the coin to an object suitable
* for JSON serialization. Note that the hash
* will be reversed to abide by bitcoind's legacy
* of little-endian uint256s.
* @param {Network} network
* @param {Boolean} minimal
* @returns {Object}
*/
Coin.prototype.getJSON = function getJSON(network, minimal) {
let addr = this.getAddress();
network = Network.get(network);
if (addr)
addr = addr.toString(network);
return {
version: this.version,
height: this.height,
value: this.value,
script: this.script.toJSON(),
address: addr,
coinbase: this.coinbase,
hash: !minimal ? this.rhash() : undefined,
index: !minimal ? this.index : undefined
};
};
/**
* Inject JSON properties into coin.
* @private
* @param {Object} json
*/
Coin.prototype.fromJSON = function fromJSON(json) {
assert(json, 'Coin data required.');
assert((json.version >>> 0) === json.version, 'Version must be a uint32.');
assert(json.height === -1 || (json.height >>> 0) === json.height,
'Height must be a uint32.');
assert(Number.isSafeInteger(json.value) && json.value >= 0,
'Value must be a uint64.');
assert(typeof json.coinbase === 'boolean', 'Coinbase must be a boolean.');
this.version = json.version;
this.height = json.height;
this.value = json.value;
this.script.fromJSON(json.script);
this.coinbase = json.coinbase;
if (json.hash != null) {
assert(typeof json.hash === 'string', 'Hash must be a string.');
assert(json.hash.length === 64, 'Hash must be a string.');
assert((json.index >>> 0) === json.index, 'Index must be a uint32.');
this.hash = encoding.revHex(json.hash);
this.index = json.index;
}
return this;
};
/**
* Instantiate an Coin from a jsonified coin object.
* @param {Object} json - The jsonified coin object.
* @returns {Coin}
*/
Coin.fromJSON = function fromJSON(json) {
return new Coin().fromJSON(json);
};
/**
* Calculate size of coin.
* @returns {Number}
*/
Coin.prototype.getSize = function getSize() {
return 17 + this.script.getVarSize();
};
/**
* Write the coin to a buffer writer.
* @param {BufferWriter} bw
*/
Coin.prototype.toWriter = function toWriter(bw) {
let height = this.height;
if (height === -1)
height = 0x7fffffff;
bw.writeU32(this.version);
bw.writeU32(height);
bw.writeI64(this.value);
bw.writeVarBytes(this.script.toRaw());
bw.writeU8(this.coinbase ? 1 : 0);
return bw;
};
/**
* Serialize the coin.
* @returns {Buffer|String}
*/
Coin.prototype.toRaw = function toRaw() {
const size = this.getSize();
return this.toWriter(new StaticWriter(size)).render();
};
/**
* Inject properties from serialized buffer writer.
* @private
* @param {BufferReader} br
*/
Coin.prototype.fromReader = function fromReader(br) {
this.version = br.readU32();
this.height = br.readU32();
this.value = br.readI64();
this.script.fromRaw(br.readVarBytes());
this.coinbase = br.readU8() === 1;
if (this.height === 0x7fffffff)
this.version = 1;
this.height = -1;
this.coinbase = false;
this.hash = encoding.NULL_HASH;
this.index = 0;
return this;
};
if (options)
this.fromOptions(options);
}
/**
* Inject properties from serialized data.
* @private
* @param {Buffer} data
*/
/**
* Inject options into coin.
* @private
* @param {Object} options
*/
Coin.prototype.fromRaw = function fromRaw(data) {
return this.fromReader(new BufferReader(data));
};
fromOptions(options) {
assert(options, 'Coin data is required.');
/**
* Instantiate a coin from a buffer reader.
* @param {BufferReader} br
* @returns {Coin}
*/
if (options.version != null) {
assert((options.version >>> 0) === options.version,
'Version must be a uint32.');
this.version = options.version;
}
Coin.fromReader = function fromReader(br) {
return new Coin().fromReader(br);
};
if (options.height != null) {
if (options.height !== -1) {
assert((options.height >>> 0) === options.height,
'Height must be a uint32.');
this.height = options.height;
} else {
this.height = -1;
}
}
/**
* Instantiate a coin from a serialized Buffer.
* @param {Buffer} data
* @param {String?} enc - Encoding, can be `'hex'` or null.
* @returns {Coin}
*/
if (options.value != null) {
assert(Number.isSafeInteger(options.value) && options.value >= 0,
'Value must be a uint64.');
this.value = options.value;
}
Coin.fromRaw = function fromRaw(data, enc) {
if (typeof data === 'string')
data = Buffer.from(data, enc);
return new Coin().fromRaw(data);
};
if (options.script)
this.script.fromOptions(options.script);
/**
* Inject properties from TX.
* @param {TX} tx
* @param {Number} index
*/
if (options.coinbase != null) {
assert(typeof options.coinbase === 'boolean',
'Coinbase must be a boolean.');
this.coinbase = options.coinbase;
}
Coin.prototype.fromTX = function fromTX(tx, index, height) {
assert(typeof index === 'number');
assert(typeof height === 'number');
assert(index >= 0 && index < tx.outputs.length);
this.version = tx.version;
this.height = height;
this.value = tx.outputs[index].value;
this.script = tx.outputs[index].script;
this.coinbase = tx.isCoinbase();
this.hash = tx.hash('hex');
this.index = index;
return this;
};
if (options.hash != null) {
assert(typeof options.hash === 'string', 'Hash must be a string.');
this.hash = options.hash;
}
/**
* Instantiate a coin from a TX
* @param {TX} tx
* @param {Number} index - Output index.
* @returns {Coin}
*/
if (options.index != null) {
assert((options.index >>> 0) === options.index,
'Index must be a uint32.');
this.index = options.index;
}
Coin.fromTX = function fromTX(tx, index, height) {
return new Coin().fromTX(tx, index, height);
};
return this;
}
/**
* Test an object to see if it is a Coin.
* @param {Object} obj
* @returns {Boolean}
*/
/**
* Instantiate Coin from options object.
* @private
* @param {Object} options
*/
Coin.isCoin = function isCoin(obj) {
return obj instanceof Coin;
};
static fromOptions(options) {
return new this().fromOptions(options);
}
/**
* Clone the coin.
* @private
* @returns {Coin}
*/
clone() {
assert(false, 'Coins are not cloneable.');
}
/**
* Calculate number of confirmations since coin was created.
* @param {Number?} height - Current chain height. Network
* height is used if not passed in.
* @return {Number}
*/
getDepth(height) {
assert(typeof height === 'number', 'Must pass a height.');
if (this.height === -1)
return 0;
if (height === -1)
return 0;
if (height < this.height)
return 0;
return height - this.height + 1;
}
/**
* Serialize coin to a key
* suitable for a hash table.
* @returns {String}
*/
toKey() {
return this.hash + this.index;
}
/**
* Inject properties from hash table key.
* @private
* @param {String} key
* @returns {Coin}
*/
fromKey(key) {
assert(key.length > 64);
this.hash = key.slice(0, 64);
this.index = parseInt(key.slice(64), 10);
return this;
}
/**
* Instantiate coin from hash table key.
* @param {String} key
* @returns {Coin}
*/
static fromKey(key) {
return new this().fromKey(key);
}
/**
* Get little-endian hash.
* @returns {Hash}
*/
rhash() {
return encoding.revHex(this.hash);
}
/**
* Get little-endian hash.
* @returns {Hash}
*/
txid() {
return this.rhash();
}
/**
* Convert the coin to a more user-friendly object.
* @returns {Object}
*/
inspect() {
return {
type: this.getType(),
version: this.version,
height: this.height,
value: Amount.btc(this.value),
script: this.script,
coinbase: this.coinbase,
hash: this.hash ? encoding.revHex(this.hash) : null,
index: this.index,
address: this.getAddress()
};
}
/**
* Convert the coin to an object suitable
* for JSON serialization.
* @returns {Object}
*/
toJSON() {
return this.getJSON();
}
/**
* Convert the coin to an object suitable
* for JSON serialization. Note that the hash
* will be reversed to abide by bitcoind's legacy
* of little-endian uint256s.
* @param {Network} network
* @param {Boolean} minimal
* @returns {Object}
*/
getJSON(network, minimal) {
let addr = this.getAddress();
network = Network.get(network);
if (addr)
addr = addr.toString(network);
return {
version: this.version,
height: this.height,
value: this.value,
script: this.script.toJSON(),
address: addr,
coinbase: this.coinbase,
hash: !minimal ? this.rhash() : undefined,
index: !minimal ? this.index : undefined
};
}
/**
* Inject JSON properties into coin.
* @private
* @param {Object} json
*/
fromJSON(json) {
assert(json, 'Coin data required.');
assert((json.version >>> 0) === json.version, 'Version must be a uint32.');
assert(json.height === -1 || (json.height >>> 0) === json.height,
'Height must be a uint32.');
assert(Number.isSafeInteger(json.value) && json.value >= 0,
'Value must be a uint64.');
assert(typeof json.coinbase === 'boolean', 'Coinbase must be a boolean.');
this.version = json.version;
this.height = json.height;
this.value = json.value;
this.script.fromJSON(json.script);
this.coinbase = json.coinbase;
if (json.hash != null) {
assert(typeof json.hash === 'string', 'Hash must be a string.');
assert(json.hash.length === 64, 'Hash must be a string.');
assert((json.index >>> 0) === json.index, 'Index must be a uint32.');
this.hash = encoding.revHex(json.hash);
this.index = json.index;
}
return this;
}
/**
* Instantiate an Coin from a jsonified coin object.
* @param {Object} json - The jsonified coin object.
* @returns {Coin}
*/
static fromJSON(json) {
return new this().fromJSON(json);
}
/**
* Calculate size of coin.
* @returns {Number}
*/
getSize() {
return 17 + this.script.getVarSize();
}
/**
* Write the coin to a buffer writer.
* @param {BufferWriter} bw
*/
toWriter(bw) {
let height = this.height;
if (height === -1)
height = 0x7fffffff;
bw.writeU32(this.version);
bw.writeU32(height);
bw.writeI64(this.value);
bw.writeVarBytes(this.script.toRaw());
bw.writeU8(this.coinbase ? 1 : 0);
return bw;
}
/**
* Serialize the coin.
* @returns {Buffer|String}
*/
toRaw() {
const size = this.getSize();
return this.toWriter(new StaticWriter(size)).render();
}
/**
* Inject properties from serialized buffer writer.
* @private
* @param {BufferReader} br
*/
fromReader(br) {
this.version = br.readU32();
this.height = br.readU32();
this.value = br.readI64();
this.script.fromRaw(br.readVarBytes());
this.coinbase = br.readU8() === 1;
if (this.height === 0x7fffffff)
this.height = -1;
return this;
}
/**
* Inject properties from serialized data.
* @private
* @param {Buffer} data
*/
fromRaw(data) {
return this.fromReader(new BufferReader(data));
}
/**
* Instantiate a coin from a buffer reader.
* @param {BufferReader} br
* @returns {Coin}
*/
static fromReader(br) {
return new this().fromReader(br);
}
/**
* Instantiate a coin from a serialized Buffer.
* @param {Buffer} data
* @param {String?} enc - Encoding, can be `'hex'` or null.
* @returns {Coin}
*/
static fromRaw(data, enc) {
if (typeof data === 'string')
data = Buffer.from(data, enc);
return new this().fromRaw(data);
}
/**
* Inject properties from TX.
* @param {TX} tx
* @param {Number} index
*/
fromTX(tx, index, height) {
assert(typeof index === 'number');
assert(typeof height === 'number');
assert(index >= 0 && index < tx.outputs.length);
this.version = tx.version;
this.height = height;
this.value = tx.outputs[index].value;
this.script = tx.outputs[index].script;
this.coinbase = tx.isCoinbase();
this.hash = tx.hash('hex');
this.index = index;
return this;
}
/**
* Instantiate a coin from a TX
* @param {TX} tx
* @param {Number} index - Output index.
* @returns {Coin}
*/
static fromTX(tx, index, height) {
return new this().fromTX(tx, index, height);
}
/**
* Test an object to see if it is a Coin.
* @param {Object} obj
* @returns {Boolean}
*/
static isCoin(obj) {
return obj instanceof Coin;
}
}
/*
* Expose

View File

@ -14,262 +14,264 @@ const BufferReader = require('bufio/lib/reader');
const encoding = require('bufio/lib/encoding');
/**
* Represents block headers obtained from the network via `headers`.
* Headers
* Represents block headers obtained
* from the network via `headers`.
* @alias module:primitives.Headers
* @constructor
* @extends AbstractBlock
* @param {NakedBlock} options
*/
function Headers(options) {
if (!(this instanceof Headers))
return new Headers(options);
class Headers extends AbstractBlock {
/**
* Create headers.
* @constructor
* @param {Object} options
*/
AbstractBlock.call(this);
constructor(options) {
super();
if (options)
this.parseOptions(options);
if (options)
this.parseOptions(options);
}
/**
* Perform non-contextual
* verification on the headers.
* @returns {Boolean}
*/
verifyBody() {
return true;
}
/**
* Get size of the headers.
* @returns {Number}
*/
getSize() {
return 81;
}
/**
* Serialize the headers to a buffer writer.
* @param {BufferWriter} bw
*/
toWriter(bw) {
this.writeHead(bw);
bw.writeVarint(0);
return bw;
}
/**
* Serialize the headers.
* @returns {Buffer|String}
*/
toRaw() {
const size = this.getSize();
return this.toWriter(new StaticWriter(size)).render();
}
/**
* Inject properties from buffer reader.
* @private
* @param {Buffer} data
*/
fromReader(br) {
this.readHead(br);
br.readVarint();
return this;
}
/**
* Inject properties from serialized data.
* @private
* @param {Buffer} data
*/
fromRaw(data) {
return this.fromReader(new BufferReader(data));
}
/**
* Instantiate headers from buffer reader.
* @param {BufferReader} br
* @returns {Headers}
*/
static fromReader(br) {
return new this().fromReader(br);
}
/**
* Instantiate headers from serialized data.
* @param {Buffer} data
* @param {String?} enc - Encoding, can be `'hex'` or null.
* @returns {Headers}
*/
static fromRaw(data, enc) {
if (typeof data === 'string')
data = Buffer.from(data, enc);
return new this().fromRaw(data);
}
/**
* Instantiate headers from serialized data.
* @param {Buffer} data
* @param {String?} enc - Encoding, can be `'hex'` or null.
* @returns {Headers}
*/
static fromHead(data, enc) {
if (typeof data === 'string')
data = Buffer.from(data, enc);
return new this().fromHead(data);
}
/**
* Instantiate headers from a chain entry.
* @param {ChainEntry} entry
* @returns {Headers}
*/
static fromEntry(entry) {
const headers = new this();
headers.version = entry.version;
headers.prevBlock = entry.prevBlock;
headers.merkleRoot = entry.merkleRoot;
headers.time = entry.time;
headers.bits = entry.bits;
headers.nonce = entry.nonce;
headers._hash = Buffer.from(entry.hash, 'hex');
headers._hhash = entry.hash;
return headers;
}
/**
* Convert the block to a headers object.
* @returns {Headers}
*/
toHeaders() {
return this;
}
/**
* Convert the block to a headers object.
* @param {Block|MerkleBlock} block
* @returns {Headers}
*/
static fromBlock(block) {
const headers = new this(block);
headers._hash = block._hash;
headers._hhash = block._hhash;
return headers;
}
/**
* Convert the block to an object suitable
* for JSON serialization.
* @returns {Object}
*/
toJSON() {
return this.getJSON();
}
/**
* Convert the block to an object suitable
* for JSON serialization. Note that the hashes
* will be reversed to abide by bitcoind's legacy
* of little-endian uint256s.
* @param {Network} network
* @param {CoinView} view
* @param {Number} height
* @returns {Object}
*/
getJSON(network, view, height) {
return {
hash: this.rhash(),
height: height,
version: this.version,
prevBlock: encoding.revHex(this.prevBlock),
merkleRoot: encoding.revHex(this.merkleRoot),
time: this.time,
bits: this.bits,
nonce: this.nonce
};
}
/**
* Inject properties from json object.
* @private
* @param {Object} json
*/
fromJSON(json) {
this.parseJSON(json);
return this;
}
/**
* Instantiate a merkle block from a jsonified block object.
* @param {Object} json - The jsonified block object.
* @returns {Headers}
*/
static fromJSON(json) {
return new this().fromJSON(json);
}
/**
* Inspect the headers and return a more
* user-friendly representation of the data.
* @returns {Object}
*/
inspect() {
return this.format();
}
/**
* Inspect the headers and return a more
* user-friendly representation of the data.
* @param {CoinView} view
* @param {Number} height
* @returns {Object}
*/
format(view, height) {
return {
hash: this.rhash(),
height: height != null ? height : -1,
date: util.date(this.time),
version: this.version.toString(16),
prevBlock: encoding.revHex(this.prevBlock),
merkleRoot: encoding.revHex(this.merkleRoot),
time: this.time,
bits: this.bits,
nonce: this.nonce
};
}
/**
* Test an object to see if it is a Headers object.
* @param {Object} obj
* @returns {Boolean}
*/
static isHeaders(obj) {
return obj instanceof Headers;
}
}
Object.setPrototypeOf(Headers.prototype, AbstractBlock.prototype);
/**
* Do non-contextual verification on the headers.
* @param {Object?} ret - Return object, may be
* set with properties `reason` and `score`.
* @returns {Boolean}
*/
Headers.prototype.verifyBody = function verifyBody(ret) {
return true;
};
/**
* Get size of the headers.
* @returns {Number}
*/
Headers.prototype.getSize = function getSize() {
return 81;
};
/**
* Serialize the headers to a buffer writer.
* @param {BufferWriter} bw
*/
Headers.prototype.toWriter = function toWriter(bw) {
this.writeHead(bw);
bw.writeVarint(0);
return bw;
};
/**
* Serialize the headers.
* @returns {Buffer|String}
*/
Headers.prototype.toRaw = function toRaw() {
const size = this.getSize();
return this.toWriter(new StaticWriter(size)).render();
};
/**
* Inject properties from buffer reader.
* @private
* @param {Buffer} data
*/
Headers.prototype.fromReader = function fromReader(br) {
this.readHead(br);
br.readVarint();
return this;
};
/**
* Inject properties from serialized data.
* @private
* @param {Buffer} data
*/
Headers.prototype.fromRaw = function fromRaw(data) {
return this.fromReader(new BufferReader(data));
};
/**
* Instantiate headers from buffer reader.
* @param {BufferReader} br
* @returns {Headers}
*/
Headers.fromReader = function fromReader(br) {
return new Headers().fromReader(br);
};
/**
* Instantiate headers from serialized data.
* @param {Buffer} data
* @param {String?} enc - Encoding, can be `'hex'` or null.
* @returns {Headers}
*/
Headers.fromRaw = function fromRaw(data, enc) {
if (typeof data === 'string')
data = Buffer.from(data, enc);
return new Headers().fromRaw(data);
};
/**
* Instantiate headers from serialized data.
* @param {Buffer} data
* @param {String?} enc - Encoding, can be `'hex'` or null.
* @returns {Headers}
*/
Headers.fromHead = function fromHead(data, enc) {
if (typeof data === 'string')
data = Buffer.from(data, enc);
return new Headers().fromHead(data);
};
/**
* Instantiate headers from a chain entry.
* @param {ChainEntry} entry
* @returns {Headers}
*/
Headers.fromEntry = function fromEntry(entry) {
const headers = new Headers();
headers.version = entry.version;
headers.prevBlock = entry.prevBlock;
headers.merkleRoot = entry.merkleRoot;
headers.time = entry.time;
headers.bits = entry.bits;
headers.nonce = entry.nonce;
headers._hash = Buffer.from(entry.hash, 'hex');
headers._hhash = entry.hash;
return headers;
};
/**
* Convert the block to a headers object.
* @returns {Headers}
*/
Headers.prototype.toHeaders = function toHeaders() {
return this;
};
/**
* Convert the block to a headers object.
* @param {Block|MerkleBlock} block
* @returns {Headers}
*/
Headers.fromBlock = function fromBlock(block) {
const headers = new Headers(block);
headers._hash = block._hash;
headers._hhash = block._hhash;
return headers;
};
/**
* Convert the block to an object suitable
* for JSON serialization.
* @returns {Object}
*/
Headers.prototype.toJSON = function toJSON() {
return this.getJSON();
};
/**
* Convert the block to an object suitable
* for JSON serialization. Note that the hashes
* will be reversed to abide by bitcoind's legacy
* of little-endian uint256s.
* @param {Network} network
* @param {CoinView} view
* @param {Number} height
* @returns {Object}
*/
Headers.prototype.getJSON = function getJSON(network, view, height) {
return {
hash: this.rhash(),
height: height,
version: this.version,
prevBlock: encoding.revHex(this.prevBlock),
merkleRoot: encoding.revHex(this.merkleRoot),
time: this.time,
bits: this.bits,
nonce: this.nonce
};
};
/**
* Inject properties from json object.
* @private
* @param {Object} json
*/
Headers.prototype.fromJSON = function fromJSON(json) {
this.parseJSON(json);
return this;
};
/**
* Instantiate a merkle block from a jsonified block object.
* @param {Object} json - The jsonified block object.
* @returns {Headers}
*/
Headers.fromJSON = function fromJSON(json) {
return new Headers().fromJSON(json);
};
/**
* Inspect the headers and return a more
* user-friendly representation of the data.
* @returns {Object}
*/
Headers.prototype.inspect = function inspect() {
return this.format();
};
/**
* Inspect the headers and return a more
* user-friendly representation of the data.
* @param {CoinView} view
* @param {Number} height
* @returns {Object}
*/
Headers.prototype.format = function format(view, height) {
return {
hash: this.rhash(),
height: height != null ? height : -1,
date: util.date(this.time),
version: this.version.toString(16),
prevBlock: encoding.revHex(this.prevBlock),
merkleRoot: encoding.revHex(this.merkleRoot),
time: this.time,
bits: this.bits,
nonce: this.nonce
};
};
/**
* Test an object to see if it is a Headers object.
* @param {Object} obj
* @returns {Boolean}
*/
Headers.isHeaders = function isHeaders(obj) {
return obj instanceof Headers;
};
/*
* Expose
*/

View File

@ -16,502 +16,507 @@ const StaticWriter = require('bufio/lib/staticwriter');
const BufferReader = require('bufio/lib/reader');
/**
* Input
* Represents a transaction input.
* @alias module:primitives.Input
* @constructor
* @param {NakedInput} options
* @property {Outpoint} prevout - Outpoint.
* @property {Script} script - Input script / scriptSig.
* @property {Number} sequence - nSequence.
* @property {Witness} witness - Witness (empty if not present).
*/
function Input(options) {
if (!(this instanceof Input))
return new Input(options);
class Input {
/**
* Create transaction input.
* @constructor
* @param {Object} options
*/
this.prevout = new Outpoint();
this.script = new Script();
this.sequence = 0xffffffff;
this.witness = new Witness();
constructor(options) {
this.prevout = new Outpoint();
this.script = new Script();
this.sequence = 0xffffffff;
this.witness = new Witness();
if (options)
this.fromOptions(options);
}
if (options)
this.fromOptions(options);
}
/**
* Inject properties from options object.
* @private
* @param {Object} options
*/
/**
* Inject properties from options object.
* @private
* @param {Object} options
*/
Input.prototype.fromOptions = function fromOptions(options) {
assert(options, 'Input data is required.');
fromOptions(options) {
assert(options, 'Input data is required.');
this.prevout.fromOptions(options.prevout);
this.prevout.fromOptions(options.prevout);
if (options.script)
this.script.fromOptions(options.script);
if (options.script)
this.script.fromOptions(options.script);
if (options.sequence != null) {
assert((options.sequence >>> 0) === options.sequence,
if (options.sequence != null) {
assert((options.sequence >>> 0) === options.sequence,
'Sequence must be a uint32.');
this.sequence = options.sequence;
}
if (options.witness)
this.witness.fromOptions(options.witness);
return this;
}
/**
* Instantiate an Input from options object.
* @param {NakedInput} options
* @returns {Input}
*/
static fromOptions(options) {
return new this().fromOptions(options);
}
/**
* Clone the input.
* @returns {Input}
*/
clone() {
const input = new this.constructor();
input.prevout = this.prevout;
input.script.inject(this.script);
input.sequence = this.sequence;
input.witness.inject(this.witness);
return input;
}
/**
* Test equality against another input.
* @param {Input} input
* @returns {Boolean}
*/
equals(input) {
assert(Input.isInput(input));
return this.prevout.equals(input.prevout);
}
/**
* Compare against another input (BIP69).
* @param {Input} input
* @returns {Number}
*/
compare(input) {
assert(Input.isInput(input));
return this.prevout.compare(input.prevout);
}
/**
* Get the previous output script type as a string.
* Will "guess" based on the input script and/or
* witness if coin is not available.
* @param {Coin?} coin
* @returns {ScriptType} type
*/
getType(coin) {
if (this.isCoinbase())
return 'coinbase';
if (coin)
return coin.getType();
let type;
if (this.witness.items.length > 0)
type = this.witness.getInputType();
else
type = this.script.getInputType();
return Script.typesByVal[type].toLowerCase();
}
/**
* Get the redeem script. Will attempt to resolve nested
* redeem scripts if witnessscripthash is behind a scripthash.
* @param {Coin?} coin
* @returns {Script?} Redeem script.
*/
getRedeem(coin) {
if (this.isCoinbase())
return null;
if (!coin) {
if (this.witness.isScripthashInput())
return this.witness.getRedeem();
if (this.script.isScripthashInput())
return this.script.getRedeem();
return null;
}
let prev = coin.script;
let redeem = null;
if (prev.isScripthash()) {
prev = this.script.getRedeem();
redeem = prev;
}
if (prev && prev.isWitnessScripthash()) {
prev = this.witness.getRedeem();
redeem = prev;
}
return redeem;
}
/**
* Get the redeem script type.
* @param {Coin?} coin
* @returns {String} subtype
*/
getSubtype(coin) {
if (this.isCoinbase())
return null;
const redeem = this.getRedeem(coin);
if (!redeem)
return null;
const type = redeem.getType();
return Script.typesByVal[type].toLowerCase();
}
/**
* Get the previous output script's address. Will "guess"
* based on the input script and/or witness if coin
* is not available.
* @param {Coin?} coin
* @returns {Address?} addr
*/
getAddress(coin) {
if (this.isCoinbase())
return null;
if (coin)
return coin.getAddress();
if (this.witness.items.length > 0)
return this.witness.getInputAddress();
return this.script.getInputAddress();
}
/**
* Get the address hash.
* @param {String?} enc
* @returns {Hash} hash
*/
getHash(enc) {
const addr = this.getAddress();
if (!addr)
return null;
return addr.getHash(enc);
}
/**
* Test to see if nSequence is equal to uint32max.
* @returns {Boolean}
*/
isFinal() {
return this.sequence === 0xffffffff;
}
/**
* Test to see if nSequence is less than 0xfffffffe.
* @returns {Boolean}
*/
isRBF() {
return this.sequence < 0xfffffffe;
}
/**
* Test to see if outpoint is null.
* @returns {Boolean}
*/
isCoinbase() {
return this.prevout.isNull();
}
/**
* Convert the input to a more user-friendly object.
* @returns {Object}
*/
inspect() {
return this.format();
}
/**
* Convert the input to a more user-friendly object.
* @param {Coin?} coin
* @returns {Object}
*/
format(coin) {
return {
type: this.getType(coin),
subtype: this.getSubtype(coin),
address: this.getAddress(coin),
script: this.script,
witness: this.witness,
redeem: this.getRedeem(coin),
sequence: this.sequence,
prevout: this.prevout,
coin: coin || null
};
}
/**
* Convert the input to an object suitable
* for JSON serialization.
* @returns {Object}
*/
toJSON(network, coin) {
return this.getJSON();
}
/**
* Convert the input to an object suitable
* for JSON serialization. Note that the hashes
* will be reversed to abide by bitcoind's legacy
* of little-endian uint256s.
* @param {Network} network
* @param {Coin} coin
* @returns {Object}
*/
getJSON(network, coin) {
network = Network.get(network);
let addr;
if (!coin) {
addr = this.getAddress();
if (addr)
addr = addr.toString(network);
}
return {
prevout: this.prevout.toJSON(),
script: this.script.toJSON(),
witness: this.witness.toJSON(),
sequence: this.sequence,
address: addr,
coin: coin ? coin.getJSON(network, true) : undefined
};
}
/**
* Inject properties from a JSON object.
* @private
* @param {Object} json
*/
fromJSON(json) {
assert(json, 'Input data is required.');
assert((json.sequence >>> 0) === json.sequence,
'Sequence must be a uint32.');
this.sequence = options.sequence;
this.prevout.fromJSON(json.prevout);
this.script.fromJSON(json.script);
this.witness.fromJSON(json.witness);
this.sequence = json.sequence;
return this;
}
if (options.witness)
this.witness.fromOptions(options.witness);
/**
* Instantiate an Input from a jsonified input object.
* @param {Object} json - The jsonified input object.
* @returns {Input}
*/
return this;
};
/**
* Instantiate an Input from options object.
* @param {NakedInput} options
* @returns {Input}
*/
Input.fromOptions = function fromOptions(options) {
return new Input().fromOptions(options);
};
/**
* Clone the input.
* @returns {Input}
*/
Input.prototype.clone = function clone() {
const input = new Input();
input.prevout = this.prevout;
input.script.inject(this.script);
input.sequence = this.sequence;
input.witness.inject(this.witness);
return input;
};
/**
* Test equality against another input.
* @param {Input} input
* @returns {Boolean}
*/
Input.prototype.equals = function equals(input) {
assert(Input.isInput(input));
return this.prevout.equals(input.prevout);
};
/**
* Compare against another input (BIP69).
* @param {Input} input
* @returns {Number}
*/
Input.prototype.compare = function compare(input) {
assert(Input.isInput(input));
return this.prevout.compare(input.prevout);
};
/**
* Get the previous output script type as a string.
* Will "guess" based on the input script and/or
* witness if coin is not available.
* @param {Coin?} coin
* @returns {ScriptType} type
*/
Input.prototype.getType = function getType(coin) {
if (this.isCoinbase())
return 'coinbase';
if (coin)
return coin.getType();
let type;
if (this.witness.items.length > 0)
type = this.witness.getInputType();
else
type = this.script.getInputType();
return Script.typesByVal[type].toLowerCase();
};
/**
* Get the redeem script. Will attempt to resolve nested
* redeem scripts if witnessscripthash is behind a scripthash.
* @param {Coin?} coin
* @returns {Script?} Redeem script.
*/
Input.prototype.getRedeem = function getRedeem(coin) {
if (this.isCoinbase())
return null;
if (!coin) {
if (this.witness.isScripthashInput())
return this.witness.getRedeem();
if (this.script.isScripthashInput())
return this.script.getRedeem();
return null;
static fromJSON(json) {
return new this().fromJSON(json);
}
let prev = coin.script;
let redeem = null;
/**
* Calculate size of serialized input.
* @returns {Number}
*/
if (prev.isScripthash()) {
prev = this.script.getRedeem();
redeem = prev;
getSize() {
return 40 + this.script.getVarSize();
}
if (prev && prev.isWitnessScripthash()) {
prev = this.witness.getRedeem();
redeem = prev;
/**
* Serialize the input.
* @param {String?} enc - Encoding, can be `'hex'` or null.
* @returns {Buffer|String}
*/
toRaw() {
const size = this.getSize();
return this.toWriter(new StaticWriter(size)).render();
}
return redeem;
};
/**
* Write the input to a buffer writer.
* @param {BufferWriter} bw
*/
/**
* Get the redeem script type.
* @param {Coin?} coin
* @returns {String} subtype
*/
Input.prototype.getSubtype = function getSubtype(coin) {
if (this.isCoinbase())
return null;
const redeem = this.getRedeem(coin);
if (!redeem)
return null;
const type = redeem.getType();
return Script.typesByVal[type].toLowerCase();
};
/**
* Get the previous output script's address. Will "guess"
* based on the input script and/or witness if coin
* is not available.
* @param {Coin?} coin
* @returns {Address?} addr
*/
Input.prototype.getAddress = function getAddress(coin) {
if (this.isCoinbase())
return null;
if (coin)
return coin.getAddress();
if (this.witness.items.length > 0)
return this.witness.getInputAddress();
return this.script.getInputAddress();
};
/**
* Get the address hash.
* @param {String?} enc
* @returns {Hash} hash
*/
Input.prototype.getHash = function getHash(enc) {
const addr = this.getAddress();
if (!addr)
return null;
return addr.getHash(enc);
};
/**
* Test to see if nSequence is equal to uint32max.
* @returns {Boolean}
*/
Input.prototype.isFinal = function isFinal() {
return this.sequence === 0xffffffff;
};
/**
* Test to see if nSequence is less than 0xfffffffe.
* @returns {Boolean}
*/
Input.prototype.isRBF = function isRBF() {
return this.sequence < 0xfffffffe;
};
/**
* Test to see if outpoint is null.
* @returns {Boolean}
*/
Input.prototype.isCoinbase = function isCoinbase() {
return this.prevout.isNull();
};
/**
* Convert the input to a more user-friendly object.
* @returns {Object}
*/
Input.prototype.inspect = function inspect() {
return this.format();
};
/**
* Convert the input to a more user-friendly object.
* @param {Coin?} coin
* @returns {Object}
*/
Input.prototype.format = function format(coin) {
return {
type: this.getType(coin),
subtype: this.getSubtype(coin),
address: this.getAddress(coin),
script: this.script,
witness: this.witness,
redeem: this.getRedeem(coin),
sequence: this.sequence,
prevout: this.prevout,
coin: coin || null
};
};
/**
* Convert the input to an object suitable
* for JSON serialization.
* @returns {Object}
*/
Input.prototype.toJSON = function toJSON(network, coin) {
return this.getJSON();
};
/**
* Convert the input to an object suitable
* for JSON serialization. Note that the hashes
* will be reversed to abide by bitcoind's legacy
* of little-endian uint256s.
* @param {Network} network
* @param {Coin} coin
* @returns {Object}
*/
Input.prototype.getJSON = function getJSON(network, coin) {
network = Network.get(network);
let addr;
if (!coin) {
addr = this.getAddress();
if (addr)
addr = addr.toString(network);
toWriter(bw) {
this.prevout.toWriter(bw);
bw.writeVarBytes(this.script.toRaw());
bw.writeU32(this.sequence);
return bw;
}
return {
prevout: this.prevout.toJSON(),
script: this.script.toJSON(),
witness: this.witness.toJSON(),
sequence: this.sequence,
address: addr,
coin: coin ? coin.getJSON(network, true) : undefined
};
};
/**
* Inject properties from buffer reader.
* @private
* @param {BufferReader} br
*/
/**
* Inject properties from a JSON object.
* @private
* @param {Object} json
*/
fromReader(br) {
this.prevout.fromReader(br);
this.script.fromRaw(br.readVarBytes());
this.sequence = br.readU32();
return this;
}
Input.prototype.fromJSON = function fromJSON(json) {
assert(json, 'Input data is required.');
assert((json.sequence >>> 0) === json.sequence, 'Sequence must be a uint32.');
this.prevout.fromJSON(json.prevout);
this.script.fromJSON(json.script);
this.witness.fromJSON(json.witness);
this.sequence = json.sequence;
return this;
};
/**
* Inject properties from serialized data.
* @param {Buffer} data
*/
/**
* Instantiate an Input from a jsonified input object.
* @param {Object} json - The jsonified input object.
* @returns {Input}
*/
fromRaw(data) {
return this.fromReader(new BufferReader(data));
}
Input.fromJSON = function fromJSON(json) {
return new Input().fromJSON(json);
};
/**
* Instantiate an input from a buffer reader.
* @param {BufferReader} br
* @returns {Input}
*/
/**
* Calculate size of serialized input.
* @returns {Number}
*/
static fromReader(br) {
return new this().fromReader(br);
}
Input.prototype.getSize = function getSize() {
return 40 + this.script.getVarSize();
};
/**
* Instantiate an input from a serialized Buffer.
* @param {Buffer} data
* @param {String?} enc - Encoding, can be `'hex'` or null.
* @returns {Input}
*/
/**
* Serialize the input.
* @param {String?} enc - Encoding, can be `'hex'` or null.
* @returns {Buffer|String}
*/
static fromRaw(data, enc) {
if (typeof data === 'string')
data = Buffer.from(data, enc);
return new this().fromRaw(data);
}
Input.prototype.toRaw = function toRaw() {
const size = this.getSize();
return this.toWriter(new StaticWriter(size)).render();
};
/**
* Inject properties from outpoint.
* @private
* @param {Outpoint} outpoint
*/
/**
* Write the input to a buffer writer.
* @param {BufferWriter} bw
*/
fromOutpoint(outpoint) {
assert(typeof outpoint.hash === 'string');
assert(typeof outpoint.index === 'number');
this.prevout.hash = outpoint.hash;
this.prevout.index = outpoint.index;
return this;
}
Input.prototype.toWriter = function toWriter(bw) {
this.prevout.toWriter(bw);
bw.writeVarBytes(this.script.toRaw());
bw.writeU32(this.sequence);
return bw;
};
/**
* Instantiate input from outpoint.
* @param {Outpoint}
* @returns {Input}
*/
/**
* Inject properties from buffer reader.
* @private
* @param {BufferReader} br
*/
static fromOutpoint(outpoint) {
return new this().fromOutpoint(outpoint);
}
Input.prototype.fromReader = function fromReader(br) {
this.prevout.fromReader(br);
this.script.fromRaw(br.readVarBytes());
this.sequence = br.readU32();
return this;
};
/**
* Inject properties from coin.
* @private
* @param {Coin} coin
*/
/**
* Inject properties from serialized data.
* @param {Buffer} data
*/
fromCoin(coin) {
assert(typeof coin.hash === 'string');
assert(typeof coin.index === 'number');
this.prevout.hash = coin.hash;
this.prevout.index = coin.index;
return this;
}
Input.prototype.fromRaw = function fromRaw(data) {
return this.fromReader(new BufferReader(data));
};
/**
* Instantiate input from coin.
* @param {Coin}
* @returns {Input}
*/
/**
* Instantiate an input from a buffer reader.
* @param {BufferReader} br
* @returns {Input}
*/
static fromCoin(coin) {
return new this().fromCoin(coin);
}
Input.fromReader = function fromReader(br) {
return new Input().fromReader(br);
};
/**
* Inject properties from transaction.
* @private
* @param {TX} tx
* @param {Number} index
*/
/**
* Instantiate an input from a serialized Buffer.
* @param {Buffer} data
* @param {String?} enc - Encoding, can be `'hex'` or null.
* @returns {Input}
*/
fromTX(tx, index) {
assert(tx);
assert(typeof index === 'number');
assert(index >= 0 && index < tx.outputs.length);
this.prevout.hash = tx.hash('hex');
this.prevout.index = index;
return this;
}
Input.fromRaw = function fromRaw(data, enc) {
if (typeof data === 'string')
data = Buffer.from(data, enc);
return new Input().fromRaw(data);
};
/**
* Instantiate input from tx.
* @param {TX} tx
* @param {Number} index
* @returns {Input}
*/
/**
* Inject properties from outpoint.
* @private
* @param {Outpoint} outpoint
*/
static fromTX(tx, index) {
return new this().fromTX(tx, index);
}
Input.prototype.fromOutpoint = function fromOutpoint(outpoint) {
assert(typeof outpoint.hash === 'string');
assert(typeof outpoint.index === 'number');
this.prevout.hash = outpoint.hash;
this.prevout.index = outpoint.index;
return this;
};
/**
* Test an object to see if it is an Input.
* @param {Object} obj
* @returns {Boolean}
*/
/**
* Instantiate input from outpoint.
* @param {Outpoint}
* @returns {Input}
*/
Input.fromOutpoint = function fromOutpoint(outpoint) {
return new Input().fromOutpoint(outpoint);
};
/**
* Inject properties from coin.
* @private
* @param {Coin} coin
*/
Input.prototype.fromCoin = function fromCoin(coin) {
assert(typeof coin.hash === 'string');
assert(typeof coin.index === 'number');
this.prevout.hash = coin.hash;
this.prevout.index = coin.index;
return this;
};
/**
* Instantiate input from coin.
* @param {Coin}
* @returns {Input}
*/
Input.fromCoin = function fromCoin(coin) {
return new Input().fromCoin(coin);
};
/**
* Inject properties from transaction.
* @private
* @param {TX} tx
* @param {Number} index
*/
Input.prototype.fromTX = function fromTX(tx, index) {
assert(tx);
assert(typeof index === 'number');
assert(index >= 0 && index < tx.outputs.length);
this.prevout.hash = tx.hash('hex');
this.prevout.index = index;
return this;
};
/**
* Instantiate input from tx.
* @param {TX} tx
* @param {Number} index
* @returns {Input}
*/
Input.fromTX = function fromTX(tx, index) {
return new Input().fromTX(tx, index);
};
/**
* Test an object to see if it is an Input.
* @param {Object} obj
* @returns {Boolean}
*/
Input.isInput = function isInput(obj) {
return obj instanceof Input;
};
static isInput(obj) {
return obj instanceof Input;
}
}
/*
* Expose

View File

@ -15,18 +15,146 @@ const encoding = require('bufio/lib/encoding');
* Inv Item
* @alias module:primitives.InvItem
* @constructor
* @param {Number} type
* @param {Hash} hash
* @property {InvType} type
* @property {Hash} hash
*/
function InvItem(type, hash) {
if (!(this instanceof InvItem))
return new InvItem(type, hash);
class InvItem {
/**
* Create an inv item.
* @constructor
* @param {Number} type
* @param {Hash} hash
*/
this.type = type;
this.hash = hash;
constructor(type, hash) {
this.type = type;
this.hash = hash;
}
/**
* Write inv item to buffer writer.
* @param {BufferWriter} bw
*/
getSize() {
return 36;
}
/**
* Write inv item to buffer writer.
* @param {BufferWriter} bw
*/
toWriter(bw) {
bw.writeU32(this.type);
bw.writeHash(this.hash);
return bw;
}
/**
* Serialize inv item.
* @returns {Buffer}
*/
toRaw() {
return this.toWriter(new StaticWriter(36)).render();
}
/**
* Inject properties from buffer reader.
* @private
* @param {BufferReader} br
*/
fromReader(br) {
this.type = br.readU32();
this.hash = br.readHash('hex');
return this;
}
/**
* Inject properties from serialized data.
* @param {Buffer} data
*/
fromRaw(data) {
return this.fromReader(new BufferReader(data));
}
/**
* Instantiate inv item from buffer reader.
* @param {BufferReader} br
* @returns {InvItem}
*/
static fromReader(br) {
return new this().fromReader(br);
}
/**
* Instantiate inv item from serialized data.
* @param {Buffer} data
* @param {String?} enc
* @returns {InvItem}
*/
static fromRaw(data, enc) {
if (typeof data === 'string')
data = Buffer.from(data, enc);
return new this().fromRaw(data);
}
/**
* Test whether the inv item is a block.
* @returns {Boolean}
*/
isBlock() {
switch (this.type) {
case InvItem.types.BLOCK:
case InvItem.types.WITNESS_BLOCK:
case InvItem.types.FILTERED_BLOCK:
case InvItem.types.WITNESS_FILTERED_BLOCK:
case InvItem.types.CMPCT_BLOCK:
return true;
default:
return false;
}
}
/**
* Test whether the inv item is a tx.
* @returns {Boolean}
*/
isTX() {
switch (this.type) {
case InvItem.types.TX:
case InvItem.types.WITNESS_TX:
return true;
default:
return false;
}
}
/**
* Test whether the inv item has the witness bit set.
* @returns {Boolean}
*/
hasWitness() {
return (this.type & InvItem.WITNESS_FLAG) !== 0;
}
/**
* Get little-endian hash.
* @returns {Hash}
*/
rhash() {
return encoding.revHex(this.hash);
}
}
/**
@ -36,7 +164,6 @@ function InvItem(type, hash) {
*/
InvItem.types = {
ERROR: 0,
TX: 1,
BLOCK: 2,
FILTERED_BLOCK: 3,
@ -52,7 +179,6 @@ InvItem.types = {
*/
InvItem.typesByVal = {
0: 'ERROR',
1: 'TX',
2: 'BLOCK',
3: 'FILTERED_BLOCK',
@ -70,130 +196,6 @@ InvItem.typesByVal = {
InvItem.WITNESS_FLAG = 1 << 30;
/**
* Write inv item to buffer writer.
* @param {BufferWriter} bw
*/
InvItem.prototype.getSize = function getSize() {
return 36;
};
/**
* Write inv item to buffer writer.
* @param {BufferWriter} bw
*/
InvItem.prototype.toWriter = function toWriter(bw) {
bw.writeU32(this.type);
bw.writeHash(this.hash);
return bw;
};
/**
* Serialize inv item.
* @returns {Buffer}
*/
InvItem.prototype.toRaw = function toRaw() {
return this.toWriter(new StaticWriter(36)).render();
};
/**
* Inject properties from buffer reader.
* @private
* @param {BufferReader} br
*/
InvItem.prototype.fromReader = function fromReader(br) {
this.type = br.readU32();
this.hash = br.readHash('hex');
return this;
};
/**
* Inject properties from serialized data.
* @param {Buffer} data
*/
InvItem.prototype.fromRaw = function fromRaw(data) {
return this.fromReader(new BufferReader(data));
};
/**
* Instantiate inv item from buffer reader.
* @param {BufferReader} br
* @returns {InvItem}
*/
InvItem.fromReader = function fromReader(br) {
return new InvItem().fromReader(br);
};
/**
* Instantiate inv item from serialized data.
* @param {Buffer} data
* @param {String?} enc
* @returns {InvItem}
*/
InvItem.fromRaw = function fromRaw(data, enc) {
if (typeof data === 'string')
data = Buffer.from(data, enc);
return new InvItem().fromRaw(data);
};
/**
* Test whether the inv item is a block.
* @returns {Boolean}
*/
InvItem.prototype.isBlock = function isBlock() {
switch (this.type) {
case InvItem.types.BLOCK:
case InvItem.types.WITNESS_BLOCK:
case InvItem.types.FILTERED_BLOCK:
case InvItem.types.WITNESS_FILTERED_BLOCK:
case InvItem.types.CMPCT_BLOCK:
return true;
default:
return false;
}
};
/**
* Test whether the inv item is a tx.
* @returns {Boolean}
*/
InvItem.prototype.isTX = function isTX() {
switch (this.type) {
case InvItem.types.TX:
case InvItem.types.WITNESS_TX:
return true;
default:
return false;
}
};
/**
* Test whether the inv item has the witness bit set.
* @returns {Boolean}
*/
InvItem.prototype.hasWitness = function hasWitness() {
return (this.type & InvItem.WITNESS_FLAG) !== 0;
};
/**
* Get little-endian hash.
* @returns {Hash}
*/
InvItem.prototype.rhash = function rhash() {
return encoding.revHex(this.hash);
};
/*
* Expose
*/

File diff suppressed because it is too large Load Diff

View File

@ -15,6 +15,7 @@ const BufferReader = require('bufio/lib/reader');
const DUMMY = Buffer.alloc(0);
/**
* Mem Block
* A block object which is essentially a "placeholder"
* for a full {@link Block} object. The v8 garbage
* collector's head will explode if there is too much
@ -31,189 +32,190 @@ const DUMMY = Buffer.alloc(0);
* 500mb of blocks on the js heap would not be a good
* thing.
* @alias module:primitives.MemBlock
* @constructor
* @param {NakedBlock} options
* @extends AbstractBlock
*/
function MemBlock() {
if (!(this instanceof MemBlock))
return new MemBlock();
class MemBlock extends AbstractBlock {
/**
* Create a mem block.
* @constructor
*/
AbstractBlock.call(this);
constructor() {
super();
this._raw = DUMMY;
this._raw = DUMMY;
}
/**
* Test whether the block is a memblock.
* @returns {Boolean}
*/
isMemory() {
return true;
}
/**
* Serialize the block headers.
* @returns {Buffer}
*/
toHead() {
return this._raw.slice(0, 80);
}
/**
* Get the full block size.
* @returns {Number}
*/
getSize() {
return this._raw.length;
}
/**
* Verify the block.
* @returns {Boolean}
*/
verifyBody() {
return true;
}
/**
* Retrieve the coinbase height
* from the coinbase input script.
* @returns {Number} height (-1 if not present).
*/
getCoinbaseHeight() {
if (this.version < 2)
return -1;
try {
return this.parseCoinbaseHeight();
} catch (e) {
return -1;
}
}
/**
* Parse the coinbase height
* from the coinbase input script.
* @private
* @returns {Number} height (-1 if not present).
*/
parseCoinbaseHeight() {
const br = new BufferReader(this._raw, true);
br.seek(80);
const txCount = br.readVarint();
if (txCount === 0)
return -1;
br.seek(4);
let inCount = br.readVarint();
if (inCount === 0) {
if (br.readU8() !== 0)
inCount = br.readVarint();
}
if (inCount === 0)
return -1;
br.seek(36);
const script = br.readVarBytes();
return Script.getCoinbaseHeight(script);
}
/**
* Inject properties from serialized data.
* @private
* @param {Buffer} data
*/
fromRaw(data) {
const br = new BufferReader(data, true);
this.readHead(br);
this._raw = br.data;
return this;
}
/**
* Insantiate a memblock from serialized data.
* @param {Buffer} data
* @returns {MemBlock}
*/
static fromRaw(data) {
return new this().fromRaw(data);
}
/**
* Return serialized block data.
* @returns {Buffer}
*/
toRaw() {
return this._raw;
}
/**
* Return serialized block data.
* @returns {Buffer}
*/
toNormal() {
return this._raw;
}
/**
* Parse the serialized block data
* and create an actual {@link Block}.
* @returns {Block}
* @throws Parse error
*/
toBlock() {
const block = Block.fromRaw(this._raw);
block._hash = this._hash;
block._hhash = this._hhash;
return block;
}
/**
* Convert the block to a headers object.
* @returns {Headers}
*/
toHeaders() {
return Headers.fromBlock(this);
}
/**
* Test whether an object is a MemBlock.
* @param {Object} obj
* @returns {Boolean}
*/
static isMemBlock(obj) {
return obj instanceof MemBlock;
}
}
Object.setPrototypeOf(MemBlock.prototype, AbstractBlock.prototype);
/**
* Test whether the block is a memblock.
* @returns {Boolean}
*/
MemBlock.prototype.isMemory = function isMemory() {
return true;
};
/**
* Serialize the block headers.
* @returns {Buffer}
*/
MemBlock.prototype.toHead = function toHead() {
return this._raw.slice(0, 80);
};
/**
* Get the full block size.
* @returns {Number}
*/
MemBlock.prototype.getSize = function getSize() {
return this._raw.length;
};
/**
* Verify the block.
* @returns {Boolean}
*/
MemBlock.prototype.verifyBody = function verifyBody() {
return true;
};
/**
* Retrieve the coinbase height
* from the coinbase input script.
* @returns {Number} height (-1 if not present).
*/
MemBlock.prototype.getCoinbaseHeight = function getCoinbaseHeight() {
if (this.version < 2)
return -1;
try {
return this.parseCoinbaseHeight();
} catch (e) {
return -1;
}
};
/**
* Parse the coinbase height
* from the coinbase input script.
* @private
* @returns {Number} height (-1 if not present).
*/
MemBlock.prototype.parseCoinbaseHeight = function parseCoinbaseHeight() {
const br = new BufferReader(this._raw, true);
br.seek(80);
const txCount = br.readVarint();
if (txCount === 0)
return -1;
br.seek(4);
let inCount = br.readVarint();
if (inCount === 0) {
if (br.readU8() !== 0)
inCount = br.readVarint();
}
if (inCount === 0)
return -1;
br.seek(36);
const script = br.readVarBytes();
return Script.getCoinbaseHeight(script);
};
/**
* Inject properties from serialized data.
* @private
* @param {Buffer} data
*/
MemBlock.prototype.fromRaw = function fromRaw(data) {
const br = new BufferReader(data, true);
this.readHead(br);
this._raw = br.data;
return this;
};
/**
* Insantiate a memblock from serialized data.
* @param {Buffer} data
* @returns {MemBlock}
*/
MemBlock.fromRaw = function fromRaw(data) {
return new MemBlock().fromRaw(data);
};
/**
* Return serialized block data.
* @returns {Buffer}
*/
MemBlock.prototype.toRaw = function toRaw() {
return this._raw;
};
/**
* Return serialized block data.
* @returns {Buffer}
*/
MemBlock.prototype.toNormal = function toNormal() {
return this._raw;
};
/**
* Parse the serialized block data
* and create an actual {@link Block}.
* @returns {Block}
* @throws Parse error
*/
MemBlock.prototype.toBlock = function toBlock() {
const block = Block.fromRaw(this._raw);
block._hash = this._hash;
block._hhash = this._hhash;
return block;
};
/**
* Convert the block to a headers object.
* @returns {Headers}
*/
MemBlock.prototype.toHeaders = function toHeaders() {
return Headers.fromBlock(this);
};
/**
* Test whether an object is a MemBlock.
* @param {Object} obj
* @returns {Boolean}
*/
MemBlock.isMemBlock = function isMemBlock(obj) {
return obj instanceof MemBlock;
};
/*
* Expose
*/

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -15,33 +15,455 @@ const StaticWriter = require('bufio/lib/staticwriter');
const BufferReader = require('bufio/lib/reader');
/**
* Net Address
* Represents a network address.
* @alias module:primitives.NetAddress
* @constructor
* @param {Object} options
* @param {Number?} options.time - Timestamp.
* @param {Number?} options.services - Service bits.
* @param {String?} options.host - IP address (IPv6 or IPv4).
* @param {Number?} options.port - Port.
* @property {Host} host
* @property {Number} port
* @property {Number} services
* @property {Number} time
*/
function NetAddress(options) {
if (!(this instanceof NetAddress))
return new NetAddress(options);
class NetAddress {
/**
* Create a network address.
* @constructor
* @param {Object} options
* @param {Number?} options.time - Timestamp.
* @param {Number?} options.services - Service bits.
* @param {String?} options.host - IP address (IPv6 or IPv4).
* @param {Number?} options.port - Port.
*/
this.host = '0.0.0.0';
this.port = 0;
this.services = 0;
this.time = 0;
this.hostname = '0.0.0.0:0';
this.raw = IP.ZERO_IP;
constructor(options) {
this.host = '0.0.0.0';
this.port = 0;
this.services = 0;
this.time = 0;
this.hostname = '0.0.0.0:0';
this.raw = IP.ZERO_IP;
if (options)
this.fromOptions(options);
if (options)
this.fromOptions(options);
}
/**
* Inject properties from options object.
* @private
* @param {Object} options
*/
fromOptions(options) {
assert(typeof options.host === 'string');
assert(typeof options.port === 'number');
this.raw = IP.toBuffer(options.host);
this.host = IP.toString(this.raw);
this.port = options.port;
if (options.services) {
assert(typeof options.services === 'number');
this.services = options.services;
}
if (options.time) {
assert(typeof options.time === 'number');
this.time = options.time;
}
this.hostname = IP.toHostname(this.host, this.port);
return this;
}
/**
* Instantiate network address from options.
* @param {Object} options
* @returns {NetAddress}
*/
static fromOptions(options) {
return new this().fromOptions(options);
}
/**
* Test whether required services are available.
* @param {Number} services
* @returns {Boolean}
*/
hasServices(services) {
return (this.services & services) === services;
}
/**
* Test whether the address is IPv4.
* @returns {Boolean}
*/
static isIPv4() {
return IP.isIPv4(this.raw);
}
/**
* Test whether the address is IPv6.
* @returns {Boolean}
*/
static isIPv6() {
return IP.isIPv6(this.raw);
}
/**
* Test whether the host is null.
* @returns {Boolean}
*/
isNull() {
return IP.isNull(this.raw);
}
/**
* Test whether the host is a local address.
* @returns {Boolean}
*/
isLocal() {
return IP.isLocal(this.raw);
}
/**
* Test whether the host is valid.
* @returns {Boolean}
*/
isValid() {
return IP.isValid(this.raw);
}
/**
* Test whether the host is routable.
* @returns {Boolean}
*/
isRoutable() {
return IP.isRoutable(this.raw);
}
/**
* Test whether the host is an onion address.
* @returns {Boolean}
*/
isOnion() {
return IP.isOnion(this.raw);
}
/**
* Compare against another network address.
* @returns {Boolean}
*/
equal(addr) {
return this.compare(addr) === 0;
}
/**
* Compare against another network address.
* @returns {Number}
*/
compare(addr) {
const cmp = this.raw.compare(addr.raw);
if (cmp !== 0)
return cmp;
return this.port - addr.port;
}
/**
* Get reachable score to destination.
* @param {NetAddress} dest
* @returns {Number}
*/
getReachability(dest) {
return IP.getReachability(this.raw, dest.raw);
}
/**
* Set null host.
*/
setNull() {
this.raw = IP.ZERO_IP;
this.host = '0.0.0.0';
this.hostname = IP.toHostname(this.host, this.port);
}
/**
* Set host.
* @param {String} host
*/
setHost(host) {
this.raw = IP.toBuffer(host);
this.host = IP.toString(this.raw);
this.hostname = IP.toHostname(this.host, this.port);
}
/**
* Set port.
* @param {Number} port
*/
setPort(port) {
assert(port >= 0 && port <= 0xffff);
this.port = port;
this.hostname = IP.toHostname(this.host, port);
}
/**
* Inject properties from host, port, and network.
* @private
* @param {String} host
* @param {Number} port
* @param {(Network|NetworkType)?} network
*/
fromHost(host, port, network) {
network = Network.get(network);
assert(port >= 0 && port <= 0xffff);
this.raw = IP.toBuffer(host);
this.host = IP.toString(this.raw);
this.port = port;
this.services = NetAddress.DEFAULT_SERVICES;
this.time = network.now();
this.hostname = IP.toHostname(this.host, this.port);
return this;
}
/**
* Instantiate a network address
* from a host and port.
* @param {String} host
* @param {Number} port
* @param {(Network|NetworkType)?} network
* @returns {NetAddress}
*/
static fromHost(host, port, network) {
return new this().fromHost(host, port, network);
}
/**
* Inject properties from hostname and network.
* @private
* @param {String} hostname
* @param {(Network|NetworkType)?} network
*/
fromHostname(hostname, network) {
network = Network.get(network);
const addr = IP.fromHostname(hostname, network.port);
return this.fromHost(addr.host, addr.port, network);
}
/**
* Instantiate a network address
* from a hostname (i.e. 127.0.0.1:8333).
* @param {String} hostname
* @param {(Network|NetworkType)?} network
* @returns {NetAddress}
*/
static fromHostname(hostname, network) {
return new this().fromHostname(hostname, network);
}
/**
* Inject properties from socket.
* @private
* @param {net.Socket} socket
*/
fromSocket(socket, network) {
const host = socket.remoteAddress;
const port = socket.remotePort;
assert(typeof host === 'string');
assert(typeof port === 'number');
return this.fromHost(IP.normalize(host), port, network);
}
/**
* Instantiate a network address
* from a socket.
* @param {net.Socket} socket
* @returns {NetAddress}
*/
static fromSocket(hostname, network) {
return new this().fromSocket(hostname, network);
}
/**
* Inject properties from buffer reader.
* @private
* @param {BufferReader} br
* @param {Boolean?} full - Include timestamp.
*/
fromReader(br, full) {
this.time = full ? br.readU32() : 0;
this.services = br.readU32();
// Note: hi service bits
// are currently unused.
br.readU32();
this.raw = br.readBytes(16);
this.host = IP.toString(this.raw);
this.port = br.readU16BE();
this.hostname = IP.toHostname(this.host, this.port);
return this;
}
/**
* Inject properties from serialized data.
* @private
* @param {Buffer} data
* @param {Boolean?} full - Include timestamp.
*/
fromRaw(data, full) {
return this.fromReader(new BufferReader(data), full);
}
/**
* Insantiate a network address from buffer reader.
* @param {BufferReader} br
* @param {Boolean?} full - Include timestamp.
* @returns {NetAddress}
*/
static fromReader(br, full) {
return new this().fromReader(br, full);
}
/**
* Insantiate a network address from serialized data.
* @param {Buffer} data
* @param {Boolean?} full - Include timestamp.
* @returns {NetAddress}
*/
static fromRaw(data, full) {
return new this().fromRaw(data, full);
}
/**
* Write network address to a buffer writer.
* @param {BufferWriter} bw
* @param {Boolean?} full - Include timestamp.
* @returns {Buffer}
*/
toWriter(bw, full) {
if (full)
bw.writeU32(this.time);
bw.writeU32(this.services);
bw.writeU32(0);
bw.writeBytes(this.raw);
bw.writeU16BE(this.port);
return bw;
}
/**
* Calculate serialization size of address.
* @returns {Number}
*/
getSize(full) {
return 26 + (full ? 4 : 0);
}
/**
* Serialize network address.
* @param {Boolean?} full - Include timestamp.
* @returns {Buffer}
*/
toRaw(full) {
const size = this.getSize(full);
return this.toWriter(new StaticWriter(size), full).render();
}
/**
* Convert net address to json-friendly object.
* @returns {Object}
*/
toJSON() {
return {
host: this.host,
port: this.port,
services: this.services,
time: this.time
};
}
/**
* Inject properties from json object.
* @private
* @param {Object} json
* @returns {NetAddress}
*/
fromJSON(json) {
assert((json.port & 0xffff) === json.port);
assert((json.services >>> 0) === json.services);
assert((json.time >>> 0) === json.time);
this.raw = IP.toBuffer(json.host);
this.host = json.host;
this.port = json.port;
this.services = json.services;
this.time = json.time;
this.hostname = IP.toHostname(this.host, this.port);
return this;
}
/**
* Instantiate net address from json object.
* @param {Object} json
* @returns {NetAddress}
*/
static fromJSON(json) {
return new this().fromJSON(json);
}
/**
* Inspect the network address.
* @returns {Object}
*/
inspect() {
return '<NetAddress:'
+ ` hostname=${this.hostname}`
+ ` services=${this.services.toString(2)}`
+ ` date=${util.date(this.time)}`
+ '>';
}
}
/**
@ -56,424 +478,6 @@ NetAddress.DEFAULT_SERVICES = 0
| common.services.WITNESS
| common.services.BLOOM;
/**
* Inject properties from options object.
* @private
* @param {Object} options
*/
NetAddress.prototype.fromOptions = function fromOptions(options) {
assert(typeof options.host === 'string');
assert(typeof options.port === 'number');
this.raw = IP.toBuffer(options.host);
this.host = IP.toString(this.raw);
this.port = options.port;
if (options.services) {
assert(typeof options.services === 'number');
this.services = options.services;
}
if (options.time) {
assert(typeof options.time === 'number');
this.time = options.time;
}
this.hostname = IP.toHostname(this.host, this.port);
return this;
};
/**
* Instantiate network address from options.
* @param {Object} options
* @returns {NetAddress}
*/
NetAddress.fromOptions = function fromOptions(options) {
return new NetAddress().fromOptions(options);
};
/**
* Test whether required services are available.
* @param {Number} services
* @returns {Boolean}
*/
NetAddress.prototype.hasServices = function hasServices(services) {
return (this.services & services) === services;
};
/**
* Test whether the address is IPv4.
* @returns {Boolean}
*/
NetAddress.isIPv4 = function isIPv4() {
return IP.isIPv4(this.raw);
};
/**
* Test whether the address is IPv6.
* @returns {Boolean}
*/
NetAddress.isIPv6 = function isIPv6() {
return IP.isIPv6(this.raw);
};
/**
* Test whether the host is null.
* @returns {Boolean}
*/
NetAddress.prototype.isNull = function isNull() {
return IP.isNull(this.raw);
};
/**
* Test whether the host is a local address.
* @returns {Boolean}
*/
NetAddress.prototype.isLocal = function isLocal() {
return IP.isLocal(this.raw);
};
/**
* Test whether the host is valid.
* @returns {Boolean}
*/
NetAddress.prototype.isValid = function isValid() {
return IP.isValid(this.raw);
};
/**
* Test whether the host is routable.
* @returns {Boolean}
*/
NetAddress.prototype.isRoutable = function isRoutable() {
return IP.isRoutable(this.raw);
};
/**
* Test whether the host is an onion address.
* @returns {Boolean}
*/
NetAddress.prototype.isOnion = function isOnion() {
return IP.isOnion(this.raw);
};
/**
* Compare against another network address.
* @returns {Boolean}
*/
NetAddress.prototype.equal = function equal(addr) {
return this.compare(addr) === 0;
};
/**
* Compare against another network address.
* @returns {Number}
*/
NetAddress.prototype.compare = function compare(addr) {
const cmp = this.raw.compare(addr.raw);
if (cmp !== 0)
return cmp;
return this.port - addr.port;
};
/**
* Get reachable score to destination.
* @param {NetAddress} dest
* @returns {Number}
*/
NetAddress.prototype.getReachability = function getReachability(dest) {
return IP.getReachability(this.raw, dest.raw);
};
/**
* Set null host.
*/
NetAddress.prototype.setNull = function setNull() {
this.raw = IP.ZERO_IP;
this.host = '0.0.0.0';
this.hostname = IP.toHostname(this.host, this.port);
};
/**
* Set host.
* @param {String} host
*/
NetAddress.prototype.setHost = function setHost(host) {
this.raw = IP.toBuffer(host);
this.host = IP.toString(this.raw);
this.hostname = IP.toHostname(this.host, this.port);
};
/**
* Set port.
* @param {Number} port
*/
NetAddress.prototype.setPort = function setPort(port) {
assert(port >= 0 && port <= 0xffff);
this.port = port;
this.hostname = IP.toHostname(this.host, port);
};
/**
* Inject properties from host, port, and network.
* @private
* @param {String} host
* @param {Number} port
* @param {(Network|NetworkType)?} network
*/
NetAddress.prototype.fromHost = function fromHost(host, port, network) {
network = Network.get(network);
assert(port >= 0 && port <= 0xffff);
this.raw = IP.toBuffer(host);
this.host = IP.toString(this.raw);
this.port = port;
this.services = NetAddress.DEFAULT_SERVICES;
this.time = network.now();
this.hostname = IP.toHostname(this.host, this.port);
return this;
};
/**
* Instantiate a network address
* from a host and port.
* @param {String} host
* @param {Number} port
* @param {(Network|NetworkType)?} network
* @returns {NetAddress}
*/
NetAddress.fromHost = function fromHost(host, port, network) {
return new NetAddress().fromHost(host, port, network);
};
/**
* Inject properties from hostname and network.
* @private
* @param {String} hostname
* @param {(Network|NetworkType)?} network
*/
NetAddress.prototype.fromHostname = function fromHostname(hostname, network) {
network = Network.get(network);
const addr = IP.fromHostname(hostname, network.port);
return this.fromHost(addr.host, addr.port, network);
};
/**
* Instantiate a network address
* from a hostname (i.e. 127.0.0.1:8333).
* @param {String} hostname
* @param {(Network|NetworkType)?} network
* @returns {NetAddress}
*/
NetAddress.fromHostname = function fromHostname(hostname, network) {
return new NetAddress().fromHostname(hostname, network);
};
/**
* Inject properties from socket.
* @private
* @param {net.Socket} socket
*/
NetAddress.prototype.fromSocket = function fromSocket(socket, network) {
const host = socket.remoteAddress;
const port = socket.remotePort;
assert(typeof host === 'string');
assert(typeof port === 'number');
return this.fromHost(IP.normalize(host), port, network);
};
/**
* Instantiate a network address
* from a socket.
* @param {net.Socket} socket
* @returns {NetAddress}
*/
NetAddress.fromSocket = function fromSocket(hostname, network) {
return new NetAddress().fromSocket(hostname, network);
};
/**
* Inject properties from buffer reader.
* @private
* @param {BufferReader} br
* @param {Boolean?} full - Include timestamp.
*/
NetAddress.prototype.fromReader = function fromReader(br, full) {
this.time = full ? br.readU32() : 0;
this.services = br.readU32();
// Note: hi service bits
// are currently unused.
br.readU32();
this.raw = br.readBytes(16);
this.host = IP.toString(this.raw);
this.port = br.readU16BE();
this.hostname = IP.toHostname(this.host, this.port);
return this;
};
/**
* Inject properties from serialized data.
* @private
* @param {Buffer} data
* @param {Boolean?} full - Include timestamp.
*/
NetAddress.prototype.fromRaw = function fromRaw(data, full) {
return this.fromReader(new BufferReader(data), full);
};
/**
* Insantiate a network address from buffer reader.
* @param {BufferReader} br
* @param {Boolean?} full - Include timestamp.
* @returns {NetAddress}
*/
NetAddress.fromReader = function fromReader(br, full) {
return new NetAddress().fromReader(br, full);
};
/**
* Insantiate a network address from serialized data.
* @param {Buffer} data
* @param {Boolean?} full - Include timestamp.
* @returns {NetAddress}
*/
NetAddress.fromRaw = function fromRaw(data, full) {
return new NetAddress().fromRaw(data, full);
};
/**
* Write network address to a buffer writer.
* @param {BufferWriter} bw
* @param {Boolean?} full - Include timestamp.
* @returns {Buffer}
*/
NetAddress.prototype.toWriter = function toWriter(bw, full) {
if (full)
bw.writeU32(this.time);
bw.writeU32(this.services);
bw.writeU32(0);
bw.writeBytes(this.raw);
bw.writeU16BE(this.port);
return bw;
};
/**
* Calculate serialization size of address.
* @returns {Number}
*/
NetAddress.prototype.getSize = function getSize(full) {
return 26 + (full ? 4 : 0);
};
/**
* Serialize network address.
* @param {Boolean?} full - Include timestamp.
* @returns {Buffer}
*/
NetAddress.prototype.toRaw = function toRaw(full) {
const size = this.getSize(full);
return this.toWriter(new StaticWriter(size), full).render();
};
/**
* Convert net address to json-friendly object.
* @returns {Object}
*/
NetAddress.prototype.toJSON = function toJSON() {
return {
host: this.host,
port: this.port,
services: this.services,
time: this.time
};
};
/**
* Inject properties from json object.
* @private
* @param {Object} json
* @returns {NetAddress}
*/
NetAddress.prototype.fromJSON = function fromJSON(json) {
assert((json.port & 0xffff) === json.port);
assert((json.services >>> 0) === json.services);
assert((json.time >>> 0) === json.time);
this.raw = IP.toBuffer(json.host);
this.host = json.host;
this.port = json.port;
this.services = json.services;
this.time = json.time;
this.hostname = IP.toHostname(this.host, this.port);
return this;
};
/**
* Instantiate net address from json object.
* @param {Object} json
* @returns {NetAddress}
*/
NetAddress.fromJSON = function fromJSON(json) {
return new NetAddress().fromJSON(json);
};
/**
* Inspect the network address.
* @returns {Object}
*/
NetAddress.prototype.inspect = function inspect() {
return '<NetAddress:'
+ ` hostname=${this.hostname}`
+ ` services=${this.services.toString(2)}`
+ ` date=${util.date(this.time)}`
+ '>';
};
/*
* Expose
*/

View File

@ -12,330 +12,334 @@ const BufferReader = require('bufio/lib/reader');
const encoding = require('bufio/lib/encoding');
/**
* Outpoint
* Represents a COutPoint.
* @alias module:primitives.Outpoint
* @constructor
* @param {Hash?} hash
* @param {Number?} index
* @property {Hash} hash
* @property {Number} index
*/
function Outpoint(hash, index) {
if (!(this instanceof Outpoint))
return new Outpoint(hash, index);
class Outpoint {
/**
* Create an outpoint.
* @constructor
* @param {Hash?} hash
* @param {Number?} index
*/
this.hash = encoding.NULL_HASH;
this.index = 0xffffffff;
constructor(hash, index) {
this.hash = encoding.NULL_HASH;
this.index = 0xffffffff;
if (hash != null) {
assert(typeof hash === 'string', 'Hash must be a string.');
assert((index >>> 0) === index, 'Index must be a uint32.');
this.hash = hash;
if (hash != null) {
assert(typeof hash === 'string', 'Hash must be a string.');
assert((index >>> 0) === index, 'Index must be a uint32.');
this.hash = hash;
this.index = index;
}
}
/**
* Inject properties from options object.
* @private
* @param {Object} options
*/
fromOptions(options) {
assert(options, 'Outpoint data is required.');
assert(typeof options.hash === 'string', 'Hash must be a string.');
assert((options.index >>> 0) === options.index, 'Index must be a uint32.');
this.hash = options.hash;
this.index = options.index;
return this;
}
/**
* Instantate outpoint from options object.
* @param {Object} options
* @returns {Outpoint}
*/
static fromOptions(options) {
return new this().fromOptions(options);
}
/**
* Clone the outpoint.
* @returns {Outpoint}
*/
clone() {
const outpoint = new this.constructor();
outpoint.hash = this.value;
outpoint.index = this.index;
return outpoint;
}
/**
* Test equality against another outpoint.
* @param {Outpoint} prevout
* @returns {Boolean}
*/
equals(prevout) {
assert(Outpoint.isOutpoint(prevout));
return this.hash === prevout.hash
&& this.index === prevout.index;
}
/**
* Compare against another outpoint (BIP69).
* @param {Outpoint} prevout
* @returns {Number}
*/
compare(prevout) {
assert(Outpoint.isOutpoint(prevout));
const cmp = strcmp(this.txid(), prevout.txid());
if (cmp !== 0)
return cmp;
return this.index - prevout.index;
}
/**
* Test whether the outpoint is null (hash of zeroes
* with max-u32 index). Used to detect coinbases.
* @returns {Boolean}
*/
isNull() {
return this.index === 0xffffffff && this.hash === encoding.NULL_HASH;
}
/**
* Get little-endian hash.
* @returns {Hash}
*/
rhash() {
return encoding.revHex(this.hash);
}
/**
* Get little-endian hash.
* @returns {Hash}
*/
txid() {
return this.rhash();
}
/**
* Serialize outpoint to a key
* suitable for a hash table.
* @returns {String}
*/
toKey() {
return Outpoint.toKey(this.hash, this.index);
}
/**
* Inject properties from hash table key.
* @private
* @param {String} key
* @returns {Outpoint}
*/
fromKey(key) {
assert(key.length > 64);
this.hash = key.slice(0, 64);
this.index = parseInt(key.slice(64), 10);
return this;
}
/**
* Instantiate outpoint from hash table key.
* @param {String} key
* @returns {Outpoint}
*/
static fromKey(key) {
return new this().fromKey(key);
}
/**
* Write outpoint to a buffer writer.
* @param {BufferWriter} bw
*/
toWriter(bw) {
bw.writeHash(this.hash);
bw.writeU32(this.index);
return bw;
}
/**
* Calculate size of outpoint.
* @returns {Number}
*/
getSize() {
return 36;
}
/**
* Serialize outpoint.
* @returns {Buffer}
*/
toRaw() {
return this.toWriter(new StaticWriter(36)).render();
}
/**
* Inject properties from buffer reader.
* @private
* @param {BufferReader} br
*/
fromReader(br) {
this.hash = br.readHash('hex');
this.index = br.readU32();
return this;
}
/**
* Inject properties from serialized data.
* @private
* @param {Buffer} data
*/
fromRaw(data) {
return this.fromReader(new BufferReader(data));
}
/**
* Instantiate outpoint from a buffer reader.
* @param {BufferReader} br
* @returns {Outpoint}
*/
static fromReader(br) {
return new this().fromReader(br);
}
/**
* Instantiate outpoint from serialized data.
* @param {Buffer} data
* @returns {Outpoint}
*/
static fromRaw(data) {
return new this().fromRaw(data);
}
/**
* Inject properties from json object.
* @private
* @params {Object} json
*/
fromJSON(json) {
assert(json, 'Outpoint data is required.');
assert(typeof json.hash === 'string', 'Hash must be a string.');
assert((json.index >>> 0) === json.index, 'Index must be a uint32.');
this.hash = encoding.revHex(json.hash);
this.index = json.index;
return this;
}
/**
* Convert the outpoint to an object suitable
* for JSON serialization. Note that the hash
* will be reversed to abide by bitcoind's legacy
* of little-endian uint256s.
* @returns {Object}
*/
toJSON() {
return {
hash: encoding.revHex(this.hash),
index: this.index
};
}
/**
* Instantiate outpoint from json object.
* @param {Object} json
* @returns {Outpoint}
*/
static fromJSON(json) {
return new this().fromJSON(json);
}
/**
* Inject properties from tx.
* @private
* @param {TX} tx
* @param {Number} index
*/
fromTX(tx, index) {
assert(tx);
assert(typeof index === 'number');
assert(index >= 0);
this.hash = tx.hash('hex');
this.index = index;
return this;
}
/**
* Instantiate outpoint from tx.
* @param {TX} tx
* @param {Number} index
* @returns {Outpoint}
*/
static fromTX(tx, index) {
return new this().fromTX(tx, index);
}
/**
* Serialize outpoint to a key
* suitable for a hash table.
* @param {Hash} hash
* @param {Number} index
* @returns {String}
*/
static toKey(hash, index) {
assert(typeof hash === 'string');
assert(hash.length === 64);
assert(index >= 0);
return hash + index;
}
/**
* Convert the outpoint to a user-friendly string.
* @returns {String}
*/
inspect() {
return `<Outpoint: ${this.rhash()}/${this.index}>`;
}
/**
* Test an object to see if it is an outpoint.
* @param {Object} obj
* @returns {Boolean}
*/
static isOutpoint(obj) {
return obj instanceof Outpoint;
}
}
/**
* Inject properties from options object.
* @private
* @param {Object} options
*/
Outpoint.prototype.fromOptions = function fromOptions(options) {
assert(options, 'Outpoint data is required.');
assert(typeof options.hash === 'string', 'Hash must be a string.');
assert((options.index >>> 0) === options.index, 'Index must be a uint32.');
this.hash = options.hash;
this.index = options.index;
return this;
};
/**
* Instantate outpoint from options object.
* @param {Object} options
* @returns {Outpoint}
*/
Outpoint.fromOptions = function fromOptions(options) {
return new Outpoint().fromOptions(options);
};
/**
* Clone the outpoint.
* @returns {Outpoint}
*/
Outpoint.prototype.clone = function clone() {
const outpoint = new Outpoint();
outpoint.hash = this.value;
outpoint.index = this.index;
return outpoint;
};
/**
* Test equality against another outpoint.
* @param {Outpoint} prevout
* @returns {Boolean}
*/
Outpoint.prototype.equals = function equals(prevout) {
assert(Outpoint.isOutpoint(prevout));
return this.hash === prevout.hash
&& this.index === prevout.index;
};
/**
* Compare against another outpoint (BIP69).
* @param {Outpoint} prevout
* @returns {Number}
*/
Outpoint.prototype.compare = function compare(prevout) {
assert(Outpoint.isOutpoint(prevout));
const cmp = strcmp(this.txid(), prevout.txid());
if (cmp !== 0)
return cmp;
return this.index - prevout.index;
};
/**
* Test whether the outpoint is null (hash of zeroes
* with max-u32 index). Used to detect coinbases.
* @returns {Boolean}
*/
Outpoint.prototype.isNull = function isNull() {
return this.index === 0xffffffff && this.hash === encoding.NULL_HASH;
};
/**
* Get little-endian hash.
* @returns {Hash}
*/
Outpoint.prototype.rhash = function rhash() {
return encoding.revHex(this.hash);
};
/**
* Get little-endian hash.
* @returns {Hash}
*/
Outpoint.prototype.txid = function txid() {
return this.rhash();
};
/**
* Serialize outpoint to a key
* suitable for a hash table.
* @returns {String}
*/
Outpoint.prototype.toKey = function toKey() {
return Outpoint.toKey(this.hash, this.index);
};
/**
* Inject properties from hash table key.
* @private
* @param {String} key
* @returns {Outpoint}
*/
Outpoint.prototype.fromKey = function fromKey(key) {
assert(key.length > 64);
this.hash = key.slice(0, 64);
this.index = parseInt(key.slice(64), 10);
return this;
};
/**
* Instantiate outpoint from hash table key.
* @param {String} key
* @returns {Outpoint}
*/
Outpoint.fromKey = function fromKey(key) {
return new Outpoint().fromKey(key);
};
/**
* Write outpoint to a buffer writer.
* @param {BufferWriter} bw
*/
Outpoint.prototype.toWriter = function toWriter(bw) {
bw.writeHash(this.hash);
bw.writeU32(this.index);
return bw;
};
/**
* Calculate size of outpoint.
* @returns {Number}
*/
Outpoint.prototype.getSize = function getSize() {
return 36;
};
/**
* Serialize outpoint.
* @returns {Buffer}
*/
Outpoint.prototype.toRaw = function toRaw() {
return this.toWriter(new StaticWriter(36)).render();
};
/**
* Inject properties from buffer reader.
* @private
* @param {BufferReader} br
*/
Outpoint.prototype.fromReader = function fromReader(br) {
this.hash = br.readHash('hex');
this.index = br.readU32();
return this;
};
/**
* Inject properties from serialized data.
* @private
* @param {Buffer} data
*/
Outpoint.prototype.fromRaw = function fromRaw(data) {
return this.fromReader(new BufferReader(data));
};
/**
* Instantiate outpoint from a buffer reader.
* @param {BufferReader} br
* @returns {Outpoint}
*/
Outpoint.fromReader = function fromReader(br) {
return new Outpoint().fromReader(br);
};
/**
* Instantiate outpoint from serialized data.
* @param {Buffer} data
* @returns {Outpoint}
*/
Outpoint.fromRaw = function fromRaw(data) {
return new Outpoint().fromRaw(data);
};
/**
* Inject properties from json object.
* @private
* @params {Object} json
*/
Outpoint.prototype.fromJSON = function fromJSON(json) {
assert(json, 'Outpoint data is required.');
assert(typeof json.hash === 'string', 'Hash must be a string.');
assert((json.index >>> 0) === json.index, 'Index must be a uint32.');
this.hash = encoding.revHex(json.hash);
this.index = json.index;
return this;
};
/**
* Convert the outpoint to an object suitable
* for JSON serialization. Note that the hash
* will be reversed to abide by bitcoind's legacy
* of little-endian uint256s.
* @returns {Object}
*/
Outpoint.prototype.toJSON = function toJSON() {
return {
hash: encoding.revHex(this.hash),
index: this.index
};
};
/**
* Instantiate outpoint from json object.
* @param {Object} json
* @returns {Outpoint}
*/
Outpoint.fromJSON = function fromJSON(json) {
return new Outpoint().fromJSON(json);
};
/**
* Inject properties from tx.
* @private
* @param {TX} tx
* @param {Number} index
*/
Outpoint.prototype.fromTX = function fromTX(tx, index) {
assert(tx);
assert(typeof index === 'number');
assert(index >= 0);
this.hash = tx.hash('hex');
this.index = index;
return this;
};
/**
* Instantiate outpoint from tx.
* @param {TX} tx
* @param {Number} index
* @returns {Outpoint}
*/
Outpoint.fromTX = function fromTX(tx, index) {
return new Outpoint().fromTX(tx, index);
};
/**
* Serialize outpoint to a key
* suitable for a hash table.
* @param {Hash} hash
* @param {Number} index
* @returns {String}
*/
Outpoint.toKey = function toKey(hash, index) {
assert(typeof hash === 'string');
assert(hash.length === 64);
assert(index >= 0);
return hash + index;
};
/**
* Convert the outpoint to a user-friendly string.
* @returns {String}
*/
Outpoint.prototype.inspect = function inspect() {
return `<Outpoint: ${this.rhash()}/${this.index}>`;
};
/**
* Test an object to see if it is an outpoint.
* @param {Object} obj
* @returns {Boolean}
*/
Outpoint.isOutpoint = function isOutpoint(obj) {
return obj instanceof Outpoint;
};
/*
* Helpers
*/

View File

@ -20,357 +20,360 @@ const policy = require('../protocol/policy');
/**
* Represents a transaction output.
* @alias module:primitives.Output
* @constructor
* @param {NakedOutput} options
* @property {Amount} value - Value in satoshis.
* @property {Amount} value
* @property {Script} script
*/
function Output(options) {
if (!(this instanceof Output))
return new Output(options);
class Output {
/**
* Create an output.
* @constructor
* @param {Object?} options
*/
this.value = 0;
this.script = new Script();
constructor(options) {
this.value = 0;
this.script = new Script();
if (options)
this.fromOptions(options);
}
if (options)
this.fromOptions(options);
}
/**
* Inject properties from options object.
* @private
* @param {NakedOutput} options
*/
/**
* Inject properties from options object.
* @private
* @param {NakedOutput} options
*/
Output.prototype.fromOptions = function fromOptions(options) {
assert(options, 'Output data is required.');
fromOptions(options) {
assert(options, 'Output data is required.');
if (options.value) {
assert(Number.isSafeInteger(options.value) && options.value >= 0,
if (options.value) {
assert(Number.isSafeInteger(options.value) && options.value >= 0,
'Value must be a uint64.');
this.value = options.value;
}
if (options.script)
this.script.fromOptions(options.script);
if (options.address)
this.script.fromAddress(options.address);
return this;
}
/**
* Instantiate output from options object.
* @param {NakedOutput} options
* @returns {Output}
*/
static fromOptions(options) {
return new this().fromOptions(options);
}
/**
* Inject properties from script/value pair.
* @private
* @param {Script|Address} script
* @param {Amount} value
* @returns {Output}
*/
fromScript(script, value) {
if (typeof script === 'string')
script = Address.fromString(script);
if (script instanceof Address)
script = Script.fromAddress(script);
assert(script instanceof Script, 'Script must be a Script.');
assert(Number.isSafeInteger(value) && value >= 0, 'Value must be a uint64.');
this.script = script;
this.value = value;
return this;
}
/**
* Instantiate output from script/value pair.
* @param {Script|Address} script
* @param {Amount} value
* @returns {Output}
*/
static fromScript(script, value) {
return new this().fromScript(script, value);
}
/**
* Clone the output.
* @returns {Output}
*/
clone() {
const output = new this.constructor();
output.value = this.value;
output.script.inject(this.script);
return output;
}
/**
* Test equality against another output.
* @param {Output} output
* @returns {Boolean}
*/
equals(output) {
assert(Output.isOutput(output));
return this.value === output.value
&& this.script.equals(output.script);
}
/**
* Compare against another output (BIP69).
* @param {Output} output
* @returns {Number}
*/
compare(output) {
assert(Output.isOutput(output));
const cmp = this.value - output.value;
if (cmp !== 0)
return cmp;
return this.script.compare(output.script);
}
/**
* Get the script type as a string.
* @returns {ScriptType} type
*/
getType() {
return Script.typesByVal[this.script.getType()].toLowerCase();
}
/**
* Get the address.
* @returns {Address} address
*/
getAddress() {
return this.script.getAddress();
}
/**
* Get the address hash.
* @param {String?} enc
* @returns {Hash} hash
*/
getHash(enc) {
const addr = this.getAddress();
if (!addr)
return null;
return addr.getHash(enc);
}
/**
* Convert the input to a more user-friendly object.
* @returns {Object}
*/
inspect() {
return {
type: this.getType(),
value: Amount.btc(this.value),
script: this.script,
address: this.getAddress()
};
}
/**
* Convert the output to an object suitable
* for JSON serialization.
* @returns {Object}
*/
toJSON() {
return this.getJSON();
}
/**
* Convert the output to an object suitable
* for JSON serialization.
* @param {Network} network
* @returns {Object}
*/
getJSON(network) {
let addr = this.getAddress();
network = Network.get(network);
if (addr)
addr = addr.toString(network);
return {
value: this.value,
script: this.script.toJSON(),
address: addr
};
}
/**
* Calculate the dust threshold for this
* output, based on serialize size and rate.
* @param {Rate?} rate
* @returns {Amount}
*/
getDustThreshold(rate) {
const scale = consensus.WITNESS_SCALE_FACTOR;
if (this.script.isUnspendable())
return 0;
let size = this.getSize();
if (this.script.isProgram()) {
// 75% segwit discount applied to script size.
size += 32 + 4 + 1 + (107 / scale | 0) + 4;
} else {
size += 32 + 4 + 1 + 107 + 4;
}
return 3 * policy.getMinFee(size, rate);
}
/**
* Calculate size of serialized output.
* @returns {Number}
*/
getSize() {
return 8 + this.script.getVarSize();
}
/**
* Test whether the output should be considered dust.
* @param {Rate?} rate
* @returns {Boolean}
*/
isDust(rate) {
return this.value < this.getDustThreshold(rate);
}
/**
* Inject properties from a JSON object.
* @private
* @param {Object} json
*/
fromJSON(json) {
assert(json, 'Output data is required.');
assert(Number.isSafeInteger(json.value) && json.value >= 0,
'Value must be a uint64.');
this.value = options.value;
this.value = json.value;
this.script.fromJSON(json.script);
return this;
}
if (options.script)
this.script.fromOptions(options.script);
/**
* Instantiate an Output from a jsonified output object.
* @param {Object} json - The jsonified output object.
* @returns {Output}
*/
if (options.address)
this.script.fromAddress(options.address);
return this;
};
/**
* Instantiate output from options object.
* @param {NakedOutput} options
* @returns {Output}
*/
Output.fromOptions = function fromOptions(options) {
return new Output().fromOptions(options);
};
/**
* Inject properties from script/value pair.
* @private
* @param {Script|Address} script
* @param {Amount} value
* @returns {Output}
*/
Output.prototype.fromScript = function fromScript(script, value) {
if (typeof script === 'string')
script = Address.fromString(script);
if (script instanceof Address)
script = Script.fromAddress(script);
assert(script instanceof Script, 'Script must be a Script.');
assert(Number.isSafeInteger(value) && value >= 0, 'Value must be a uint64.');
this.script = script;
this.value = value;
return this;
};
/**
* Instantiate output from script/value pair.
* @param {Script|Address} script
* @param {Amount} value
* @returns {Output}
*/
Output.fromScript = function fromScript(script, value) {
return new Output().fromScript(script, value);
};
/**
* Clone the output.
* @returns {Output}
*/
Output.prototype.clone = function clone() {
const output = new Output();
output.value = this.value;
output.script.inject(this.script);
return output;
};
/**
* Test equality against another output.
* @param {Output} output
* @returns {Boolean}
*/
Output.prototype.equals = function equals(output) {
assert(Output.isOutput(output));
return this.value === output.value
&& this.script.equals(output.script);
};
/**
* Compare against another output (BIP69).
* @param {Output} output
* @returns {Number}
*/
Output.prototype.compare = function compare(output) {
assert(Output.isOutput(output));
const cmp = this.value - output.value;
if (cmp !== 0)
return cmp;
return this.script.compare(output.script);
};
/**
* Get the script type as a string.
* @returns {ScriptType} type
*/
Output.prototype.getType = function getType() {
return Script.typesByVal[this.script.getType()].toLowerCase();
};
/**
* Get the address.
* @returns {Address} address
*/
Output.prototype.getAddress = function getAddress() {
return this.script.getAddress();
};
/**
* Get the address hash.
* @param {String?} enc
* @returns {Hash} hash
*/
Output.prototype.getHash = function getHash(enc) {
const addr = this.getAddress();
if (!addr)
return null;
return addr.getHash(enc);
};
/**
* Convert the input to a more user-friendly object.
* @returns {Object}
*/
Output.prototype.inspect = function inspect() {
return {
type: this.getType(),
value: Amount.btc(this.value),
script: this.script,
address: this.getAddress()
};
};
/**
* Convert the output to an object suitable
* for JSON serialization.
* @returns {Object}
*/
Output.prototype.toJSON = function toJSON() {
return this.getJSON();
};
/**
* Convert the output to an object suitable
* for JSON serialization.
* @param {Network} network
* @returns {Object}
*/
Output.prototype.getJSON = function getJSON(network) {
let addr = this.getAddress();
network = Network.get(network);
if (addr)
addr = addr.toString(network);
return {
value: this.value,
script: this.script.toJSON(),
address: addr
};
};
/**
* Calculate the dust threshold for this
* output, based on serialize size and rate.
* @param {Rate?} rate
* @returns {Amount}
*/
Output.prototype.getDustThreshold = function getDustThreshold(rate) {
const scale = consensus.WITNESS_SCALE_FACTOR;
if (this.script.isUnspendable())
return 0;
let size = this.getSize();
if (this.script.isProgram()) {
// 75% segwit discount applied to script size.
size += 32 + 4 + 1 + (107 / scale | 0) + 4;
} else {
size += 32 + 4 + 1 + 107 + 4;
static fromJSON(json) {
return new this().fromJSON(json);
}
return 3 * policy.getMinFee(size, rate);
};
/**
* Write the output to a buffer writer.
* @param {BufferWriter} bw
*/
/**
* Calculate size of serialized output.
* @returns {Number}
*/
toWriter(bw) {
bw.writeI64(this.value);
bw.writeVarBytes(this.script.toRaw());
return bw;
}
Output.prototype.getSize = function getSize() {
return 8 + this.script.getVarSize();
};
/**
* Serialize the output.
* @param {String?} enc - Encoding, can be `'hex'` or null.
* @returns {Buffer|String}
*/
/**
* Test whether the output should be considered dust.
* @param {Rate?} rate
* @returns {Boolean}
*/
toRaw() {
const size = this.getSize();
return this.toWriter(new StaticWriter(size)).render();
}
Output.prototype.isDust = function isDust(rate) {
return this.value < this.getDustThreshold(rate);
};
/**
* Inject properties from buffer reader.
* @private
* @param {BufferReader} br
*/
/**
* Inject properties from a JSON object.
* @private
* @param {Object} json
*/
fromReader(br) {
this.value = br.readI64();
this.script.fromRaw(br.readVarBytes());
return this;
}
Output.prototype.fromJSON = function fromJSON(json) {
assert(json, 'Output data is required.');
assert(Number.isSafeInteger(json.value) && json.value >= 0,
'Value must be a uint64.');
this.value = json.value;
this.script.fromJSON(json.script);
return this;
};
/**
* Inject properties from serialized data.
* @private
* @param {Buffer} data
*/
/**
* Instantiate an Output from a jsonified output object.
* @param {Object} json - The jsonified output object.
* @returns {Output}
*/
fromRaw(data) {
return this.fromReader(new BufferReader(data));
}
Output.fromJSON = function fromJSON(json) {
return new Output().fromJSON(json);
};
/**
* Instantiate an output from a buffer reader.
* @param {BufferReader} br
* @returns {Output}
*/
/**
* Write the output to a buffer writer.
* @param {BufferWriter} bw
*/
static fromReader(br) {
return new this().fromReader(br);
}
Output.prototype.toWriter = function toWriter(bw) {
bw.writeI64(this.value);
bw.writeVarBytes(this.script.toRaw());
return bw;
};
/**
* Instantiate an output from a serialized Buffer.
* @param {Buffer} data
* @param {String?} enc - Encoding, can be `'hex'` or null.
* @returns {Output}
*/
/**
* Serialize the output.
* @param {String?} enc - Encoding, can be `'hex'` or null.
* @returns {Buffer|String}
*/
static fromRaw(data, enc) {
if (typeof data === 'string')
data = Buffer.from(data, enc);
return new this().fromRaw(data);
}
Output.prototype.toRaw = function toRaw() {
const size = this.getSize();
return this.toWriter(new StaticWriter(size)).render();
};
/**
* Test an object to see if it is an Output.
* @param {Object} obj
* @returns {Boolean}
*/
/**
* Inject properties from buffer reader.
* @private
* @param {BufferReader} br
*/
Output.prototype.fromReader = function fromReader(br) {
this.value = br.readI64();
this.script.fromRaw(br.readVarBytes());
return this;
};
/**
* Inject properties from serialized data.
* @private
* @param {Buffer} data
*/
Output.prototype.fromRaw = function fromRaw(data) {
return this.fromReader(new BufferReader(data));
};
/**
* Instantiate an output from a buffer reader.
* @param {BufferReader} br
* @returns {Output}
*/
Output.fromReader = function fromReader(br) {
return new Output().fromReader(br);
};
/**
* Instantiate an output from a serialized Buffer.
* @param {Buffer} data
* @param {String?} enc - Encoding, can be `'hex'` or null.
* @returns {Output}
*/
Output.fromRaw = function fromRaw(data, enc) {
if (typeof data === 'string')
data = Buffer.from(data, enc);
return new Output().fromRaw(data);
};
/**
* Test an object to see if it is an Output.
* @param {Object} obj
* @returns {Boolean}
*/
Output.isOutput = function isOutput(obj) {
return obj instanceof Output;
};
static isOutput(obj) {
return obj instanceof Output;
}
}
/*
* Expose

File diff suppressed because it is too large Load Diff

View File

@ -14,293 +14,297 @@ const BufferReader = require('bufio/lib/reader');
const encoding = require('bufio/lib/encoding');
/**
* TXMeta
* An extended transaction object.
* @alias module:primitives.TXMeta
* @constructor
* @param {Object} options
*/
function TXMeta(options) {
if (!(this instanceof TXMeta))
return new TXMeta(options);
class TXMeta {
/**
* Create an extended transaction.
* @constructor
* @param {Object?} options
*/
this.tx = new TX();
this.mtime = util.now();
this.height = -1;
this.block = null;
this.time = 0;
this.index = -1;
constructor(options) {
this.tx = new TX();
this.mtime = util.now();
this.height = -1;
this.block = null;
this.time = 0;
this.index = -1;
if (options)
this.fromOptions(options);
if (options)
this.fromOptions(options);
}
/**
* Inject properties from options object.
* @private
* @param {Object} options
*/
fromOptions(options) {
if (options.tx) {
assert(options.tx instanceof TX);
this.tx = options.tx;
}
if (options.mtime != null) {
assert((options.mtime >>> 0) === options.mtime);
this.mtime = options.mtime;
}
if (options.height != null) {
assert(Number.isSafeInteger(options.height));
this.height = options.height;
}
if (options.block !== undefined) {
assert(options.block === null || typeof options.block === 'string');
this.block = options.block;
}
if (options.time != null) {
assert((options.time >>> 0) === options.time);
this.time = options.time;
}
if (options.index != null) {
assert(Number.isSafeInteger(options.index));
this.index = options.index;
}
return this;
}
/**
* Instantiate TXMeta from options.
* @param {Object} options
* @returns {TXMeta}
*/
static fromOptions(options) {
return new this().fromOptions(options);
}
/**
* Inject properties from options object.
* @private
* @param {Object} options
*/
fromTX(tx, entry, index) {
this.tx = tx;
if (entry) {
this.height = entry.height;
this.block = entry.hash;
this.time = entry.time;
this.index = index;
}
return this;
}
/**
* Instantiate TXMeta from options.
* @param {Object} options
* @returns {TXMeta}
*/
static fromTX(tx, entry, index) {
return new this().fromTX(tx, entry, index);
}
/**
* Inspect the transaction.
* @returns {Object}
*/
inspect() {
return this.format();
}
/**
* Inspect the transaction.
* @returns {Object}
*/
format(view) {
const data = this.tx.format(view, null, this.index);
data.mtime = this.mtime;
data.height = this.height;
data.block = this.block ? encoding.revHex(this.block) : null;
data.time = this.time;
return data;
}
/**
* Convert transaction to JSON.
* @returns {Object}
*/
toJSON() {
return this.getJSON();
}
/**
* Convert the transaction to an object suitable
* for JSON serialization.
* @param {Network} network
* @param {CoinView} view
* @returns {Object}
*/
getJSON(network, view, chainHeight) {
const json = this.tx.getJSON(network, view, null, this.index);
json.mtime = this.mtime;
json.height = this.height;
json.block = this.block ? encoding.revHex(this.block) : null;
json.time = this.time;
json.confirmations = 0;
if (chainHeight != null)
json.confirmations = chainHeight - this.height + 1;
return json;
}
/**
* Inject properties from a json object.
* @private
* @param {Object} json
*/
fromJSON(json) {
this.tx.fromJSON(json);
assert((json.mtime >>> 0) === json.mtime);
assert(Number.isSafeInteger(json.height));
assert(!json.block || typeof json.block === 'string');
assert((json.time >>> 0) === json.time);
assert(Number.isSafeInteger(json.index));
this.mtime = json.mtime;
this.height = json.height;
this.block = encoding.revHex(json.block);
this.index = json.index;
return this;
}
/**
* Instantiate a transaction from a
* jsonified transaction object.
* @param {Object} json - The jsonified transaction object.
* @returns {TX}
*/
static fromJSON(json) {
return new this().fromJSON(JSON);
}
/**
* Calculate serialization size.
* @returns {Number}
*/
getSize() {
let size = 0;
size += this.tx.getSize();
size += 4;
if (this.block) {
size += 1;
size += 32;
size += 4 * 3;
} else {
size += 1;
}
return size;
}
/**
* Serialize a transaction to "extended format".
* This is the serialization format bcoin uses internally
* to store transactions in the database. The extended
* serialization includes the height, block hash, index,
* timestamp, and pending-since time.
* @returns {Buffer}
*/
toRaw() {
const size = this.getSize();
const bw = new StaticWriter(size);
this.tx.toWriter(bw);
bw.writeU32(this.mtime);
if (this.block) {
bw.writeU8(1);
bw.writeHash(this.block);
bw.writeU32(this.height);
bw.writeU32(this.time);
bw.writeU32(this.index);
} else {
bw.writeU8(0);
}
return bw.render();
}
/**
* Inject properties from "extended" serialization format.
* @private
* @param {Buffer} data
*/
fromRaw(data) {
const br = new BufferReader(data);
this.tx.fromReader(br);
this.mtime = br.readU32();
if (br.readU8() === 1) {
this.block = br.readHash('hex');
this.height = br.readU32();
this.time = br.readU32();
this.index = br.readU32();
if (this.index === 0x7fffffff)
this.index = -1;
}
return this;
}
/**
* Instantiate a transaction from a Buffer
* in "extended" serialization format.
* @param {Buffer} data
* @param {String?} enc - One of `"hex"` or `null`.
* @returns {TX}
*/
static fromRaw(data, enc) {
if (typeof data === 'string')
data = Buffer.from(data, enc);
return new this().fromRaw(data);
}
/**
* Test whether an object is an TXMeta.
* @param {Object} obj
* @returns {Boolean}
*/
static isTXMeta(obj) {
return obj instanceof TXMeta;
}
}
/**
* Inject properties from options object.
* @private
* @param {Object} options
*/
TXMeta.prototype.fromOptions = function fromOptions(options) {
if (options.tx) {
assert(options.tx instanceof TX);
this.tx = options.tx;
}
if (options.mtime != null) {
assert((options.mtime >>> 0) === options.mtime);
this.mtime = options.mtime;
}
if (options.height != null) {
assert(Number.isSafeInteger(options.height));
this.height = options.height;
}
if (options.block !== undefined) {
assert(options.block === null || typeof options.block === 'string');
this.block = options.block;
}
if (options.time != null) {
assert((options.time >>> 0) === options.time);
this.time = options.time;
}
if (options.index != null) {
assert(Number.isSafeInteger(options.index));
this.index = options.index;
}
return this;
};
/**
* Instantiate TXMeta from options.
* @param {Object} options
* @returns {TXMeta}
*/
TXMeta.fromOptions = function fromOptions(options) {
return new TXMeta().fromOptions(options);
};
/**
* Inject properties from options object.
* @private
* @param {Object} options
*/
TXMeta.prototype.fromTX = function fromTX(tx, entry, index) {
this.tx = tx;
if (entry) {
this.height = entry.height;
this.block = entry.hash;
this.time = entry.time;
this.index = index;
}
return this;
};
/**
* Instantiate TXMeta from options.
* @param {Object} options
* @returns {TXMeta}
*/
TXMeta.fromTX = function fromTX(tx, entry, index) {
return new TXMeta().fromTX(tx, entry, index);
};
/**
* Inspect the transaction.
* @returns {Object}
*/
TXMeta.prototype.inspect = function inspect() {
return this.format();
};
/**
* Inspect the transaction.
* @returns {Object}
*/
TXMeta.prototype.format = function format(view) {
const data = this.tx.format(view, null, this.index);
data.mtime = this.mtime;
data.height = this.height;
data.block = this.block ? encoding.revHex(this.block) : null;
data.time = this.time;
return data;
};
/**
* Convert transaction to JSON.
* @returns {Object}
*/
TXMeta.prototype.toJSON = function toJSON() {
return this.getJSON();
};
/**
* Convert the transaction to an object suitable
* for JSON serialization.
* @param {Network} network
* @param {CoinView} view
* @returns {Object}
*/
TXMeta.prototype.getJSON = function getJSON(network, view, chainHeight) {
const json = this.tx.getJSON(network, view, null, this.index);
json.mtime = this.mtime;
json.height = this.height;
json.block = this.block ? encoding.revHex(this.block) : null;
json.time = this.time;
json.confirmations = 0;
if (chainHeight != null)
json.confirmations = chainHeight - this.height + 1;
return json;
};
/**
* Inject properties from a json object.
* @private
* @param {Object} json
*/
TXMeta.prototype.fromJSON = function fromJSON(json) {
this.tx.fromJSON(json);
assert((json.mtime >>> 0) === json.mtime);
assert(Number.isSafeInteger(json.height));
assert(!json.block || typeof json.block === 'string');
assert((json.time >>> 0) === json.time);
assert(Number.isSafeInteger(json.index));
this.mtime = json.mtime;
this.height = json.height;
this.block = encoding.revHex(json.block);
this.index = json.index;
return this;
};
/**
* Instantiate a transaction from a
* jsonified transaction object.
* @param {Object} json - The jsonified transaction object.
* @returns {TX}
*/
TXMeta.fromJSON = function fromJSON(json) {
return new TXMeta().fromJSON(JSON);
};
/**
* Calculate serialization size.
* @returns {Number}
*/
TXMeta.prototype.getSize = function getSize() {
let size = 0;
size += this.tx.getSize();
size += 4;
if (this.block) {
size += 1;
size += 32;
size += 4 * 3;
} else {
size += 1;
}
return size;
};
/**
* Serialize a transaction to "extended format".
* This is the serialization format bcoin uses internally
* to store transactions in the database. The extended
* serialization includes the height, block hash, index,
* timestamp, and pending-since time.
* @returns {Buffer}
*/
TXMeta.prototype.toRaw = function toRaw() {
const size = this.getSize();
const bw = new StaticWriter(size);
this.tx.toWriter(bw);
bw.writeU32(this.mtime);
if (this.block) {
bw.writeU8(1);
bw.writeHash(this.block);
bw.writeU32(this.height);
bw.writeU32(this.time);
bw.writeU32(this.index);
} else {
bw.writeU8(0);
}
return bw.render();
};
/**
* Inject properties from "extended" serialization format.
* @private
* @param {Buffer} data
*/
TXMeta.prototype.fromRaw = function fromRaw(data) {
const br = new BufferReader(data);
this.tx.fromReader(br);
this.mtime = br.readU32();
if (br.readU8() === 1) {
this.block = br.readHash('hex');
this.height = br.readU32();
this.time = br.readU32();
this.index = br.readU32();
if (this.index === 0x7fffffff)
this.index = -1;
}
return this;
};
/**
* Instantiate a transaction from a Buffer
* in "extended" serialization format.
* @param {Buffer} data
* @param {String?} enc - One of `"hex"` or `null`.
* @returns {TX}
*/
TXMeta.fromRaw = function fromRaw(data, enc) {
if (typeof data === 'string')
data = Buffer.from(data, enc);
return new TXMeta().fromRaw(data);
};
/**
* Test whether an object is an TXMeta.
* @param {Object} obj
* @returns {Boolean}
*/
TXMeta.isTXMeta = function isTXMeta(obj) {
return obj instanceof TXMeta;
};
/*
* Expose
*/

View File

@ -12,273 +12,276 @@ const KeyRing = require('../primitives/keyring');
const Path = require('./path');
/**
* Wallet Key
* Represents a key ring which amounts to an address.
* @alias module:wallet.WalletKey
* @constructor
* @param {Object} options
* @extends KeyRing
*/
function WalletKey(options) {
if (!(this instanceof WalletKey))
return new WalletKey(options);
class WalletKey extends KeyRing {
/**
* Create a wallet key.
* @constructor
* @param {Object?} options
*/
KeyRing.call(this, options);
constructor(options) {
super(options);
this.keyType = Path.types.HD;
this.keyType = Path.types.HD;
this.name = null;
this.account = -1;
this.branch = -1;
this.index = -1;
}
Object.setPrototypeOf(WalletKey.prototype, KeyRing.prototype);
/**
* Instantiate key ring from options.
* @param {Object} options
* @returns {WalletKey}
*/
WalletKey.fromOptions = function fromOptions(options) {
return new WalletKey().fromOptions(options);
};
/**
* Instantiate wallet key from a private key.
* @param {Buffer} key
* @param {Boolean?} compressed
* @returns {WalletKey}
*/
WalletKey.fromPrivate = function fromPrivate(key, compressed) {
return new WalletKey().fromPrivate(key, compressed);
};
/**
* Generate a wallet key.
* @param {Boolean?} compressed
* @returns {WalletKey}
*/
WalletKey.generate = function generate(compressed) {
return new WalletKey().generate(compressed);
};
/**
* Instantiate wallet key from a public key.
* @param {Buffer} publicKey
* @returns {WalletKey}
*/
WalletKey.fromPublic = function fromPublic(key) {
return new WalletKey().fromPublic(key);
};
/**
* Instantiate wallet key from a public key.
* @param {Buffer} publicKey
* @returns {WalletKey}
*/
WalletKey.fromKey = function fromKey(key, compressed) {
return new WalletKey().fromKey(key, compressed);
};
/**
* Instantiate wallet key from script.
* @param {Buffer} key
* @param {Script} script
* @returns {WalletKey}
*/
WalletKey.fromScript = function fromScript(key, script, compressed) {
return new WalletKey().fromScript(key, script, compressed);
};
/**
* Instantiate a wallet key from a serialized CBitcoinSecret.
* @param {Base58String} secret
* @param {Network?} network
* @returns {WalletKey}
*/
WalletKey.fromSecret = function fromSecret(data, network) {
return new WalletKey().fromSecret(data, network);
};
/**
* Convert an WalletKey to a more json-friendly object.
* @returns {Object}
*/
WalletKey.prototype.toJSON = function toJSON(network) {
return {
name: this.name,
account: this.account,
branch: this.branch,
index: this.index,
witness: this.witness,
nested: this.nested,
publicKey: this.publicKey.toString('hex'),
script: this.script ? this.script.toRaw().toString('hex') : null,
program: this.witness ? this.getProgram().toRaw().toString('hex') : null,
type: Address.typesByVal[this.getType()].toLowerCase(),
address: this.getAddress('string', network)
};
};
/**
* Instantiate an WalletKey from a jsonified transaction object.
* @param {Object} json - The jsonified transaction object.
* @returns {WalletKey}
*/
WalletKey.fromJSON = function fromJSON(json) {
return new WalletKey().fromJSON(json);
};
/**
* Instantiate a wallet key from serialized data.
* @param {Buffer} data
* @returns {WalletKey}
*/
WalletKey.fromRaw = function fromRaw(data) {
return new WalletKey().fromRaw(data);
};
/**
* Inject properties from hd key.
* @private
* @param {Account} account
* @param {HDPrivateKey|HDPublicKey} key
* @param {Number} branch
* @param {Number} index
* @returns {WalletKey}
*/
WalletKey.prototype.fromHD = function fromHD(account, key, branch, index) {
this.keyType = Path.types.HD;
this.name = account.name;
this.account = account.accountIndex;
this.branch = branch;
this.index = index;
this.witness = account.witness;
this.nested = branch === 2;
if (key.privateKey)
return this.fromPrivate(key.privateKey);
return this.fromPublic(key.publicKey);
};
/**
* Instantiate a wallet key from hd key.
* @param {Account} account
* @param {HDPrivateKey|HDPublicKey} key
* @param {Number} branch
* @param {Number} index
* @returns {WalletKey}
*/
WalletKey.fromHD = function fromHD(account, key, branch, index) {
return new WalletKey().fromHD(account, key, branch, index);
};
/**
* Inject properties from imported data.
* @private
* @param {Account} account
* @param {Buffer} data
* @returns {WalletKey}
*/
WalletKey.prototype.fromImport = function fromImport(account, data) {
this.keyType = Path.types.KEY;
this.name = account.name;
this.account = account.accountIndex;
this.witness = account.witness;
return this.fromRaw(data);
};
/**
* Instantiate a wallet key from imported data.
* @param {Account} account
* @param {Buffer} data
* @returns {WalletKey}
*/
WalletKey.fromImport = function fromImport(account, data) {
return new WalletKey().fromImport(account, data);
};
/**
* Inject properties from key.
* @private
* @param {Account} account
* @param {KeyRing} ring
* @returns {WalletKey}
*/
WalletKey.prototype.fromRing = function fromRing(account, ring) {
this.keyType = Path.types.KEY;
this.name = account.name;
this.account = account.accountIndex;
this.witness = account.witness;
return this.fromOptions(ring);
};
/**
* Instantiate a wallet key from regular key.
* @param {Account} account
* @param {KeyRing} ring
* @returns {WalletKey}
*/
WalletKey.fromRing = function fromRing(account, ring) {
return new WalletKey().fromRing(account, ring);
};
/**
* Convert wallet key to a path.
* @returns {Path}
*/
WalletKey.prototype.toPath = function toPath() {
const path = new Path();
path.name = this.name;
path.account = this.account;
switch (this.keyType) {
case Path.types.HD:
path.branch = this.branch;
path.index = this.index;
break;
case Path.types.KEY:
path.data = this.toRaw();
break;
this.name = null;
this.account = -1;
this.branch = -1;
this.index = -1;
}
path.keyType = this.keyType;
/**
* Instantiate key ring from options.
* @param {Object} options
* @returns {WalletKey}
*/
path.version = this.getVersion();
path.type = this.getType();
path.hash = this.getHash('hex');
static fromOptions(options) {
return new this().fromOptions(options);
}
return path;
};
/**
* Instantiate wallet key from a private key.
* @param {Buffer} key
* @param {Boolean?} compressed
* @returns {WalletKey}
*/
/**
* Test whether an object is a WalletKey.
* @param {Object} obj
* @returns {Boolean}
*/
static fromPrivate(key, compressed) {
return new this().fromPrivate(key, compressed);
}
WalletKey.isWalletKey = function isWalletKey(obj) {
return obj instanceof WalletKey;
};
/**
* Generate a wallet key.
* @param {Boolean?} compressed
* @returns {WalletKey}
*/
static generate(compressed) {
return new this().generate(compressed);
}
/**
* Instantiate wallet key from a public key.
* @param {Buffer} publicKey
* @returns {WalletKey}
*/
static fromPublic(key) {
return new this().fromPublic(key);
}
/**
* Instantiate wallet key from a public key.
* @param {Buffer} publicKey
* @returns {WalletKey}
*/
static fromKey(key, compressed) {
return new this().fromKey(key, compressed);
}
/**
* Instantiate wallet key from script.
* @param {Buffer} key
* @param {Script} script
* @returns {WalletKey}
*/
static fromScript(key, script, compressed) {
return new this().fromScript(key, script, compressed);
}
/**
* Instantiate a wallet key from a serialized CBitcoinSecret.
* @param {Base58String} secret
* @param {Network?} network
* @returns {WalletKey}
*/
static fromSecret(data, network) {
return new this().fromSecret(data, network);
}
/**
* Convert an WalletKey to a more json-friendly object.
* @returns {Object}
*/
toJSON(network) {
return {
name: this.name,
account: this.account,
branch: this.branch,
index: this.index,
witness: this.witness,
nested: this.nested,
publicKey: this.publicKey.toString('hex'),
script: this.script ? this.script.toRaw().toString('hex') : null,
program: this.witness ? this.getProgram().toRaw().toString('hex') : null,
type: Address.typesByVal[this.getType()].toLowerCase(),
address: this.getAddress('string', network)
};
}
/**
* Instantiate an WalletKey from a jsonified transaction object.
* @param {Object} json - The jsonified transaction object.
* @returns {WalletKey}
*/
static fromJSON(json) {
return new this().fromJSON(json);
}
/**
* Instantiate a wallet key from serialized data.
* @param {Buffer} data
* @returns {WalletKey}
*/
static fromRaw(data) {
return new this().fromRaw(data);
}
/**
* Inject properties from hd key.
* @private
* @param {Account} account
* @param {HDPrivateKey|HDPublicKey} key
* @param {Number} branch
* @param {Number} index
* @returns {WalletKey}
*/
fromHD(account, key, branch, index) {
this.keyType = Path.types.HD;
this.name = account.name;
this.account = account.accountIndex;
this.branch = branch;
this.index = index;
this.witness = account.witness;
this.nested = branch === 2;
if (key.privateKey)
return this.fromPrivate(key.privateKey);
return this.fromPublic(key.publicKey);
}
/**
* Instantiate a wallet key from hd key.
* @param {Account} account
* @param {HDPrivateKey|HDPublicKey} key
* @param {Number} branch
* @param {Number} index
* @returns {WalletKey}
*/
static fromHD(account, key, branch, index) {
return new this().fromHD(account, key, branch, index);
}
/**
* Inject properties from imported data.
* @private
* @param {Account} account
* @param {Buffer} data
* @returns {WalletKey}
*/
fromImport(account, data) {
this.keyType = Path.types.KEY;
this.name = account.name;
this.account = account.accountIndex;
this.witness = account.witness;
return this.fromRaw(data);
}
/**
* Instantiate a wallet key from imported data.
* @param {Account} account
* @param {Buffer} data
* @returns {WalletKey}
*/
static fromImport(account, data) {
return new this().fromImport(account, data);
}
/**
* Inject properties from key.
* @private
* @param {Account} account
* @param {KeyRing} ring
* @returns {WalletKey}
*/
fromRing(account, ring) {
this.keyType = Path.types.KEY;
this.name = account.name;
this.account = account.accountIndex;
this.witness = account.witness;
return this.fromOptions(ring);
}
/**
* Instantiate a wallet key from regular key.
* @param {Account} account
* @param {KeyRing} ring
* @returns {WalletKey}
*/
static fromRing(account, ring) {
return new this().fromRing(account, ring);
}
/**
* Convert wallet key to a path.
* @returns {Path}
*/
toPath() {
const path = new Path();
path.name = this.name;
path.account = this.account;
switch (this.keyType) {
case Path.types.HD:
path.branch = this.branch;
path.index = this.index;
break;
case Path.types.KEY:
path.data = this.toRaw();
break;
}
path.keyType = this.keyType;
path.version = this.getVersion();
path.type = this.getType();
path.hash = this.getHash('hex');
return path;
}
/**
* Test whether an object is a WalletKey.
* @param {Object} obj
* @returns {Boolean}
*/
static isWalletKey(obj) {
return obj instanceof WalletKey;
}
}
/*
* Expose

View File

@ -168,7 +168,7 @@ MemWallet.prototype.getUndo = function getUndo(key) {
};
MemWallet.prototype.addCoin = function addCoin(coin) {
const op = Outpoint(coin.hash, coin.index);
const op = new Outpoint(coin.hash, coin.index);
const key = op.toKey();
this.filter.add(op.toRaw());
@ -301,7 +301,7 @@ MemWallet.prototype.removeTX = function removeTX(tx, height) {
return false;
for (let i = 0; i < tx.outputs.length; i++) {
const op = Outpoint(hash, i).toKey();
const op = new Outpoint(hash, i).toKey();
const coin = this.getCoin(op);
if (!coin)