primitives: classify primitives.
This commit is contained in:
parent
ca08d6d97e
commit
daa55a05bc
File diff suppressed because it is too large
Load Diff
@ -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
|
||||
*/
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
@ -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
|
||||
*/
|
||||
|
||||
@ -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));
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@ -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();
|
||||
};
|
||||
|
||||
|
||||
1664
lib/net/bip152.js
1664
lib/net/bip152.js
File diff suppressed because it is too large
Load Diff
@ -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
@ -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
|
||||
|
||||
@ -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
|
||||
*/
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
@ -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
@ -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
|
||||
*/
|
||||
|
||||
@ -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
|
||||
*/
|
||||
|
||||
@ -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
|
||||
|
||||
4564
lib/primitives/tx.js
4564
lib/primitives/tx.js
File diff suppressed because it is too large
Load Diff
@ -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
|
||||
*/
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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)
|
||||
|
||||
Loading…
Reference in New Issue
Block a user