primitives: cloning and assertions.

This commit is contained in:
Christopher Jeffrey 2017-01-15 15:14:48 -08:00
parent 58da4be8fa
commit a53b2c0eef
No known key found for this signature in database
GPG Key ID: 8962AB9DE6666BBD
9 changed files with 269 additions and 81 deletions

View File

@ -791,7 +791,7 @@ RPC.prototype._txToJSON = function _txToJSON(tx, entry) {
if (tx.isCoinbase()) { if (tx.isCoinbase()) {
out.coinbase = input.script.toJSON(); out.coinbase = input.script.toJSON();
} else { } else {
out.txid = input.prevout.rhash(); out.txid = input.prevout.txid();
out.vout = input.prevout.index; out.vout = input.prevout.index;
out.scriptSig = { out.scriptSig = {
asm: input.script.toASM(), asm: input.script.toASM(),
@ -3381,7 +3381,7 @@ RPC.prototype.listlockunspent = co(function* listlockunspent(args) {
for (i = 0; i < outpoints.length; i++) { for (i = 0; i < outpoints.length; i++) {
outpoint = outpoints[i]; outpoint = outpoints[i];
out.push({ out.push({
txid: outpoint.rhash(), txid: outpoint.txid(),
vout: outpoint.index vout: outpoint.index
}); });
} }
@ -3749,7 +3749,7 @@ RPC.prototype.listunspent = co(function* listunspent(args) {
ring = yield wallet.getKey(hash); ring = yield wallet.getKey(hash);
out.push({ out.push({
txid: coin.rhash(), txid: coin.txid(),
vout: coin.index, vout: coin.index,
address: address ? address.toBase58(this.network) : null, address: address ? address.toBase58(this.network) : null,
account: ring ? ring.name : undefined, account: ring ? ring.name : undefined,

View File

@ -62,17 +62,21 @@ Coin.prototype.fromOptions = function fromOptions(options) {
assert(options, 'Coin data is required.'); assert(options, 'Coin data is required.');
if (options.version != null) { if (options.version != null) {
assert(util.isUInt32(options.version)); assert(util.isUInt32(options.version), 'Version must be a uint32.');
this.version = options.version; this.version = options.version;
} }
if (options.height != null) { if (options.height != null) {
assert(options.height === -1 || util.isUInt32(options.height)); if (options.height !== -1) {
this.height = options.height; assert(util.isUInt32(options.height), 'Height must be a uint32.');
this.height = options.height;
} else {
this.height = -1;
}
} }
if (options.value != null) { if (options.value != null) {
assert(util.isUInt53(options.value)); assert(util.isUInt53(options.value), 'Value must be a uint53.');
this.value = options.value; this.value = options.value;
} }
@ -80,17 +84,18 @@ Coin.prototype.fromOptions = function fromOptions(options) {
this.script.fromOptions(options.script); this.script.fromOptions(options.script);
if (options.coinbase != null) { if (options.coinbase != null) {
assert(typeof options.coinbase === 'boolean'); assert(typeof options.coinbase === 'boolean',
'Coinbase must be a boolean.');
this.coinbase = options.coinbase; this.coinbase = options.coinbase;
} }
if (options.hash != null) { if (options.hash != null) {
assert(typeof options.hash === 'string'); assert(typeof options.hash === 'string', 'Hash must be a string.');
this.hash = options.hash; this.hash = options.hash;
} }
if (options.index != null) { if (options.index != null) {
assert(util.isUInt32(options.index)); assert(util.isUInt32(options.index), 'Index must be a uint32.');
this.index = options.index; this.index = options.index;
} }
@ -107,6 +112,16 @@ Coin.fromOptions = function fromOptions(options) {
return new Coin().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. * Calculate number of confirmations since coin was created.
* @param {Number?} height - Current chain height. Network * @param {Number?} height - Current chain height. Network
@ -172,6 +187,15 @@ Coin.prototype.rhash = function rhash() {
return util.revHex(this.hash); return util.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. * Convert the coin to a more user-friendly object.
* @returns {Object} * @returns {Object}
@ -239,20 +263,25 @@ Coin.prototype.getJSON = function getJSON(network, minimal) {
Coin.prototype.fromJSON = function fromJSON(json) { Coin.prototype.fromJSON = function fromJSON(json) {
assert(json, 'Coin data required.'); assert(json, 'Coin data required.');
assert(util.isNumber(json.version)); assert(util.isUInt32(json.version), 'Version must be a uint32.');
assert(util.isNumber(json.height)); assert(json.height === -1 || util.isUInt32(json.height),
assert(typeof json.value === 'string'); 'Height must be a uint32.');
assert(typeof json.coinbase === 'boolean'); assert(typeof json.value === 'string', 'Value must be a string.');
assert(!json.hash || typeof json.hash === 'string'); assert(typeof json.coinbase === 'boolean', 'Coinbase must be a boolean.');
assert(!json.index || util.isNumber(json.index));
this.version = json.version; this.version = json.version;
this.height = json.height; this.height = json.height;
this.value = Amount.value(json.value); this.value = Amount.value(json.value);
this.script.fromJSON(json.script); this.script.fromJSON(json.script);
this.coinbase = json.coinbase; this.coinbase = json.coinbase;
this.hash = json.hash ? util.revHex(json.hash) : encoding.NULL_HASH;
this.index = json.index != null ? json.index : 0; 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(util.isUInt32(json.index), 'Index must be a uint32.');
this.hash = util.revHex(json.hash);
this.index = json.index;
}
return this; return this;
}; };
@ -365,9 +394,9 @@ Coin.fromRaw = function fromRaw(data, enc) {
*/ */
Coin.prototype.fromTX = function fromTX(tx, index, height) { Coin.prototype.fromTX = function fromTX(tx, index, height) {
assert(util.isNumber(index)); assert(typeof index === 'number');
assert(index < tx.outputs.length);
assert(typeof height === 'number'); assert(typeof height === 'number');
assert(index >= 0 && index < tx.outputs.length);
this.version = tx.version; this.version = tx.version;
this.height = height; this.height = height;
this.value = tx.outputs[index].value; this.value = tx.outputs[index].value;

View File

@ -48,7 +48,6 @@ function Input(options) {
Input.prototype.fromOptions = function fromOptions(options) { Input.prototype.fromOptions = function fromOptions(options) {
assert(options, 'Input data is required.'); assert(options, 'Input data is required.');
assert(options.prevout);
this.prevout.fromOptions(options.prevout); this.prevout.fromOptions(options.prevout);
@ -56,7 +55,7 @@ Input.prototype.fromOptions = function fromOptions(options) {
this.script.fromOptions(options.script); this.script.fromOptions(options.script);
if (options.sequence != null) { if (options.sequence != null) {
assert(util.isUInt32(options.sequence)); assert(util.isUInt32(options.sequence), 'Sequence must be a uint32.');
this.sequence = options.sequence; this.sequence = options.sequence;
} }
@ -76,6 +75,20 @@ Input.fromOptions = function fromOptions(options) {
return new Input().fromOptions(options); return new Input().fromOptions(options);
}; };
/**
* Clone the input.
* @returns {Input}
*/
Input.prototype.clone = function clone() {
var input = new Input();
input.prevout = this.prevout;
input.script.inject(this.script);
input.sequence = this.sequence;
input.witness.inject(this.witness);
return input;
};
/** /**
* Get the previous output script type as a string. * Get the previous output script type as a string.
* Will "guess" based on the input script and/or * Will "guess" based on the input script and/or
@ -300,7 +313,7 @@ Input.prototype.getJSON = function getJSON(network, coin) {
Input.prototype.fromJSON = function fromJSON(json) { Input.prototype.fromJSON = function fromJSON(json) {
assert(json, 'Input data is required.'); assert(json, 'Input data is required.');
assert(util.isUInt32(json.sequence)); assert(util.isUInt32(json.sequence), 'Sequence must be a uint32.');
this.prevout.fromJSON(json.prevout); this.prevout.fromJSON(json.prevout);
this.script.fromJSON(json.script); this.script.fromJSON(json.script);
this.witness.fromJSON(json.witness); this.witness.fromJSON(json.witness);
@ -453,7 +466,7 @@ Input.fromCoin = function fromCoin(coin) {
Input.prototype.fromTX = function fromTX(tx, index) { Input.prototype.fromTX = function fromTX(tx, index) {
assert(tx); assert(tx);
assert(typeof index === 'number'); assert(typeof index === 'number');
assert(index < tx.outputs.length); assert(index >= 0 && index < tx.outputs.length);
this.prevout.hash = tx.hash('hex'); this.prevout.hash = tx.hash('hex');
this.prevout.index = index; this.prevout.index = index;
return this; return this;

View File

@ -70,35 +70,40 @@ MTX.prototype.fromOptions = function fromOptions(options) {
var i; var i;
if (options.version != null) { if (options.version != null) {
assert(util.isUInt32(options.version)); assert(util.isUInt32(options.version), 'Version must a be uint32.');
this.version = options.version; this.version = options.version;
} }
if (options.flag != null) { if (options.flag != null) {
assert(util.isUInt8(options.flag)); assert(util.isUInt8(options.flag), 'Flag must be a uint8.');
this.flag = options.flag; this.flag = options.flag;
} }
if (options.inputs) { if (options.inputs) {
assert(Array.isArray(options.inputs)); assert(Array.isArray(options.inputs), 'Inputs must be an array.');
for (i = 0; i < options.inputs.length; i++) for (i = 0; i < options.inputs.length; i++)
this.addInput(options.inputs[i]); this.addInput(options.inputs[i]);
} }
if (options.outputs) { if (options.outputs) {
assert(Array.isArray(options.outputs)); assert(Array.isArray(options.outputs), 'Outputs must be an array.');
for (i = 0; i < options.outputs.length; i++) for (i = 0; i < options.outputs.length; i++)
this.addOutput(options.outputs[i]); this.addOutput(options.outputs[i]);
} }
if (options.locktime != null) { if (options.locktime != null) {
assert(util.isUInt32(options.locktime)); assert(util.isUInt32(options.locktime), 'Locktime must be a uint32.');
this.locktime = options.locktime; this.locktime = options.locktime;
} }
if (options.changeIndex != null) { if (options.changeIndex != null) {
assert(util.isNumber(options.changeIndex)); if (options.changeIndex !== -1) {
this.changeIndex = options.changeIndex; assert(util.isUInt32(options.changeIndex),
'Change index must be a uint32.');
this.changeIndex = options.changeIndex;
} else {
this.changeIndex = -1;
}
} }
return this; return this;
@ -115,12 +120,16 @@ MTX.fromOptions = function fromOptions(options) {
}; };
/** /**
* Clone the transaction. * Clone the transaction. Note that
* this will not carry over the view.
* @returns {MTX} * @returns {MTX}
*/ */
MTX.prototype.clone = function clone() { MTX.prototype.clone = function clone() {
return new MTX(this); var mtx = new MTX();
mtx.inject(this);
mtx.changeIndex = this.changeIndex;
return mtx;
}; };
/** /**
@ -211,8 +220,8 @@ MTX.prototype.addTX = function addTX(tx, index, height) {
/** /**
* Add an output. * Add an output.
* @param {Address|Script|Output|Object} options * @param {Address|Script|Output|Object} script - Script or output options.
* @param {Amount?} value - Only needs to be present for non-output options. * @param {Amount?} value
* @returns {Output} * @returns {Output}
* *
* @example * @example
@ -222,24 +231,14 @@ MTX.prototype.addTX = function addTX(tx, index, height) {
* mtx.addOutput(script, 100000); * mtx.addOutput(script, 100000);
*/ */
MTX.prototype.addOutput = function addOutput(options, value) { MTX.prototype.addOutput = function addOutput(script, value) {
var output; var output;
if (typeof options === 'string') if (value != null) {
options = Address.fromBase58(options);
if (options instanceof Address)
options = Script.fromAddress(options);
output = new Output();
if (options instanceof Script) {
assert(util.isUInt53(value), 'Value must be a uint53.'); assert(util.isUInt53(value), 'Value must be a uint53.');
output.script.fromOptions(options); output = Output.fromScript(script, value);
output.value = value;
} else { } else {
output.fromOptions(options); output = Output.fromOptions(script);
assert(util.isUInt53(output.value), 'Value must be a uint53.');
} }
this.outputs.push(output); this.outputs.push(output);
@ -1404,7 +1403,7 @@ MTX.fromRaw = function fromRaw(data, enc) {
*/ */
MTX.prototype.toTX = function toTX() { MTX.prototype.toTX = function toTX() {
return new TX(this); return new TX().inject(this);
}; };
/** /**
@ -1414,7 +1413,7 @@ MTX.prototype.toTX = function toTX() {
*/ */
MTX.fromTX = function fromTX(tx) { MTX.fromTX = function fromTX(tx) {
return new MTX(tx); return new MTX().inject(tx);
}; };
/** /**
@ -1834,8 +1833,8 @@ function sortRandom(a, b) {
} }
function sortInputs(a, b) { function sortInputs(a, b) {
var ahash = a.prevout.rhash(); var ahash = a.prevout.txid();
var bhash = b.prevout.rhash(); var bhash = b.prevout.txid();
var cmp = util.strcmp(ahash, bhash); var cmp = util.strcmp(ahash, bhash);
if (cmp !== 0) if (cmp !== 0)

View File

@ -26,8 +26,15 @@ function Outpoint(hash, index) {
if (!(this instanceof Outpoint)) if (!(this instanceof Outpoint))
return new Outpoint(hash, index); return new Outpoint(hash, index);
this.hash = hash || encoding.NULL_HASH; this.hash = encoding.NULL_HASH;
this.index = index != null ? index : 0xffffffff; this.index = 0xffffffff;
if (hash != null) {
assert(typeof hash === 'string', 'Hash must be a string.');
assert(util.isUInt32(index), 'Index must be a uint32.');
this.hash = hash;
this.index = index;
}
} }
/** /**
@ -37,8 +44,9 @@ function Outpoint(hash, index) {
*/ */
Outpoint.prototype.fromOptions = function fromOptions(options) { Outpoint.prototype.fromOptions = function fromOptions(options) {
assert(typeof options.hash === 'string'); assert(options, 'Outpoint data is required.');
assert(util.isNumber(options.index)); assert(typeof options.hash === 'string', 'Hash must be a string.');
assert(util.isUInt32(options.index), 'Index must be a uint32.');
this.hash = options.hash; this.hash = options.hash;
this.index = options.index; this.index = options.index;
return this; return this;
@ -73,6 +81,15 @@ Outpoint.prototype.rhash = function rhash() {
return util.revHex(this.hash); return util.revHex(this.hash);
}; };
/**
* Get little-endian hash.
* @returns {Hash}
*/
Outpoint.prototype.txid = function txid() {
return this.rhash();
};
/** /**
* Serialize outpoint to a key * Serialize outpoint to a key
* suitable for a hash table. * suitable for a hash table.
@ -185,8 +202,9 @@ Outpoint.fromRaw = function fromRaw(data) {
*/ */
Outpoint.prototype.fromJSON = function fromJSON(json) { Outpoint.prototype.fromJSON = function fromJSON(json) {
assert(typeof json.hash === 'string'); assert(json, 'Outpoint data is required.');
assert(util.isNumber(json.index)); assert(typeof json.hash === 'string', 'Hash must be a string.');
assert(util.isUInt32(json.index), 'Index must be a uint32.');
this.hash = util.revHex(json.hash); this.hash = util.revHex(json.hash);
this.index = json.index; this.index = json.index;
return this; return this;
@ -225,7 +243,8 @@ Outpoint.fromJSON = function fromJSON(json) {
*/ */
Outpoint.prototype.fromTX = function fromTX(tx, index) { Outpoint.prototype.fromTX = function fromTX(tx, index) {
assert(util.isNumber(index)); assert(tx);
assert(typeof index === 'number');
this.hash = tx.hash('hex'); this.hash = tx.hash('hex');
this.index = index; this.index = index;
return this; return this;

View File

@ -11,6 +11,7 @@ var assert = require('assert');
var util = require('../utils/util'); var util = require('../utils/util');
var Amount = require('../btc/amount'); var Amount = require('../btc/amount');
var Network = require('../protocol/network'); var Network = require('../protocol/network');
var Address = require('../primitives/address');
var Script = require('../script/script'); var Script = require('../script/script');
var StaticWriter = require('../utils/staticwriter'); var StaticWriter = require('../utils/staticwriter');
var BufferReader = require('../utils/reader'); var BufferReader = require('../utils/reader');
@ -47,7 +48,7 @@ Output.prototype.fromOptions = function fromOptions(options) {
assert(options, 'Output data is required.'); assert(options, 'Output data is required.');
if (options.value) { if (options.value) {
assert(util.isInt53(options.value)); assert(util.isUInt53(options.value), 'Value must be a uint53.');
this.value = options.value; this.value = options.value;
} }
@ -70,6 +71,53 @@ Output.fromOptions = function fromOptions(options) {
return new Output().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.fromBase58(script);
if (script instanceof Address)
script = Script.fromAddress(script);
assert(script instanceof Script, 'Script must be a Script.');
assert(util.isUInt53(value), 'Value must be a uint53.');
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() {
var output = new Output();
output.value = this.value;
output.script.inject(this.script);
return output;
};
/** /**
* Get the script type as a string. * Get the script type as a string.
* @returns {ScriptType} type * @returns {ScriptType} type
@ -199,7 +247,8 @@ Output.prototype.isDust = function isDust(rate) {
*/ */
Output.prototype.fromJSON = function fromJSON(json) { Output.prototype.fromJSON = function fromJSON(json) {
assert(typeof json.value === 'string'); assert(json, 'Output data is required.');
assert(typeof json.value === 'string', 'Value must be a string.');
this.value = Amount.value(json.value); this.value = Amount.value(json.value);
this.script.fromJSON(json.script); this.script.fromJSON(json.script);
return this; return this;

View File

@ -94,29 +94,29 @@ TX.prototype.fromOptions = function fromOptions(options) {
assert(options, 'TX data is required.'); assert(options, 'TX data is required.');
if (options.version != null) { if (options.version != null) {
assert(util.isUInt32(options.version)); assert(util.isUInt32(options.version), 'Version must be a uint32.');
this.version = options.version; this.version = options.version;
} }
if (options.flag != null) { if (options.flag != null) {
assert(util.isUInt8(options.flag)); assert(util.isUInt8(options.flag), 'Flag must be a uint8.');
this.flag = options.flag; this.flag = options.flag;
} }
if (options.inputs) { if (options.inputs) {
assert(Array.isArray(options.inputs)); assert(Array.isArray(options.inputs), 'Inputs must be an array.');
for (i = 0; i < options.inputs.length; i++) for (i = 0; i < options.inputs.length; i++)
this.inputs.push(new Input(options.inputs[i])); this.inputs.push(new Input(options.inputs[i]));
} }
if (options.outputs) { if (options.outputs) {
assert(Array.isArray(options.outputs)); assert(Array.isArray(options.outputs), 'Outputs must be an array.');
for (i = 0; i < options.outputs.length; i++) for (i = 0; i < options.outputs.length; i++)
this.outputs.push(new Output(options.outputs[i])); this.outputs.push(new Output(options.outputs[i]));
} }
if (options.locktime != null) { if (options.locktime != null) {
assert(util.isUInt32(options.locktime)); assert(util.isUInt32(options.locktime), 'Locktime must be a uint32.');
this.locktime = options.locktime; this.locktime = options.locktime;
} }
@ -139,7 +139,36 @@ TX.fromOptions = function fromOptions(options) {
*/ */
TX.prototype.clone = function clone() { TX.prototype.clone = function clone() {
return new TX(this); return new TX().inject(this);
};
/**
* Inject properties from tx.
* Used for cloning.
* @private
* @param {TX} tx
* @returns {TX}
*/
TX.prototype.inject = function inject(tx) {
var i, input, output;
this.version = tx.version;
this.flag = tx.flag;
for (i = 0; i < tx.inputs.length; i++) {
input = tx.inputs[i];
this.inputs.push(input.clone());
}
for (i = 0; i < tx.outputs.length; i++) {
output = tx.outputs[i];
this.outputs.push(output.clone());
}
this.locktime = tx.locktime;
return this;
}; };
/** /**

View File

@ -147,14 +147,14 @@ Script.prototype.fromOptions = function fromOptions(options) {
if (options.raw) { if (options.raw) {
if (!options.code) if (!options.code)
return this.fromRaw(options.raw); return this.fromRaw(options.raw);
assert(Buffer.isBuffer(options.raw)); assert(Buffer.isBuffer(options.raw), 'Raw must be a Buffer.');
this.raw = options.raw; this.raw = options.raw;
} }
if (options.code) { if (options.code) {
if (!options.raw) if (!options.raw)
return this.fromCode(options.code); return this.fromCode(options.code);
assert(Array.isArray(options.code)); assert(Array.isArray(options.code), 'Code must be an array.');
this.code = options.code; this.code = options.code;
} }
@ -284,7 +284,21 @@ Script.fromCode = function fromCode(code) {
*/ */
Script.prototype.clone = function clone() { Script.prototype.clone = function clone() {
return new Script(this.raw); return new Script().inject(this);
};
/**
* Inject properties from script.
* Used for cloning.
* @private
* @param {Script} script
* @returns {Script}
*/
Script.prototype.inject = function inject(script) {
this.code = script.code.slice();
this.raw = script.raw;
return this;
}; };
/** /**
@ -388,7 +402,7 @@ Script.prototype.toJSON = function toJSON() {
*/ */
Script.prototype.fromJSON = function fromJSON(json) { Script.prototype.fromJSON = function fromJSON(json) {
assert(typeof json === 'string'); assert(typeof json === 'string', 'Code must be a string.');
return this.fromRaw(new Buffer(json, 'hex')); return this.fromRaw(new Buffer(json, 'hex'));
}; };
@ -417,12 +431,14 @@ Script.prototype.getSubscript = function getSubscript(lastSep) {
for (i = lastSep; i < this.code.length; i++) { for (i = lastSep; i < this.code.length; i++) {
op = this.code[i]; op = this.code[i];
if (op.value === -1) if (op.value === -1)
break; break;
code.push(op); code.push(op);
} }
return new Script(code); return Script.fromCode(code);
}; };
/** /**
@ -434,21 +450,42 @@ Script.prototype.getSubscript = function getSubscript(lastSep) {
*/ */
Script.prototype.removeSeparators = function removeSeparators() { Script.prototype.removeSeparators = function removeSeparators() {
var code = []; var found = false;
var i, op; var i, op, code;
// Optimizing for the common case:
// Check for any separators first.
for (i = 0; i < this.code.length; i++) {
op = this.code[i];
if (op.value === -1)
break;
if (op.value === opcodes.OP_CODESEPARATOR) {
found = true;
break;
}
}
if (!found)
return this.clone();
// Uncommon case: someone actually
// has a code separator. Go through
// and remove them all.
code = [];
for (i = 0; i < this.code.length; i++) { for (i = 0; i < this.code.length; i++) {
op = this.code[i]; op = this.code[i];
if (op.value === -1) if (op.value === -1)
break; break;
if (op.value !== opcodes.OP_CODESEPARATOR) if (op.value !== opcodes.OP_CODESEPARATOR)
code.push(op); code.push(op);
} }
if (code.length === this.code.length) return Script.fromCode(code);
return this.clone();
return new Script(code);
}; };
/** /**

View File

@ -165,7 +165,20 @@ Witness.prototype.toASM = function toASM(decode) {
*/ */
Witness.prototype.clone = function clone() { Witness.prototype.clone = function clone() {
return new Witness(this.items.slice()); return new Witness().inject(this);
};
/**
* Inject properties from witness.
* Used for cloning.
* @private
* @param {Witness} witness
* @returns {Witness}
*/
Witness.prototype.inject = function inject(witness) {
this.items = witness.items.slice();
return this;
}; };
/** /**
@ -387,7 +400,7 @@ Witness.prototype.toJSON = function toJSON() {
*/ */
Witness.prototype.fromJSON = function fromJSON(json) { Witness.prototype.fromJSON = function fromJSON(json) {
assert(typeof json === 'string'); assert(typeof json === 'string', 'Witness must be a string.');
return this.fromRaw(new Buffer(json, 'hex')); return this.fromRaw(new Buffer(json, 'hex'));
}; };