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);
|
const ZERO = new BN(0);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Chain Entry
|
||||||
* Represents an entry in the chain. Unlike
|
* Represents an entry in the chain. Unlike
|
||||||
* other bitcoin fullnodes, we store the
|
* other bitcoin fullnodes, we store the
|
||||||
* chainwork _with_ the entry in order to
|
* chainwork _with_ the entry in order to
|
||||||
* avoid reading the entire chain index on
|
* avoid reading the entire chain index on
|
||||||
* boot and recalculating the chainworks.
|
* boot and recalculating the chainworks.
|
||||||
* @alias module:blockchain.ChainEntry
|
* @alias module:blockchain.ChainEntry
|
||||||
* @constructor
|
|
||||||
* @param {Object?} options
|
|
||||||
* @property {Hash} hash
|
* @property {Hash} hash
|
||||||
* @property {Number} version - Transaction version. Note that Bcoin reads
|
* @property {Number} version
|
||||||
* versions as unsigned even though they are signed at the protocol level.
|
|
||||||
* This value will never be negative.
|
|
||||||
* @property {Hash} prevBlock
|
* @property {Hash} prevBlock
|
||||||
* @property {Hash} merkleRoot
|
* @property {Hash} merkleRoot
|
||||||
* @property {Number} time
|
* @property {Number} time
|
||||||
@ -38,25 +35,331 @@ const ZERO = new BN(0);
|
|||||||
* @property {Number} nonce
|
* @property {Number} nonce
|
||||||
* @property {Number} height
|
* @property {Number} height
|
||||||
* @property {BN} chainwork
|
* @property {BN} chainwork
|
||||||
* @property {ReversedHash} rhash - Reversed block hash (uint256le).
|
* @property {ReversedHash} rhash
|
||||||
*/
|
*/
|
||||||
|
|
||||||
function ChainEntry(options) {
|
class ChainEntry {
|
||||||
if (!(this instanceof ChainEntry))
|
/**
|
||||||
return new ChainEntry(options);
|
* Create a chain entry.
|
||||||
|
* @constructor
|
||||||
|
* @param {Object?} options
|
||||||
|
*/
|
||||||
|
|
||||||
this.hash = encoding.NULL_HASH;
|
constructor(options) {
|
||||||
this.version = 1;
|
this.hash = encoding.NULL_HASH;
|
||||||
this.prevBlock = encoding.NULL_HASH;
|
this.version = 1;
|
||||||
this.merkleRoot = encoding.NULL_HASH;
|
this.prevBlock = encoding.NULL_HASH;
|
||||||
this.time = 0;
|
this.merkleRoot = encoding.NULL_HASH;
|
||||||
this.bits = 0;
|
this.time = 0;
|
||||||
this.nonce = 0;
|
this.bits = 0;
|
||||||
this.height = 0;
|
this.nonce = 0;
|
||||||
this.chainwork = ZERO;
|
this.height = 0;
|
||||||
|
this.chainwork = ZERO;
|
||||||
|
|
||||||
if (options)
|
if (options)
|
||||||
this.fromOptions(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);
|
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
|
* Expose
|
||||||
*/
|
*/
|
||||||
|
|||||||
@ -22,9 +22,9 @@ const NUM_FLAGS = 1;
|
|||||||
const MAX_HEIGHT = ((1 << (32 - NUM_FLAGS)) >>> 0) - 1;
|
const MAX_HEIGHT = ((1 << (32 - NUM_FLAGS)) >>> 0) - 1;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Coin Entry
|
||||||
* Represents an unspent output.
|
* Represents an unspent output.
|
||||||
* @alias module:coins.CoinEntry
|
* @alias module:coins.CoinEntry
|
||||||
* @constructor
|
|
||||||
* @property {Number} version - Transaction version.
|
* @property {Number} version - Transaction version.
|
||||||
* @property {Number} height - Transaction height (-1 if unconfirmed).
|
* @property {Number} height - Transaction height (-1 if unconfirmed).
|
||||||
* @property {Boolean} coinbase - Whether the containing
|
* @property {Boolean} coinbase - Whether the containing
|
||||||
@ -34,241 +34,245 @@ const MAX_HEIGHT = ((1 << (32 - NUM_FLAGS)) >>> 0) - 1;
|
|||||||
* @property {Buffer} raw
|
* @property {Buffer} raw
|
||||||
*/
|
*/
|
||||||
|
|
||||||
function CoinEntry() {
|
class CoinEntry {
|
||||||
if (!(this instanceof CoinEntry))
|
/**
|
||||||
return new CoinEntry();
|
* Create a coin entry.
|
||||||
|
* @constructor
|
||||||
|
*/
|
||||||
|
|
||||||
this.version = 1;
|
constructor() {
|
||||||
this.height = -1;
|
this.version = 1;
|
||||||
this.coinbase = false;
|
this.height = -1;
|
||||||
this.output = new Output();
|
this.coinbase = false;
|
||||||
this.spent = false;
|
this.output = new Output();
|
||||||
this.raw = null;
|
this.spent = false;
|
||||||
}
|
this.raw = null;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convert coin entry to an output.
|
* Convert coin entry to an output.
|
||||||
* @returns {Output}
|
* @returns {Output}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
CoinEntry.prototype.toOutput = function toOutput() {
|
toOutput() {
|
||||||
return this.output;
|
return this.output;
|
||||||
};
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convert coin entry to a coin.
|
* Convert coin entry to a coin.
|
||||||
* @param {Outpoint} prevout
|
* @param {Outpoint} prevout
|
||||||
* @returns {Coin}
|
* @returns {Coin}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
CoinEntry.prototype.toCoin = function toCoin(prevout) {
|
toCoin(prevout) {
|
||||||
const coin = new Coin();
|
const coin = new Coin();
|
||||||
coin.version = this.version;
|
coin.version = this.version;
|
||||||
coin.height = this.height;
|
coin.height = this.height;
|
||||||
coin.coinbase = this.coinbase;
|
coin.coinbase = this.coinbase;
|
||||||
coin.script = this.output.script;
|
coin.script = this.output.script;
|
||||||
coin.value = this.output.value;
|
coin.value = this.output.value;
|
||||||
coin.hash = prevout.hash;
|
coin.hash = prevout.hash;
|
||||||
coin.index = prevout.index;
|
coin.index = prevout.index;
|
||||||
return coin;
|
return coin;
|
||||||
};
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Inject properties from TX.
|
* Inject properties from TX.
|
||||||
* @param {TX} tx
|
* @param {TX} tx
|
||||||
* @param {Number} index
|
* @param {Number} index
|
||||||
*/
|
*/
|
||||||
|
|
||||||
CoinEntry.prototype.fromOutput = function fromOutput(output) {
|
fromOutput(output) {
|
||||||
this.output = output;
|
this.output = output;
|
||||||
return this;
|
return this;
|
||||||
};
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Instantiate a coin from a TX
|
* Instantiate a coin from a TX
|
||||||
* @param {TX} tx
|
* @param {TX} tx
|
||||||
* @param {Number} index - Output index.
|
* @param {Number} index - Output index.
|
||||||
* @returns {CoinEntry}
|
* @returns {CoinEntry}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
CoinEntry.fromOutput = function fromOutput(output) {
|
static fromOutput(output) {
|
||||||
return new CoinEntry().fromOutput(output);
|
return new this().fromOutput(output);
|
||||||
};
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Inject properties from TX.
|
* Inject properties from TX.
|
||||||
* @param {TX} tx
|
* @param {TX} tx
|
||||||
* @param {Number} index
|
* @param {Number} index
|
||||||
*/
|
*/
|
||||||
|
|
||||||
CoinEntry.prototype.fromCoin = function fromCoin(coin) {
|
fromCoin(coin) {
|
||||||
this.version = coin.version;
|
this.version = coin.version;
|
||||||
this.height = coin.height;
|
this.height = coin.height;
|
||||||
this.coinbase = coin.coinbase;
|
this.coinbase = coin.coinbase;
|
||||||
this.output.script = coin.script;
|
this.output.script = coin.script;
|
||||||
this.output.value = coin.value;
|
this.output.value = coin.value;
|
||||||
return this;
|
return this;
|
||||||
};
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Instantiate a coin from a TX
|
* Instantiate a coin from a TX
|
||||||
* @param {TX} tx
|
* @param {TX} tx
|
||||||
* @param {Number} index - Output index.
|
* @param {Number} index - Output index.
|
||||||
* @returns {CoinEntry}
|
* @returns {CoinEntry}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
CoinEntry.fromCoin = function fromCoin(coin) {
|
static fromCoin(coin) {
|
||||||
return new CoinEntry().fromCoin(coin);
|
return new this().fromCoin(coin);
|
||||||
};
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Inject properties from TX.
|
* Inject properties from TX.
|
||||||
* @param {TX} tx
|
* @param {TX} tx
|
||||||
* @param {Number} index
|
* @param {Number} index
|
||||||
*/
|
*/
|
||||||
|
|
||||||
CoinEntry.prototype.fromTX = function fromTX(tx, index, height) {
|
fromTX(tx, index, height) {
|
||||||
assert(typeof index === 'number');
|
assert(typeof index === 'number');
|
||||||
assert(typeof height === 'number');
|
assert(typeof height === 'number');
|
||||||
assert(index >= 0 && index < tx.outputs.length);
|
assert(index >= 0 && index < tx.outputs.length);
|
||||||
this.version = tx.version;
|
this.version = tx.version;
|
||||||
this.height = height;
|
this.height = height;
|
||||||
this.coinbase = tx.isCoinbase();
|
this.coinbase = tx.isCoinbase();
|
||||||
this.output = tx.outputs[index];
|
this.output = tx.outputs[index];
|
||||||
return this;
|
return this;
|
||||||
};
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Instantiate a coin from a TX
|
* Instantiate a coin from a TX
|
||||||
* @param {TX} tx
|
* @param {TX} tx
|
||||||
* @param {Number} index - Output index.
|
* @param {Number} index - Output index.
|
||||||
* @returns {CoinEntry}
|
* @returns {CoinEntry}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
CoinEntry.fromTX = function fromTX(tx, index, height) {
|
static fromTX(tx, index, height) {
|
||||||
return new CoinEntry().fromTX(tx, index, height);
|
return new this().fromTX(tx, index, height);
|
||||||
};
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Calculate size of coin.
|
* Calculate size of coin.
|
||||||
* @returns {Number}
|
* @returns {Number}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
CoinEntry.prototype.getSize = function getSize() {
|
getSize() {
|
||||||
if (this.raw)
|
if (this.raw)
|
||||||
return this.raw.length;
|
return this.raw.length;
|
||||||
|
|
||||||
let size = 0;
|
let size = 0;
|
||||||
size += encoding.sizeVarint(this.version);
|
size += encoding.sizeVarint(this.version);
|
||||||
size += 4;
|
size += 4;
|
||||||
size += compress.size(this.output);
|
size += compress.size(this.output);
|
||||||
|
|
||||||
return size;
|
return size;
|
||||||
};
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Write the coin to a buffer writer.
|
* Write the coin to a buffer writer.
|
||||||
* @param {BufferWriter} bw
|
* @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;
|
return bw;
|
||||||
}
|
}
|
||||||
|
|
||||||
let height = this.height;
|
/**
|
||||||
let field = 0;
|
* Serialize the coin.
|
||||||
|
* @returns {Buffer}
|
||||||
|
*/
|
||||||
|
|
||||||
if (this.coinbase)
|
toRaw() {
|
||||||
field |= 1;
|
if (this.raw)
|
||||||
|
return this.raw;
|
||||||
|
|
||||||
if (height === -1)
|
const size = this.getSize();
|
||||||
height = MAX_HEIGHT;
|
const bw = new StaticWriter(size);
|
||||||
|
|
||||||
field |= height << NUM_FLAGS;
|
this.toWriter(bw);
|
||||||
|
|
||||||
bw.writeVarint(this.version);
|
this.raw = bw.render();
|
||||||
bw.writeU32(field);
|
|
||||||
compress.pack(this.output, bw);
|
|
||||||
|
|
||||||
return bw;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Serialize the coin.
|
|
||||||
* @returns {Buffer}
|
|
||||||
*/
|
|
||||||
|
|
||||||
CoinEntry.prototype.toRaw = function toRaw() {
|
|
||||||
if (this.raw)
|
|
||||||
return 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;
|
||||||
|
|
||||||
/**
|
this.version = version;
|
||||||
* Inject properties from serialized buffer writer.
|
this.coinbase = (field & 1) !== 0;
|
||||||
* @private
|
this.height = height;
|
||||||
* @param {BufferReader} br
|
|
||||||
*/
|
|
||||||
|
|
||||||
CoinEntry.prototype.fromReader = function fromReader(br) {
|
compress.unpack(this.output, br);
|
||||||
const version = br.readVarint();
|
|
||||||
const field = br.readU32();
|
|
||||||
|
|
||||||
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;
|
static fromReader(data) {
|
||||||
this.coinbase = (field & 1) !== 0;
|
return new this().fromReader(data);
|
||||||
this.height = height;
|
}
|
||||||
|
|
||||||
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.
|
* Instantiate a coin from a serialized Buffer.
|
||||||
* @param {Buffer} data
|
* @param {Buffer} data
|
||||||
* @returns {CoinEntry}
|
* @returns {CoinEntry}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
CoinEntry.fromReader = function fromReader(data) {
|
static fromRaw(data) {
|
||||||
return new CoinEntry().fromReader(data);
|
return new this().fromRaw(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);
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Expose
|
* Expose
|
||||||
|
|||||||
@ -10,209 +10,213 @@ const assert = require('assert');
|
|||||||
const CoinEntry = require('./coinentry');
|
const CoinEntry = require('./coinentry');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Coins
|
||||||
* Represents the outputs for a single transaction.
|
* Represents the outputs for a single transaction.
|
||||||
* @alias module:coins.Coins
|
* @alias module:coins.Coins
|
||||||
* @constructor
|
|
||||||
* @property {Map[]} outputs - Coins.
|
* @property {Map[]} outputs - Coins.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
function Coins() {
|
class Coins {
|
||||||
if (!(this instanceof Coins))
|
/**
|
||||||
return new Coins();
|
* Create coins.
|
||||||
|
* @constructor
|
||||||
|
*/
|
||||||
|
|
||||||
this.outputs = new Map();
|
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return this;
|
/**
|
||||||
};
|
* Add a single entry to the collection.
|
||||||
|
* @param {Number} index
|
||||||
|
* @param {CoinEntry} coin
|
||||||
|
* @returns {CoinEntry}
|
||||||
|
*/
|
||||||
|
|
||||||
/**
|
add(index, coin) {
|
||||||
* Instantiate a coins object from a transaction.
|
assert((index >>> 0) === index);
|
||||||
* @param {TX} tx
|
assert(coin);
|
||||||
* @param {Number} height
|
this.outputs.set(index, coin);
|
||||||
* @returns {Coins}
|
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
|
* 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');
|
const CoinEntry = require('../coins/coinentry');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* UndoCoins
|
* Undo Coins
|
||||||
* Coins need to be resurrected from somewhere
|
* Coins need to be resurrected from somewhere
|
||||||
* during a reorg. The undo coins store all
|
* during a reorg. The undo coins store all
|
||||||
* spent coins in a single record per block
|
* spent coins in a single record per block
|
||||||
* (in a compressed format).
|
* (in a compressed format).
|
||||||
* @alias module:coins.UndoCoins
|
* @alias module:coins.UndoCoins
|
||||||
* @constructor
|
|
||||||
* @property {UndoCoin[]} items
|
* @property {UndoCoin[]} items
|
||||||
*/
|
*/
|
||||||
|
|
||||||
function UndoCoins() {
|
class UndoCoins {
|
||||||
if (!(this instanceof UndoCoins))
|
/**
|
||||||
return new 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
|
* Expose
|
||||||
*/
|
*/
|
||||||
|
|||||||
@ -221,7 +221,7 @@ Miner.prototype.mineBlock = function mineBlock(tip, address) {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
Miner.prototype.addAddress = function addAddress(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) {
|
BlockTemplate.prototype.setAddress = function setAddress(address) {
|
||||||
this.address = Address(address);
|
this.address = new Address(address);
|
||||||
this.refresh();
|
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 hash256 = require('bcrypto/lib/hash256');
|
||||||
const BufferReader = require('bufio/lib/reader');
|
const BufferReader = require('bufio/lib/reader');
|
||||||
const StaticWriter = require('bufio/lib/staticwriter');
|
const StaticWriter = require('bufio/lib/staticwriter');
|
||||||
const InvItem = require('./invitem');
|
|
||||||
const encoding = require('bufio/lib/encoding');
|
const encoding = require('bufio/lib/encoding');
|
||||||
|
const InvItem = require('./invitem');
|
||||||
const consensus = require('../protocol/consensus');
|
const consensus = require('../protocol/consensus');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Abstract Block
|
||||||
* The class which all block-like objects inherit from.
|
* The class which all block-like objects inherit from.
|
||||||
* @alias module:primitives.AbstractBlock
|
* @alias module:primitives.AbstractBlock
|
||||||
* @constructor
|
|
||||||
* @abstract
|
* @abstract
|
||||||
* @property {Number} version - Block version. Note
|
* @property {Number} version
|
||||||
* that Bcoin reads versions as unsigned despite
|
* @property {Hash} prevBlock
|
||||||
* them being signed on the protocol level. This
|
* @property {Hash} merkleRoot
|
||||||
* number will never be negative.
|
* @property {Number} time
|
||||||
* @property {Hash} prevBlock - Previous block hash.
|
|
||||||
* @property {Hash} merkleRoot - Merkle root hash.
|
|
||||||
* @property {Number} time - Timestamp.
|
|
||||||
* @property {Number} bits
|
* @property {Number} bits
|
||||||
* @property {Number} nonce
|
* @property {Number} nonce
|
||||||
*/
|
*/
|
||||||
|
|
||||||
function AbstractBlock() {
|
class AbstractBlock {
|
||||||
if (!(this instanceof AbstractBlock))
|
/**
|
||||||
return new AbstractBlock();
|
* Create an abstract block.
|
||||||
|
* @constructor
|
||||||
|
*/
|
||||||
|
|
||||||
this.version = 1;
|
constructor() {
|
||||||
this.prevBlock = encoding.NULL_HASH;
|
this.version = 1;
|
||||||
this.merkleRoot = encoding.NULL_HASH;
|
this.prevBlock = encoding.NULL_HASH;
|
||||||
this.time = 0;
|
this.merkleRoot = encoding.NULL_HASH;
|
||||||
this.bits = 0;
|
this.time = 0;
|
||||||
this.nonce = 0;
|
this.bits = 0;
|
||||||
|
this.nonce = 0;
|
||||||
|
|
||||||
this.mutable = false;
|
this.mutable = false;
|
||||||
|
|
||||||
this._hash = null;
|
this._hash = null;
|
||||||
this._hhash = 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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (enc === 'hex') {
|
/**
|
||||||
let hex = this._hhash;
|
* Inject properties from options object.
|
||||||
if (!hex) {
|
* @private
|
||||||
hex = h.toString('hex');
|
* @param {NakedBlock} options
|
||||||
if (!this.mutable)
|
*/
|
||||||
this._hhash = hex;
|
|
||||||
|
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
|
||||||
|
*/
|
||||||
|
|
||||||
/**
|
parseJSON(json) {
|
||||||
* Serialize the block headers.
|
assert(json, 'Block data is required.');
|
||||||
* @returns {Buffer}
|
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() {
|
this.version = json.version;
|
||||||
return this.writeHead(new StaticWriter(80)).render();
|
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;
|
||||||
* Inject properties from serialized data.
|
}
|
||||||
* @private
|
|
||||||
* @param {Buffer} data
|
|
||||||
*/
|
|
||||||
|
|
||||||
AbstractBlock.prototype.fromHead = function fromHead(data) {
|
/**
|
||||||
return this.readHead(new BufferReader(data));
|
* Test whether the block is a memblock.
|
||||||
};
|
* @returns {Boolean}
|
||||||
|
*/
|
||||||
|
|
||||||
/**
|
isMemory() {
|
||||||
* 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())
|
|
||||||
return false;
|
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.
|
* Clear any cached values.
|
||||||
* @returns {Boolean}
|
*/
|
||||||
*/
|
|
||||||
|
|
||||||
AbstractBlock.prototype.verifyPOW = function verifyPOW() {
|
refresh() {
|
||||||
return consensus.verifyPOW(this.hash(), this.bits);
|
return this._refresh();
|
||||||
};
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Verify the block.
|
* Hash the block headers.
|
||||||
* @returns {Boolean}
|
* @param {String?} enc - Can be `'hex'` or `null`.
|
||||||
*/
|
* @returns {Hash|Buffer} hash
|
||||||
|
*/
|
||||||
|
|
||||||
AbstractBlock.prototype.verifyBody = function verifyBody() {
|
hash(enc) {
|
||||||
throw new Error('Abstract method.');
|
let h = this._hash;
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
if (!h) {
|
||||||
* Get little-endian block hash.
|
h = hash256.digest(this.toHead());
|
||||||
* @returns {Hash}
|
if (!this.mutable)
|
||||||
*/
|
this._hash = h;
|
||||||
|
}
|
||||||
|
|
||||||
AbstractBlock.prototype.rhash = function rhash() {
|
if (enc === 'hex') {
|
||||||
return encoding.revHex(this.hash('hex'));
|
let hex = this._hhash;
|
||||||
};
|
if (!hex) {
|
||||||
|
hex = h.toString('hex');
|
||||||
|
if (!this.mutable)
|
||||||
|
this._hhash = hex;
|
||||||
|
}
|
||||||
|
h = hex;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
return h;
|
||||||
* Convert the block to an inv item.
|
}
|
||||||
* @returns {InvItem}
|
|
||||||
*/
|
|
||||||
|
|
||||||
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
|
* 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';
|
'use strict';
|
||||||
|
|
||||||
const assert = require('assert');
|
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 Amount = require('../btc/amount');
|
||||||
const Output = require('./output');
|
const Output = require('./output');
|
||||||
const Script = require('../script/script');
|
const Script = require('../script/script');
|
||||||
const Network = require('../protocol/network');
|
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.
|
* Represents an unspent output.
|
||||||
* @alias module:primitives.Coin
|
* @alias module:primitives.Coin
|
||||||
* @constructor
|
|
||||||
* @extends Output
|
* @extends Output
|
||||||
* @param {NakedCoin|Coin} options
|
* @property {Number} version
|
||||||
* @property {Number} version - Transaction version.
|
* @property {Number} height
|
||||||
* @property {Number} height - Transaction height (-1 if unconfirmed).
|
* @property {Amount} value
|
||||||
* @property {Amount} value - Output value in satoshis.
|
* @property {Script} script
|
||||||
* @property {Script} script - Output script.
|
* @property {Boolean} coinbase
|
||||||
* @property {Boolean} coinbase - Whether the containing
|
* @property {Hash} hash
|
||||||
* transaction is a coinbase.
|
* @property {Number} index
|
||||||
* @property {Hash} hash - Transaction hash.
|
|
||||||
* @property {Number} index - Output index.
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
function Coin(options) {
|
class Coin extends Output {
|
||||||
if (!(this instanceof Coin))
|
/**
|
||||||
return new Coin(options);
|
* Create a coin.
|
||||||
|
* @constructor
|
||||||
|
* @param {Object} options
|
||||||
|
*/
|
||||||
|
|
||||||
this.version = 1;
|
constructor(options) {
|
||||||
this.height = -1;
|
super();
|
||||||
this.value = 0;
|
|
||||||
this.script = new Script();
|
|
||||||
this.coinbase = false;
|
|
||||||
this.hash = encoding.NULL_HASH;
|
|
||||||
this.index = 0;
|
|
||||||
|
|
||||||
if (options)
|
this.version = 1;
|
||||||
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.height = -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.
|
* Inject options into coin.
|
||||||
* @private
|
* @private
|
||||||
* @param {Buffer} data
|
* @param {Object} options
|
||||||
*/
|
*/
|
||||||
|
|
||||||
Coin.prototype.fromRaw = function fromRaw(data) {
|
fromOptions(options) {
|
||||||
return this.fromReader(new BufferReader(data));
|
assert(options, 'Coin data is required.');
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
if (options.version != null) {
|
||||||
* Instantiate a coin from a buffer reader.
|
assert((options.version >>> 0) === options.version,
|
||||||
* @param {BufferReader} br
|
'Version must be a uint32.');
|
||||||
* @returns {Coin}
|
this.version = options.version;
|
||||||
*/
|
}
|
||||||
|
|
||||||
Coin.fromReader = function fromReader(br) {
|
if (options.height != null) {
|
||||||
return new Coin().fromReader(br);
|
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) {
|
||||||
* Instantiate a coin from a serialized Buffer.
|
assert(Number.isSafeInteger(options.value) && options.value >= 0,
|
||||||
* @param {Buffer} data
|
'Value must be a uint64.');
|
||||||
* @param {String?} enc - Encoding, can be `'hex'` or null.
|
this.value = options.value;
|
||||||
* @returns {Coin}
|
}
|
||||||
*/
|
|
||||||
|
|
||||||
Coin.fromRaw = function fromRaw(data, enc) {
|
if (options.script)
|
||||||
if (typeof data === 'string')
|
this.script.fromOptions(options.script);
|
||||||
data = Buffer.from(data, enc);
|
|
||||||
return new Coin().fromRaw(data);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
if (options.coinbase != null) {
|
||||||
* Inject properties from TX.
|
assert(typeof options.coinbase === 'boolean',
|
||||||
* @param {TX} tx
|
'Coinbase must be a boolean.');
|
||||||
* @param {Number} index
|
this.coinbase = options.coinbase;
|
||||||
*/
|
}
|
||||||
|
|
||||||
Coin.prototype.fromTX = function fromTX(tx, index, height) {
|
if (options.hash != null) {
|
||||||
assert(typeof index === 'number');
|
assert(typeof options.hash === 'string', 'Hash must be a string.');
|
||||||
assert(typeof height === 'number');
|
this.hash = options.hash;
|
||||||
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.index != null) {
|
||||||
* Instantiate a coin from a TX
|
assert((options.index >>> 0) === options.index,
|
||||||
* @param {TX} tx
|
'Index must be a uint32.');
|
||||||
* @param {Number} index - Output index.
|
this.index = options.index;
|
||||||
* @returns {Coin}
|
}
|
||||||
*/
|
|
||||||
|
|
||||||
Coin.fromTX = function fromTX(tx, index, height) {
|
return this;
|
||||||
return new Coin().fromTX(tx, index, height);
|
}
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test an object to see if it is a Coin.
|
* Instantiate Coin from options object.
|
||||||
* @param {Object} obj
|
* @private
|
||||||
* @returns {Boolean}
|
* @param {Object} options
|
||||||
*/
|
*/
|
||||||
|
|
||||||
Coin.isCoin = function isCoin(obj) {
|
static fromOptions(options) {
|
||||||
return obj instanceof Coin;
|
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
|
* Expose
|
||||||
|
|||||||
@ -14,262 +14,264 @@ const BufferReader = require('bufio/lib/reader');
|
|||||||
const encoding = require('bufio/lib/encoding');
|
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
|
* @alias module:primitives.Headers
|
||||||
* @constructor
|
|
||||||
* @extends AbstractBlock
|
* @extends AbstractBlock
|
||||||
* @param {NakedBlock} options
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
function Headers(options) {
|
class Headers extends AbstractBlock {
|
||||||
if (!(this instanceof Headers))
|
/**
|
||||||
return new Headers(options);
|
* Create headers.
|
||||||
|
* @constructor
|
||||||
|
* @param {Object} options
|
||||||
|
*/
|
||||||
|
|
||||||
AbstractBlock.call(this);
|
constructor(options) {
|
||||||
|
super();
|
||||||
|
|
||||||
if (options)
|
if (options)
|
||||||
this.parseOptions(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
|
* Expose
|
||||||
*/
|
*/
|
||||||
|
|||||||
@ -16,502 +16,507 @@ const StaticWriter = require('bufio/lib/staticwriter');
|
|||||||
const BufferReader = require('bufio/lib/reader');
|
const BufferReader = require('bufio/lib/reader');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Input
|
||||||
* Represents a transaction input.
|
* Represents a transaction input.
|
||||||
* @alias module:primitives.Input
|
* @alias module:primitives.Input
|
||||||
* @constructor
|
|
||||||
* @param {NakedInput} options
|
|
||||||
* @property {Outpoint} prevout - Outpoint.
|
* @property {Outpoint} prevout - Outpoint.
|
||||||
* @property {Script} script - Input script / scriptSig.
|
* @property {Script} script - Input script / scriptSig.
|
||||||
* @property {Number} sequence - nSequence.
|
* @property {Number} sequence - nSequence.
|
||||||
* @property {Witness} witness - Witness (empty if not present).
|
* @property {Witness} witness - Witness (empty if not present).
|
||||||
*/
|
*/
|
||||||
|
|
||||||
function Input(options) {
|
class Input {
|
||||||
if (!(this instanceof Input))
|
/**
|
||||||
return new Input(options);
|
* Create transaction input.
|
||||||
|
* @constructor
|
||||||
|
* @param {Object} options
|
||||||
|
*/
|
||||||
|
|
||||||
this.prevout = new Outpoint();
|
constructor(options) {
|
||||||
this.script = new Script();
|
this.prevout = new Outpoint();
|
||||||
this.sequence = 0xffffffff;
|
this.script = new Script();
|
||||||
this.witness = new Witness();
|
this.sequence = 0xffffffff;
|
||||||
|
this.witness = new Witness();
|
||||||
|
|
||||||
if (options)
|
if (options)
|
||||||
this.fromOptions(options);
|
this.fromOptions(options);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Inject properties from options object.
|
* Inject properties from options object.
|
||||||
* @private
|
* @private
|
||||||
* @param {Object} options
|
* @param {Object} options
|
||||||
*/
|
*/
|
||||||
|
|
||||||
Input.prototype.fromOptions = function fromOptions(options) {
|
fromOptions(options) {
|
||||||
assert(options, 'Input data is required.');
|
assert(options, 'Input data is required.');
|
||||||
|
|
||||||
this.prevout.fromOptions(options.prevout);
|
this.prevout.fromOptions(options.prevout);
|
||||||
|
|
||||||
if (options.script)
|
if (options.script)
|
||||||
this.script.fromOptions(options.script);
|
this.script.fromOptions(options.script);
|
||||||
|
|
||||||
if (options.sequence != null) {
|
if (options.sequence != null) {
|
||||||
assert((options.sequence >>> 0) === options.sequence,
|
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.');
|
'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;
|
static fromJSON(json) {
|
||||||
};
|
return new this().fromJSON(json);
|
||||||
|
|
||||||
/**
|
|
||||||
* 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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let prev = coin.script;
|
/**
|
||||||
let redeem = null;
|
* Calculate size of serialized input.
|
||||||
|
* @returns {Number}
|
||||||
|
*/
|
||||||
|
|
||||||
if (prev.isScripthash()) {
|
getSize() {
|
||||||
prev = this.script.getRedeem();
|
return 40 + this.script.getVarSize();
|
||||||
redeem = prev;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (prev && prev.isWitnessScripthash()) {
|
/**
|
||||||
prev = this.witness.getRedeem();
|
* Serialize the input.
|
||||||
redeem = prev;
|
* @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
|
||||||
|
*/
|
||||||
|
|
||||||
/**
|
toWriter(bw) {
|
||||||
* Get the redeem script type.
|
this.prevout.toWriter(bw);
|
||||||
* @param {Coin?} coin
|
bw.writeVarBytes(this.script.toRaw());
|
||||||
* @returns {String} subtype
|
bw.writeU32(this.sequence);
|
||||||
*/
|
return bw;
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
/**
|
||||||
prevout: this.prevout.toJSON(),
|
* Inject properties from buffer reader.
|
||||||
script: this.script.toJSON(),
|
* @private
|
||||||
witness: this.witness.toJSON(),
|
* @param {BufferReader} br
|
||||||
sequence: this.sequence,
|
*/
|
||||||
address: addr,
|
|
||||||
coin: coin ? coin.getJSON(network, true) : undefined
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
fromReader(br) {
|
||||||
* Inject properties from a JSON object.
|
this.prevout.fromReader(br);
|
||||||
* @private
|
this.script.fromRaw(br.readVarBytes());
|
||||||
* @param {Object} json
|
this.sequence = br.readU32();
|
||||||
*/
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
Input.prototype.fromJSON = function fromJSON(json) {
|
/**
|
||||||
assert(json, 'Input data is required.');
|
* Inject properties from serialized data.
|
||||||
assert((json.sequence >>> 0) === json.sequence, 'Sequence must be a uint32.');
|
* @param {Buffer} data
|
||||||
this.prevout.fromJSON(json.prevout);
|
*/
|
||||||
this.script.fromJSON(json.script);
|
|
||||||
this.witness.fromJSON(json.witness);
|
|
||||||
this.sequence = json.sequence;
|
|
||||||
return this;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
fromRaw(data) {
|
||||||
* Instantiate an Input from a jsonified input object.
|
return this.fromReader(new BufferReader(data));
|
||||||
* @param {Object} json - The jsonified input object.
|
}
|
||||||
* @returns {Input}
|
|
||||||
*/
|
|
||||||
|
|
||||||
Input.fromJSON = function fromJSON(json) {
|
/**
|
||||||
return new Input().fromJSON(json);
|
* Instantiate an input from a buffer reader.
|
||||||
};
|
* @param {BufferReader} br
|
||||||
|
* @returns {Input}
|
||||||
|
*/
|
||||||
|
|
||||||
/**
|
static fromReader(br) {
|
||||||
* Calculate size of serialized input.
|
return new this().fromReader(br);
|
||||||
* @returns {Number}
|
}
|
||||||
*/
|
|
||||||
|
|
||||||
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}
|
||||||
|
*/
|
||||||
|
|
||||||
/**
|
static fromRaw(data, enc) {
|
||||||
* Serialize the input.
|
if (typeof data === 'string')
|
||||||
* @param {String?} enc - Encoding, can be `'hex'` or null.
|
data = Buffer.from(data, enc);
|
||||||
* @returns {Buffer|String}
|
return new this().fromRaw(data);
|
||||||
*/
|
}
|
||||||
|
|
||||||
Input.prototype.toRaw = function toRaw() {
|
/**
|
||||||
const size = this.getSize();
|
* Inject properties from outpoint.
|
||||||
return this.toWriter(new StaticWriter(size)).render();
|
* @private
|
||||||
};
|
* @param {Outpoint} outpoint
|
||||||
|
*/
|
||||||
|
|
||||||
/**
|
fromOutpoint(outpoint) {
|
||||||
* Write the input to a buffer writer.
|
assert(typeof outpoint.hash === 'string');
|
||||||
* @param {BufferWriter} bw
|
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);
|
* Instantiate input from outpoint.
|
||||||
bw.writeVarBytes(this.script.toRaw());
|
* @param {Outpoint}
|
||||||
bw.writeU32(this.sequence);
|
* @returns {Input}
|
||||||
return bw;
|
*/
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
static fromOutpoint(outpoint) {
|
||||||
* Inject properties from buffer reader.
|
return new this().fromOutpoint(outpoint);
|
||||||
* @private
|
}
|
||||||
* @param {BufferReader} br
|
|
||||||
*/
|
|
||||||
|
|
||||||
Input.prototype.fromReader = function fromReader(br) {
|
/**
|
||||||
this.prevout.fromReader(br);
|
* Inject properties from coin.
|
||||||
this.script.fromRaw(br.readVarBytes());
|
* @private
|
||||||
this.sequence = br.readU32();
|
* @param {Coin} coin
|
||||||
return this;
|
*/
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
fromCoin(coin) {
|
||||||
* Inject properties from serialized data.
|
assert(typeof coin.hash === 'string');
|
||||||
* @param {Buffer} data
|
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}
|
||||||
|
*/
|
||||||
|
|
||||||
/**
|
static fromCoin(coin) {
|
||||||
* Instantiate an input from a buffer reader.
|
return new this().fromCoin(coin);
|
||||||
* @param {BufferReader} br
|
}
|
||||||
* @returns {Input}
|
|
||||||
*/
|
|
||||||
|
|
||||||
Input.fromReader = function fromReader(br) {
|
/**
|
||||||
return new Input().fromReader(br);
|
* Inject properties from transaction.
|
||||||
};
|
* @private
|
||||||
|
* @param {TX} tx
|
||||||
|
* @param {Number} index
|
||||||
|
*/
|
||||||
|
|
||||||
/**
|
fromTX(tx, index) {
|
||||||
* Instantiate an input from a serialized Buffer.
|
assert(tx);
|
||||||
* @param {Buffer} data
|
assert(typeof index === 'number');
|
||||||
* @param {String?} enc - Encoding, can be `'hex'` or null.
|
assert(index >= 0 && index < tx.outputs.length);
|
||||||
* @returns {Input}
|
this.prevout.hash = tx.hash('hex');
|
||||||
*/
|
this.prevout.index = index;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
Input.fromRaw = function fromRaw(data, enc) {
|
/**
|
||||||
if (typeof data === 'string')
|
* Instantiate input from tx.
|
||||||
data = Buffer.from(data, enc);
|
* @param {TX} tx
|
||||||
return new Input().fromRaw(data);
|
* @param {Number} index
|
||||||
};
|
* @returns {Input}
|
||||||
|
*/
|
||||||
|
|
||||||
/**
|
static fromTX(tx, index) {
|
||||||
* Inject properties from outpoint.
|
return new this().fromTX(tx, index);
|
||||||
* @private
|
}
|
||||||
* @param {Outpoint} outpoint
|
|
||||||
*/
|
|
||||||
|
|
||||||
Input.prototype.fromOutpoint = function fromOutpoint(outpoint) {
|
/**
|
||||||
assert(typeof outpoint.hash === 'string');
|
* Test an object to see if it is an Input.
|
||||||
assert(typeof outpoint.index === 'number');
|
* @param {Object} obj
|
||||||
this.prevout.hash = outpoint.hash;
|
* @returns {Boolean}
|
||||||
this.prevout.index = outpoint.index;
|
*/
|
||||||
return this;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
static isInput(obj) {
|
||||||
* Instantiate input from outpoint.
|
return obj instanceof Input;
|
||||||
* @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;
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Expose
|
* Expose
|
||||||
|
|||||||
@ -15,18 +15,146 @@ const encoding = require('bufio/lib/encoding');
|
|||||||
* Inv Item
|
* Inv Item
|
||||||
* @alias module:primitives.InvItem
|
* @alias module:primitives.InvItem
|
||||||
* @constructor
|
* @constructor
|
||||||
* @param {Number} type
|
|
||||||
* @param {Hash} hash
|
|
||||||
* @property {InvType} type
|
* @property {InvType} type
|
||||||
* @property {Hash} hash
|
* @property {Hash} hash
|
||||||
*/
|
*/
|
||||||
|
|
||||||
function InvItem(type, hash) {
|
class InvItem {
|
||||||
if (!(this instanceof InvItem))
|
/**
|
||||||
return new InvItem(type, hash);
|
* Create an inv item.
|
||||||
|
* @constructor
|
||||||
|
* @param {Number} type
|
||||||
|
* @param {Hash} hash
|
||||||
|
*/
|
||||||
|
|
||||||
this.type = type;
|
constructor(type, hash) {
|
||||||
this.hash = 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 = {
|
InvItem.types = {
|
||||||
ERROR: 0,
|
|
||||||
TX: 1,
|
TX: 1,
|
||||||
BLOCK: 2,
|
BLOCK: 2,
|
||||||
FILTERED_BLOCK: 3,
|
FILTERED_BLOCK: 3,
|
||||||
@ -52,7 +179,6 @@ InvItem.types = {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
InvItem.typesByVal = {
|
InvItem.typesByVal = {
|
||||||
0: 'ERROR',
|
|
||||||
1: 'TX',
|
1: 'TX',
|
||||||
2: 'BLOCK',
|
2: 'BLOCK',
|
||||||
3: 'FILTERED_BLOCK',
|
3: 'FILTERED_BLOCK',
|
||||||
@ -70,130 +196,6 @@ InvItem.typesByVal = {
|
|||||||
|
|
||||||
InvItem.WITNESS_FLAG = 1 << 30;
|
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
|
* 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);
|
const DUMMY = Buffer.alloc(0);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Mem Block
|
||||||
* A block object which is essentially a "placeholder"
|
* A block object which is essentially a "placeholder"
|
||||||
* for a full {@link Block} object. The v8 garbage
|
* for a full {@link Block} object. The v8 garbage
|
||||||
* collector's head will explode if there is too much
|
* 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
|
* 500mb of blocks on the js heap would not be a good
|
||||||
* thing.
|
* thing.
|
||||||
* @alias module:primitives.MemBlock
|
* @alias module:primitives.MemBlock
|
||||||
* @constructor
|
* @extends AbstractBlock
|
||||||
* @param {NakedBlock} options
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
function MemBlock() {
|
class MemBlock extends AbstractBlock {
|
||||||
if (!(this instanceof MemBlock))
|
/**
|
||||||
return new MemBlock();
|
* 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
|
* 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');
|
const BufferReader = require('bufio/lib/reader');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Net Address
|
||||||
* Represents a network address.
|
* Represents a network address.
|
||||||
* @alias module:primitives.NetAddress
|
* @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 {Host} host
|
||||||
* @property {Number} port
|
* @property {Number} port
|
||||||
* @property {Number} services
|
* @property {Number} services
|
||||||
* @property {Number} time
|
* @property {Number} time
|
||||||
*/
|
*/
|
||||||
|
|
||||||
function NetAddress(options) {
|
class NetAddress {
|
||||||
if (!(this instanceof NetAddress))
|
/**
|
||||||
return new NetAddress(options);
|
* 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';
|
constructor(options) {
|
||||||
this.port = 0;
|
this.host = '0.0.0.0';
|
||||||
this.services = 0;
|
this.port = 0;
|
||||||
this.time = 0;
|
this.services = 0;
|
||||||
this.hostname = '0.0.0.0:0';
|
this.time = 0;
|
||||||
this.raw = IP.ZERO_IP;
|
this.hostname = '0.0.0.0:0';
|
||||||
|
this.raw = IP.ZERO_IP;
|
||||||
|
|
||||||
if (options)
|
if (options)
|
||||||
this.fromOptions(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.WITNESS
|
||||||
| common.services.BLOOM;
|
| 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
|
* Expose
|
||||||
*/
|
*/
|
||||||
|
|||||||
@ -12,330 +12,334 @@ const BufferReader = require('bufio/lib/reader');
|
|||||||
const encoding = require('bufio/lib/encoding');
|
const encoding = require('bufio/lib/encoding');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Outpoint
|
||||||
* Represents a COutPoint.
|
* Represents a COutPoint.
|
||||||
* @alias module:primitives.Outpoint
|
* @alias module:primitives.Outpoint
|
||||||
* @constructor
|
|
||||||
* @param {Hash?} hash
|
|
||||||
* @param {Number?} index
|
|
||||||
* @property {Hash} hash
|
* @property {Hash} hash
|
||||||
* @property {Number} index
|
* @property {Number} index
|
||||||
*/
|
*/
|
||||||
|
|
||||||
function Outpoint(hash, index) {
|
class Outpoint {
|
||||||
if (!(this instanceof Outpoint))
|
/**
|
||||||
return new Outpoint(hash, index);
|
* Create an outpoint.
|
||||||
|
* @constructor
|
||||||
|
* @param {Hash?} hash
|
||||||
|
* @param {Number?} index
|
||||||
|
*/
|
||||||
|
|
||||||
this.hash = encoding.NULL_HASH;
|
constructor(hash, index) {
|
||||||
this.index = 0xffffffff;
|
this.hash = encoding.NULL_HASH;
|
||||||
|
this.index = 0xffffffff;
|
||||||
|
|
||||||
if (hash != null) {
|
if (hash != null) {
|
||||||
assert(typeof hash === 'string', 'Hash must be a string.');
|
assert(typeof hash === 'string', 'Hash must be a string.');
|
||||||
assert((index >>> 0) === index, 'Index must be a uint32.');
|
assert((index >>> 0) === index, 'Index must be a uint32.');
|
||||||
this.hash = hash;
|
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;
|
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
|
* Helpers
|
||||||
*/
|
*/
|
||||||
|
|||||||
@ -20,357 +20,360 @@ const policy = require('../protocol/policy');
|
|||||||
/**
|
/**
|
||||||
* Represents a transaction output.
|
* Represents a transaction output.
|
||||||
* @alias module:primitives.Output
|
* @alias module:primitives.Output
|
||||||
* @constructor
|
* @property {Amount} value
|
||||||
* @param {NakedOutput} options
|
|
||||||
* @property {Amount} value - Value in satoshis.
|
|
||||||
* @property {Script} script
|
* @property {Script} script
|
||||||
*/
|
*/
|
||||||
|
|
||||||
function Output(options) {
|
class Output {
|
||||||
if (!(this instanceof Output))
|
/**
|
||||||
return new Output(options);
|
* Create an output.
|
||||||
|
* @constructor
|
||||||
|
* @param {Object?} options
|
||||||
|
*/
|
||||||
|
|
||||||
this.value = 0;
|
constructor(options) {
|
||||||
this.script = new Script();
|
this.value = 0;
|
||||||
|
this.script = new Script();
|
||||||
|
|
||||||
if (options)
|
if (options)
|
||||||
this.fromOptions(options);
|
this.fromOptions(options);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Inject properties from options object.
|
* Inject properties from options object.
|
||||||
* @private
|
* @private
|
||||||
* @param {NakedOutput} options
|
* @param {NakedOutput} options
|
||||||
*/
|
*/
|
||||||
|
|
||||||
Output.prototype.fromOptions = function fromOptions(options) {
|
fromOptions(options) {
|
||||||
assert(options, 'Output data is required.');
|
assert(options, 'Output data is required.');
|
||||||
|
|
||||||
if (options.value) {
|
if (options.value) {
|
||||||
assert(Number.isSafeInteger(options.value) && options.value >= 0,
|
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.');
|
'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)
|
static fromJSON(json) {
|
||||||
this.script.fromAddress(options.address);
|
return new this().fromJSON(json);
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return 3 * policy.getMinFee(size, rate);
|
/**
|
||||||
};
|
* Write the output to a buffer writer.
|
||||||
|
* @param {BufferWriter} bw
|
||||||
|
*/
|
||||||
|
|
||||||
/**
|
toWriter(bw) {
|
||||||
* Calculate size of serialized output.
|
bw.writeI64(this.value);
|
||||||
* @returns {Number}
|
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}
|
||||||
|
*/
|
||||||
|
|
||||||
/**
|
toRaw() {
|
||||||
* Test whether the output should be considered dust.
|
const size = this.getSize();
|
||||||
* @param {Rate?} rate
|
return this.toWriter(new StaticWriter(size)).render();
|
||||||
* @returns {Boolean}
|
}
|
||||||
*/
|
|
||||||
|
|
||||||
Output.prototype.isDust = function isDust(rate) {
|
/**
|
||||||
return this.value < this.getDustThreshold(rate);
|
* Inject properties from buffer reader.
|
||||||
};
|
* @private
|
||||||
|
* @param {BufferReader} br
|
||||||
|
*/
|
||||||
|
|
||||||
/**
|
fromReader(br) {
|
||||||
* Inject properties from a JSON object.
|
this.value = br.readI64();
|
||||||
* @private
|
this.script.fromRaw(br.readVarBytes());
|
||||||
* @param {Object} json
|
return this;
|
||||||
*/
|
}
|
||||||
|
|
||||||
Output.prototype.fromJSON = function fromJSON(json) {
|
/**
|
||||||
assert(json, 'Output data is required.');
|
* Inject properties from serialized data.
|
||||||
assert(Number.isSafeInteger(json.value) && json.value >= 0,
|
* @private
|
||||||
'Value must be a uint64.');
|
* @param {Buffer} data
|
||||||
this.value = json.value;
|
*/
|
||||||
this.script.fromJSON(json.script);
|
|
||||||
return this;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
fromRaw(data) {
|
||||||
* Instantiate an Output from a jsonified output object.
|
return this.fromReader(new BufferReader(data));
|
||||||
* @param {Object} json - The jsonified output object.
|
}
|
||||||
* @returns {Output}
|
|
||||||
*/
|
|
||||||
|
|
||||||
Output.fromJSON = function fromJSON(json) {
|
/**
|
||||||
return new Output().fromJSON(json);
|
* Instantiate an output from a buffer reader.
|
||||||
};
|
* @param {BufferReader} br
|
||||||
|
* @returns {Output}
|
||||||
|
*/
|
||||||
|
|
||||||
/**
|
static fromReader(br) {
|
||||||
* Write the output to a buffer writer.
|
return new this().fromReader(br);
|
||||||
* @param {BufferWriter} bw
|
}
|
||||||
*/
|
|
||||||
|
|
||||||
Output.prototype.toWriter = function toWriter(bw) {
|
/**
|
||||||
bw.writeI64(this.value);
|
* Instantiate an output from a serialized Buffer.
|
||||||
bw.writeVarBytes(this.script.toRaw());
|
* @param {Buffer} data
|
||||||
return bw;
|
* @param {String?} enc - Encoding, can be `'hex'` or null.
|
||||||
};
|
* @returns {Output}
|
||||||
|
*/
|
||||||
|
|
||||||
/**
|
static fromRaw(data, enc) {
|
||||||
* Serialize the output.
|
if (typeof data === 'string')
|
||||||
* @param {String?} enc - Encoding, can be `'hex'` or null.
|
data = Buffer.from(data, enc);
|
||||||
* @returns {Buffer|String}
|
return new this().fromRaw(data);
|
||||||
*/
|
}
|
||||||
|
|
||||||
Output.prototype.toRaw = function toRaw() {
|
/**
|
||||||
const size = this.getSize();
|
* Test an object to see if it is an Output.
|
||||||
return this.toWriter(new StaticWriter(size)).render();
|
* @param {Object} obj
|
||||||
};
|
* @returns {Boolean}
|
||||||
|
*/
|
||||||
|
|
||||||
/**
|
static isOutput(obj) {
|
||||||
* Inject properties from buffer reader.
|
return obj instanceof Output;
|
||||||
* @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;
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Expose
|
* 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');
|
const encoding = require('bufio/lib/encoding');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* TXMeta
|
||||||
* An extended transaction object.
|
* An extended transaction object.
|
||||||
* @alias module:primitives.TXMeta
|
* @alias module:primitives.TXMeta
|
||||||
* @constructor
|
|
||||||
* @param {Object} options
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
function TXMeta(options) {
|
class TXMeta {
|
||||||
if (!(this instanceof TXMeta))
|
/**
|
||||||
return new TXMeta(options);
|
* Create an extended transaction.
|
||||||
|
* @constructor
|
||||||
|
* @param {Object?} options
|
||||||
|
*/
|
||||||
|
|
||||||
this.tx = new TX();
|
constructor(options) {
|
||||||
this.mtime = util.now();
|
this.tx = new TX();
|
||||||
this.height = -1;
|
this.mtime = util.now();
|
||||||
this.block = null;
|
this.height = -1;
|
||||||
this.time = 0;
|
this.block = null;
|
||||||
this.index = -1;
|
this.time = 0;
|
||||||
|
this.index = -1;
|
||||||
|
|
||||||
if (options)
|
if (options)
|
||||||
this.fromOptions(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
|
* Expose
|
||||||
*/
|
*/
|
||||||
|
|||||||
@ -12,273 +12,276 @@ const KeyRing = require('../primitives/keyring');
|
|||||||
const Path = require('./path');
|
const Path = require('./path');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Wallet Key
|
||||||
* Represents a key ring which amounts to an address.
|
* Represents a key ring which amounts to an address.
|
||||||
* @alias module:wallet.WalletKey
|
* @alias module:wallet.WalletKey
|
||||||
* @constructor
|
* @extends KeyRing
|
||||||
* @param {Object} options
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
function WalletKey(options) {
|
class WalletKey extends KeyRing {
|
||||||
if (!(this instanceof WalletKey))
|
/**
|
||||||
return new WalletKey(options);
|
* 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.name = null;
|
||||||
this.account = -1;
|
this.account = -1;
|
||||||
this.branch = -1;
|
this.branch = -1;
|
||||||
this.index = -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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
path.keyType = this.keyType;
|
/**
|
||||||
|
* Instantiate key ring from options.
|
||||||
|
* @param {Object} options
|
||||||
|
* @returns {WalletKey}
|
||||||
|
*/
|
||||||
|
|
||||||
path.version = this.getVersion();
|
static fromOptions(options) {
|
||||||
path.type = this.getType();
|
return new this().fromOptions(options);
|
||||||
path.hash = this.getHash('hex');
|
}
|
||||||
|
|
||||||
return path;
|
/**
|
||||||
};
|
* Instantiate wallet key from a private key.
|
||||||
|
* @param {Buffer} key
|
||||||
|
* @param {Boolean?} compressed
|
||||||
|
* @returns {WalletKey}
|
||||||
|
*/
|
||||||
|
|
||||||
/**
|
static fromPrivate(key, compressed) {
|
||||||
* Test whether an object is a WalletKey.
|
return new this().fromPrivate(key, compressed);
|
||||||
* @param {Object} obj
|
}
|
||||||
* @returns {Boolean}
|
|
||||||
*/
|
|
||||||
|
|
||||||
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
|
* Expose
|
||||||
|
|||||||
@ -168,7 +168,7 @@ MemWallet.prototype.getUndo = function getUndo(key) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
MemWallet.prototype.addCoin = function addCoin(coin) {
|
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();
|
const key = op.toKey();
|
||||||
|
|
||||||
this.filter.add(op.toRaw());
|
this.filter.add(op.toRaw());
|
||||||
@ -301,7 +301,7 @@ MemWallet.prototype.removeTX = function removeTX(tx, height) {
|
|||||||
return false;
|
return false;
|
||||||
|
|
||||||
for (let i = 0; i < tx.outputs.length; i++) {
|
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);
|
const coin = this.getCoin(op);
|
||||||
|
|
||||||
if (!coin)
|
if (!coin)
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user