script work.

This commit is contained in:
Christopher Jeffrey 2016-06-14 18:13:51 -07:00
parent 27b18c0fd2
commit 3a18052f87
No known key found for this signature in database
GPG Key ID: 8962AB9DE6666BBD
3 changed files with 269 additions and 143 deletions

View File

@ -373,7 +373,7 @@ function MinerBlock(options) {
index: 0xffffffff index: 0xffffffff
}, },
coin: null, coin: null,
script: new bcoin.script([ script: bcoin.script.fromArray([
// Height (required in v2+ blocks) // Height (required in v2+ blocks)
bcoin.script.array(this.height), bcoin.script.array(this.height),
// extraNonce - incremented when // extraNonce - incremented when
@ -442,9 +442,10 @@ utils.inherits(MinerBlock, EventEmitter);
*/ */
MinerBlock.prototype.updateCommitment = function updateCommitment() { MinerBlock.prototype.updateCommitment = function updateCommitment() {
var output = this.coinbase.outputs[1];
var hash = this.block.getCommitmentHash(); var hash = this.block.getCommitmentHash();
var commit = bcoin.script.createCommitment(hash, this.coinbaseFlags); var commit = bcoin.script.createCommitment(hash, this.coinbaseFlags);
this.coinbase.outputs[1].script = commit; output.script = commit;
}; };
/** /**
@ -453,9 +454,10 @@ MinerBlock.prototype.updateCommitment = function updateCommitment() {
MinerBlock.prototype.updateCoinbase = function updateCoinbase() { MinerBlock.prototype.updateCoinbase = function updateCoinbase() {
var input = this.coinbase.inputs[0]; var input = this.coinbase.inputs[0];
input.script.code[1] = bcoin.opcode.fromNumber(this.extraNonce); var output = this.coinbase.outputs[0];
input.script.set(1, this.extraNonce);
input.script.compile(); input.script.compile();
this.coinbase.outputs[0].value = this.block.getReward(this.network); output.value = this.block.getReward(this.network);
}; };
/** /**

View File

@ -161,8 +161,7 @@ MTX.prototype.addInput = function addInput(options, index) {
*/ */
MTX.prototype.scriptInput = function scriptInput(index, addr) { MTX.prototype.scriptInput = function scriptInput(index, addr) {
var input, prev, n, i, redeemScript, witnessScript, vector, dummy; var input, prev, n, i, vector, redeemScript, witnessScript;
var inputScript, witnessItems;
if (typeof index !== 'number') if (typeof index !== 'number')
index = this.inputs.indexOf(index); index = this.inputs.indexOf(index);
@ -174,13 +173,10 @@ MTX.prototype.scriptInput = function scriptInput(index, addr) {
// We should have previous outputs by now. // We should have previous outputs by now.
assert(input.coin, 'Coins are not available for scripting.'); assert(input.coin, 'Coins are not available for scripting.');
inputScript = input.script.toArray();
witnessItems = input.witness.toArray();
// Optimization: Don't bother with any below // Optimization: Don't bother with any below
// calculation if the output is already templated. // calculation if the output is already templated.
// Just say this is "our" output. // Just say this is "our" output.
if (inputScript.length || witnessItems.length) if (input.script.length !== 0 || input.witness.length !== 0)
return true; return true;
// Optimization: test output against the // Optimization: test output against the
@ -197,11 +193,10 @@ MTX.prototype.scriptInput = function scriptInput(index, addr) {
// with segwit: figuring out where the redeem script and witness // with segwit: figuring out where the redeem script and witness
// redeem scripts go. // redeem scripts go.
if (prev.isScripthash()) { if (prev.isScripthash()) {
if (addr.program && utils.equal(prev.code[1].data, addr.programHash)) { if (addr.program && utils.equal(prev.get(1), addr.programHash)) {
// Witness program nested in regular P2SH. // Witness program nested in regular P2SH.
redeemScript = addr.program.encode(); redeemScript = addr.program.encode();
vector = witnessItems; vector = input.witness;
dummy = new Buffer([]);
if (addr.program.isWitnessScripthash()) { if (addr.program.isWitnessScripthash()) {
// P2WSH nested within pay-to-scripthash // P2WSH nested within pay-to-scripthash
// (it had to be this complicated, didn't it?) // (it had to be this complicated, didn't it?)
@ -213,117 +208,114 @@ MTX.prototype.scriptInput = function scriptInput(index, addr) {
} else { } else {
assert(false, 'Unknown program.'); assert(false, 'Unknown program.');
} }
} else if (addr.script && utils.equal(prev.code[1].data, addr.scriptHash160)) { } else if (addr.script && utils.equal(prev.get(1), addr.scriptHash160)) {
// Regular P2SH. // Regular P2SH.
redeemScript = addr.script.encode(); redeemScript = addr.script.encode();
vector = inputScript; vector = input.script;
prev = addr.script; prev = addr.script;
dummy = opcodes.OP_0;
} else { } else {
return false; return false;
} }
} else if (prev.isWitnessProgram()) { } else if (prev.isWitnessProgram()) {
// Witness program. // Witness program.
vector = witnessItems; vector = input.witness;
dummy = new Buffer([]);
if (prev.isWitnessScripthash()) { if (prev.isWitnessScripthash()) {
// Bare P2WSH. // Bare P2WSH.
if (!addr.script || !utils.equal(prev.code[1].data, addr.scriptHash256)) if (!addr.script || !utils.equal(prev.get(1), addr.scriptHash256))
return false; return false;
witnessScript = addr.script.encode(); witnessScript = addr.script.encode();
prev = addr.script; prev = addr.script;
} else if (prev.isWitnessPubkeyhash()) { } else if (prev.isWitnessPubkeyhash()) {
// Bare P2WPKH. // Bare P2WPKH.
if (!utils.equal(prev.code[1].data, addr.keyHash)) if (!utils.equal(prev.get(1), addr.keyHash))
return false; return false;
prev = Script.createPubkeyhash(prev.code[1].data); prev = Script.createPubkeyhash(prev.get(1));
} else { } else {
// Bare... who knows? // Bare... who knows?
return false; return false;
} }
} else { } else {
// Wow, a normal output! Praise be to Jengus and Gord. // Wow, a normal output! Praise be to Jengus and Gord.
vector = inputScript; vector = input.script;
dummy = opcodes.OP_0;
} }
if (prev.isPubkey()) { if (prev.isPubkey()) {
// P2PK // P2PK
if (!utils.equal(prev.code[0].data, addr.publicKey)) if (!utils.equal(prev.get(1), addr.publicKey))
return false; return false;
// Already has a script template (at least) // Already has a script template (at least)
if (vector.length) if (vector.length !== 0)
return true; return true;
vector[0] = dummy; vector.set(0, opcodes.OP_0);
} else if (prev.isPubkeyhash()) { } else if (prev.isPubkeyhash()) {
// P2PKH // P2PKH
if (!utils.equal(prev.code[2].data, addr.keyHash)) if (!utils.equal(prev.get(2), addr.keyHash))
return false; return false;
// Already has a script template (at least) // Already has a script template (at least)
if (vector.length) if (vector.length !== 0)
return true; return true;
vector[0] = dummy; vector.set(0, opcodes.OP_0);
vector[1] = addr.publicKey; vector.set(1, addr.publicKey);
} else if (prev.isMultisig()) { } else if (prev.isMultisig()) {
// Multisig // Multisig
if (prev.indexOf(addr.publicKey) === -1) if (prev.indexOf(addr.publicKey) === -1)
return false; return false;
// Already has a script template (at least) // Already has a script template (at least)
if (vector.length) if (vector.length !== 0)
return true; return true;
// Technically we should create m signature slots, // Technically we should create m signature slots,
// but we create n signature slots so we can order // but we create n signature slots so we can order
// the signatures properly. // the signatures properly.
vector[0] = dummy; vector.set(0, opcodes.OP_0);
// Grab `n` value (number of keys). // Grab `n` value (number of keys).
n = prev.getSmall(-2); n = prev.getSmall(prev.length - 2);
// Fill script with `n` signature slots. // Fill script with `n` signature slots.
for (i = 0; i < n; i++) for (i = 0; i < n; i++)
vector[i + 1] = dummy; vector.set(i + 1, opcodes.OP_0);
} else { } else {
if (prev.indexOf(addr.publicKey) === -1) if (prev.indexOf(addr.publicKey) === -1)
return false; return false;
// Already has a script template (at least) // Already has a script template (at least)
if (vector.length) if (vector.length !== 0)
return true; return true;
// Likely a non-standard scripthash multisig // Likely a non-standard scripthash multisig
// input. Determine n value by counting keys. // input. Determine n value by counting keys.
// Also, only allow nonstandard types for // Also, only allow nonstandard types for
// scripthash. // scripthash.
vector[0] = dummy; vector.set(0, opcodes.OP_0);
// Fill script with `n` signature slots. // Fill script with `n` signature slots.
for (i = 0; i < prev.code.length; i++) { for (i = 0; i < prev.length; i++) {
if (Script.isKey(prev.code[i].data)) if (Script.isKey(prev.get(i)))
vector[i + 1] = dummy; vector.set(i + 1, opcodes.OP_0);
} }
} }
// P2SH requires the redeem // P2SH requires the redeem
// script after signatures. // script after signatures.
if (redeemScript) if (redeemScript)
inputScript.push(redeemScript); input.script.push(redeemScript);
// P2WSH requires the witness // P2WSH requires the witness
// script after signatures. // script after signatures.
if (witnessScript) if (witnessScript)
witnessItems.push(witnessScript); input.witness.push(witnessScript);
input.script = bcoin.script.fromArray(inputScript); input.script.compile();
input.witness = bcoin.witness.fromArray(witnessItems); input.witness.compile();
return true; return true;
}; };
@ -372,7 +364,7 @@ MTX.prototype.createSignature = function createSignature(index, prev, key, type,
MTX.prototype.signInput = function signInput(index, addr, key, type) { MTX.prototype.signInput = function signInput(index, addr, key, type) {
var input, prev, signature, keyIndex, signatures, i; var input, prev, signature, keyIndex, signatures, i;
var len, m, n, keys, vector, dummy, version, inputScript, witnessItems; var len, m, n, keys, vector, version;
if (typeof index !== 'number') if (typeof index !== 'number')
index = this.inputs.indexOf(index); index = this.inputs.indexOf(index);
@ -384,15 +376,11 @@ MTX.prototype.signInput = function signInput(index, addr, key, type) {
// We should have previous outputs by now. // We should have previous outputs by now.
assert(input.coin, 'Coins are not available for signing.'); assert(input.coin, 'Coins are not available for signing.');
inputScript = input.script.toArray();
witnessItems = input.witness.toArray();
// Get the previous output's script // Get the previous output's script
prev = input.coin.script; prev = input.coin.script;
vector = inputScript; vector = input.script;
len = vector.length; len = vector.length;
dummy = opcodes.OP_0;
version = 0; version = 0;
// We need to grab the redeem script when // We need to grab the redeem script when
@ -410,15 +398,13 @@ MTX.prototype.signInput = function signInput(index, addr, key, type) {
// pushes onto the stack). // pushes onto the stack).
if (prev.isWitnessScripthash()) { if (prev.isWitnessScripthash()) {
prev = input.witness.getRedeem(); prev = input.witness.getRedeem();
vector = witnessItems; vector = input.witness;
len = vector.length - 1; len = vector.length - 1;
dummy = new Buffer([]);
version = 1; version = 1;
} else if (prev.isWitnessPubkeyhash()) { } else if (prev.isWitnessPubkeyhash()) {
prev = Script.createPubkeyhash(prev.code[1].data); prev = Script.createPubkeyhash(prev.get(1));
vector = witnessItems; vector = input.witness;
len = vector.length; len = vector.length;
dummy = new Buffer([]);
version = 1; version = 1;
} }
@ -428,17 +414,15 @@ MTX.prototype.signInput = function signInput(index, addr, key, type) {
// P2PK // P2PK
if (prev.isPubkey()) { if (prev.isPubkey()) {
// Already signed. // Already signed.
if (Script.isSignature(vector[0])) if (Script.isSignature(vector.get(0)))
return true; return true;
// Make sure the pubkey is ours. // Make sure the pubkey is ours.
if (!utils.equal(addr.publicKey, prev.code[0].data)) if (!utils.equal(addr.publicKey, prev.get(0)))
return false; return false;
vector[0] = signature; vector.set(0, signature);
vector.compile();
input.script = bcoin.script.fromArray(inputScript);
input.witness = bcoin.witness.fromArray(witnessItems);
return true; return true;
} }
@ -446,17 +430,15 @@ MTX.prototype.signInput = function signInput(index, addr, key, type) {
// P2PKH // P2PKH
if (prev.isPubkeyhash()) { if (prev.isPubkeyhash()) {
// Already signed. // Already signed.
if (Script.isSignature(vector[0])) if (Script.isSignature(vector.get(0)))
return true; return true;
// Make sure the pubkey hash is ours. // Make sure the pubkey hash is ours.
if (!utils.equal(addr.keyHash, prev.code[2].data)) if (!utils.equal(addr.keyHash, prev.get(2)))
return false; return false;
vector[0] = signature; vector.set(0, signature);
vector.compile();
input.script = bcoin.script.fromArray(inputScript);
input.witness = bcoin.witness.fromArray(witnessItems);
return true; return true;
} }
@ -465,13 +447,16 @@ MTX.prototype.signInput = function signInput(index, addr, key, type) {
if (prev.isMultisig()) { if (prev.isMultisig()) {
// Grab the redeem script's keys to figure // Grab the redeem script's keys to figure
// out where our key should go. // out where our key should go.
keys = prev.toArray().slice(1, -2); keys = [];
for (i = 1; i < prev.length - 2; i++)
keys.push(prev.get(i));
// Grab `m` value (number of sigs required). // Grab `m` value (number of sigs required).
m = prev.getSmall(0); m = prev.getSmall(0);
// Grab `n` value (number of keys). // Grab `n` value (number of keys).
n = prev.getSmall(-2); n = prev.getSmall(prev.length - 2);
} else { } else {
// Only allow non-standard signing for // Only allow non-standard signing for
// scripthash. // scripthash.
@ -480,9 +465,9 @@ MTX.prototype.signInput = function signInput(index, addr, key, type) {
keys = []; keys = [];
for (i = 0; i < prev.code.length; i++) { for (i = 0; i < prev.length; i++) {
if (Script.isKey(prev.code[i].data)) if (Script.isKey(prev.get(i)))
keys.push(prev.code[i].data); keys.push(prev.get(i));
} }
// We don't know what m is, so // We don't know what m is, so
@ -498,7 +483,7 @@ MTX.prototype.signInput = function signInput(index, addr, key, type) {
// Count the number of current signatures. // Count the number of current signatures.
signatures = 0; signatures = 0;
for (i = 1; i < len; i++) { for (i = 1; i < len; i++) {
if (Script.isSignature(vector[i])) if (Script.isSignature(vector.get(i)))
signatures++; signatures++;
} }
@ -511,7 +496,7 @@ MTX.prototype.signInput = function signInput(index, addr, key, type) {
// or by `m`. Add some signature slots for // or by `m`. Add some signature slots for
// us to use. // us to use.
while (len - 1 < n) { while (len - 1 < n) {
vector.splice(len, 0, dummy); vector.insert(len, opcodes.OP_0);
len++; len++;
} }
@ -534,8 +519,8 @@ MTX.prototype.signInput = function signInput(index, addr, key, type) {
// and increment the total number of // and increment the total number of
// signatures. // signatures.
if (keyIndex < len && signatures < m) { if (keyIndex < len && signatures < m) {
if (Script.isZero(vector[keyIndex])) { if (vector.getSmall(keyIndex) === 0) {
vector[keyIndex] = signature; vector.set(keyIndex, signature);
signatures++; signatures++;
} }
} }
@ -544,8 +529,8 @@ MTX.prototype.signInput = function signInput(index, addr, key, type) {
if (signatures >= m) { if (signatures >= m) {
// Remove empty slots left over. // Remove empty slots left over.
for (i = len - 1; i >= 1; i--) { for (i = len - 1; i >= 1; i--) {
if (Script.isZero(vector[i])) { if (vector.getSmall(i) === 0) {
vector.splice(i, 1); vector.remove(i);
len--; len--;
} }
} }
@ -555,7 +540,7 @@ MTX.prototype.signInput = function signInput(index, addr, key, type) {
// with implementations that potentially handle // with implementations that potentially handle
// signature slots differently. // signature slots differently.
while (signatures > m) { while (signatures > m) {
vector.splice(len - 1, 1); vector.remove(len - 1);
signatures--; signatures--;
len--; len--;
} }
@ -565,8 +550,7 @@ MTX.prototype.signInput = function signInput(index, addr, key, type) {
assert(len - 1 === m); assert(len - 1 === m);
} }
input.script = bcoin.script.fromArray(inputScript); vector.compile();
input.witness = bcoin.witness.fromArray(witnessItems);
return signatures === m; return signatures === m;
}; };
@ -591,7 +575,7 @@ MTX.prototype.isSigned = function isSigned() {
prev = input.coin.script; prev = input.coin.script;
// Script length, needed for multisig // Script length, needed for multisig
vector = input.script.toArray(); vector = input.script;
len = vector.length; len = vector.length;
// We need to grab the redeem script when // We need to grab the redeem script when
@ -606,19 +590,19 @@ MTX.prototype.isSigned = function isSigned() {
// and potentially alter the length. // and potentially alter the length.
if (prev.isWitnessScripthash()) { if (prev.isWitnessScripthash()) {
prev = input.witness.getRedeem(); prev = input.witness.getRedeem();
vector = input.witness.toArray(); vector = input.witness;
len = vector.length - 1; len = vector.length - 1;
} else if (prev.isWitnessPubkeyhash()) { } else if (prev.isWitnessPubkeyhash()) {
prev = Script.createPubkeyhash(prev.code[1].data); prev = Script.createPubkeyhash(prev.get(1));
vector = input.witness.toArray(); vector = input.witness;
len = vector.length; len = vector.length;
} }
if (prev.isPubkey()) { if (prev.isPubkey()) {
if (!Script.isSignature(vector[0])) if (!Script.isSignature(vector.get(0)))
return false; return false;
} else if (prev.isPubkeyhash()) { } else if (prev.isPubkeyhash()) {
if (!Script.isSignature(vector[0])) if (!Script.isSignature(vector.get(0)))
return false; return false;
} else if (prev.isMultisig()) { } else if (prev.isMultisig()) {
// Grab `m` value (number of required sigs). // Grab `m` value (number of required sigs).
@ -626,7 +610,7 @@ MTX.prototype.isSigned = function isSigned() {
// Ensure all members are signatures. // Ensure all members are signatures.
for (j = 1; j < len; j++) { for (j = 1; j < len; j++) {
if (!Script.isSignature(vector[j])) if (!Script.isSignature(vector.get(j)))
return false; return false;
} }
@ -839,7 +823,7 @@ MTX.prototype.maxSize = function maxSize(options, force) {
// here since it will be ignored by // here since it will be ignored by
// the isMultisig clause. // the isMultisig clause.
// OP_PUSHDATA2 [redeem] // OP_PUSHDATA2 [redeem]
redeem = getRedeem(input.script, prev.code[1].data); redeem = getRedeem(input.script, prev.get(1));
if (redeem) { if (redeem) {
prev = redeem; prev = redeem;
sz = prev.getSize(); sz = prev.getSize();
@ -870,7 +854,7 @@ MTX.prototype.maxSize = function maxSize(options, force) {
hadWitness = true; hadWitness = true;
if (prev.isWitnessScripthash()) { if (prev.isWitnessScripthash()) {
redeem = getRedeem(input.witness, prev.code[1].data); redeem = getRedeem(input.witness, prev.get(1));
if (redeem) { if (redeem) {
prev = redeem; prev = redeem;
sz = prev.getSize(); sz = prev.getSize();
@ -878,7 +862,7 @@ MTX.prototype.maxSize = function maxSize(options, force) {
size += sz; size += sz;
} }
} else if (prev.isWitnessPubkeyhash()) { } else if (prev.isWitnessPubkeyhash()) {
prev = Script.createPubkeyhash(prev.code[1].data); prev = Script.createPubkeyhash(prev.get(1));
} }
} }
@ -930,8 +914,8 @@ MTX.prototype.maxSize = function maxSize(options, force) {
size += 1; size += 1;
} else { } else {
// OP_PUSHDATA0 [signature] // OP_PUSHDATA0 [signature]
for (j = 0; j < prev.code.length; j++) { for (j = 0; j < prev.length; j++) {
if (Script.isKey(prev.code[j].data)) if (Script.isKey(prev.get(j)))
size += 1 + 73; size += 1 + 73;
} }
} }

View File

@ -28,6 +28,7 @@ var ScriptError = bcoin.errors.ScriptError;
* stack items or raw witness buffer. * stack items or raw witness buffer.
* @property {Buffer[]} items * @property {Buffer[]} items
* @property {Script?} redeem * @property {Script?} redeem
* @property {Number} length
*/ */
function Witness(items) { function Witness(items) {
@ -253,35 +254,98 @@ Witness.prototype.toRaw = function toRaw(enc) {
return data; return data;
}; };
/**
* Unshift an item onto the witness vector.
* @param {Number|String|Buffer|BN} data
* @returns {Number}
*/
Witness.prototype.unshift = function unshift(data) { Witness.prototype.unshift = function unshift(data) {
return this.items.unshift(Witness.encodeItem(data)); return this.items.unshift(Witness.encodeItem(data));
}; };
/**
* Push an item onto the witness vector.
* @param {Number|String|Buffer|BN} data
* @returns {Number}
*/
Witness.prototype.push = function push(data) { Witness.prototype.push = function push(data) {
return this.items.push(Witness.encodeItem(data)); return this.items.push(Witness.encodeItem(data));
}; };
/**
* Shift an item off the witness vector.
* @returns {Buffer}
*/
Witness.prototype.shift = function shift() { Witness.prototype.shift = function shift() {
return this.items.shift(); return this.items.shift();
}; };
/**
* Shift an item off the witness vector.
* @returns {Buffer}
*/
Witness.prototype.pop = function push(data) { Witness.prototype.pop = function push(data) {
return this.items.pop(); return this.items.pop();
}; };
/**
* Remove an item from the witness vector.
* @param {Number} index
* @returns {Buffer}
*/
Witness.prototype.remove = function remove(i) { Witness.prototype.remove = function remove(i) {
return this.items.splice(i, 1)[0]; return this.items.splice(i, 1)[0];
}; };
/**
* Insert an item into the witness vector.
* @param {Number} index
* @param {Number|String|Buffer|BN} data
*/
Witness.prototype.insert = function insert(i, data) { Witness.prototype.insert = function insert(i, data) {
assert(i <= this.items.length, 'Index out of bounds.'); assert(i <= this.items.length, 'Index out of bounds.');
this.items.splice(i, 0, Witness.encodeItem(data))[0]; this.items.splice(i, 0, Witness.encodeItem(data))[0];
}; };
/**
* Get an item from the witness vector.
* @param {Number} index
* @returns {Buffer}
*/
Witness.prototype.get = function get(i) { Witness.prototype.get = function get(i) {
return this.items[i]; return this.items[i];
}; };
/**
* Get a small int (0-16) from the witness vector.
* @param {Number} index
* @returns {Number} `-1` on non-existent.
*/
Witness.prototype.getSmall = function getSmall(i) {
var item = this.items[i];
if (!item || item.length > 1)
return -1;
if (item.length === 0)
return 0;
if (!(item[0] >= 1 && item[1] <= 16))
return -1;
return item[0];
};
/**
* Get a number from the witness vector.
* @param {Number} index
* @returns {BN}
*/
Witness.prototype.getNumber = function getNumber(i) { Witness.prototype.getNumber = function getNumber(i) {
var item = this.items[i]; var item = this.items[i];
if (!item || item.length > 5) if (!item || item.length > 5)
@ -289,6 +353,12 @@ Witness.prototype.getNumber = function getNumber(i) {
return Script.num(item, constants.flags.VERIFY_NONE, 5); return Script.num(item, constants.flags.VERIFY_NONE, 5);
}; };
/**
* Get a string from the witness vector.
* @param {Number} index
* @returns {String}
*/
Witness.prototype.getString = function getString(i) { Witness.prototype.getString = function getString(i) {
var item = this.items[i]; var item = this.items[i];
if (!item) if (!item)
@ -296,35 +366,54 @@ Witness.prototype.getString = function getString(i) {
return item.toString('utf8'); return item.toString('utf8');
}; };
/**
* Set an item in the witness vector.
* @param {Number} index
* @param {Number|String|Buffer|BN} data
*/
Witness.prototype.set = function set(i, data) { Witness.prototype.set = function set(i, data) {
assert(i <= this.items.length, 'Index out of bounds.'); assert(i <= this.items.length, 'Index out of bounds.');
this.items[i] = Witness.encodeItem(data); this.items[i] = Witness.encodeItem(data);
}; };
Witness.prototype.__defineGetter__('length', function() { Witness.prototype.__defineGetter__('length', function() {
return this.code.length; return this.items.length;
}); });
Witness.prototype.__defineSetter__('length', function(len) { Witness.prototype.__defineSetter__('length', function(len) {
return this.code.length = len; return this.items.length = len;
}); });
/**
* Encode a witness item.
* @param {Number|String|Buffer|BN} data
* @returns {Buffer}
*/
Witness.encodeItem = function encodeItem(data) { Witness.encodeItem = function encodeItem(data) {
if (data instanceof Opcode)
data = data.data || data.value;
if (typeof data === 'number') { if (typeof data === 'number') {
if (data === opcodes.OP_1NEGATE) if (data === opcodes.OP_1NEGATE)
data = -1; return STACK_NEGATE;
else if (data === opcodes.OP_0)
data = 0; if (data === opcodes.OP_0)
else if (data >= opcodes.OP_1 && data <= opcodes.OP_16) return STACK_FALSE;
data -= 0x50;
else if (data >= opcodes.OP_1 && data <= opcodes.OP_16)
throw new Error('Non-push opcode in witness.'); return new Buffer([data - 0x50]);
return Script.array(data);
throw new Error('Non-push opcode in witness.');
} }
if (bn.isBN(data)) if (bn.isBN(data))
return Script.array(data); return Script.array(data);
if (typeof data === 'string') if (typeof data === 'string')
return new Buffer(data, 'utf8'); return new Buffer(data, 'utf8');
return data; return data;
}; };
@ -913,6 +1002,7 @@ Stack.isStack = function isStack(obj) {
* @property {Array} code - Script code. * @property {Array} code - Script code.
* @property {Buffer?} raw - Serialized script. * @property {Buffer?} raw - Serialized script.
* @property {Script?} redeem - Redeem script. * @property {Script?} redeem - Redeem script.
* @property {Number} length
*/ */
function Script(raw) { function Script(raw) {
@ -2204,7 +2294,7 @@ Script.isCode = function isCode(raw) {
Script.createPubkey = function createPubkey(key) { Script.createPubkey = function createPubkey(key) {
assert(key.length >= 33); assert(key.length >= 33);
return new Script([key, opcodes.OP_CHECKSIG]); return Script.fromArray([key, opcodes.OP_CHECKSIG]);
}; };
/** /**
@ -2215,7 +2305,7 @@ Script.createPubkey = function createPubkey(key) {
Script.createPubkeyhash = function createPubkeyhash(hash) { Script.createPubkeyhash = function createPubkeyhash(hash) {
assert(hash.length === 20); assert(hash.length === 20);
return new Script([ return Script.fromArray([
opcodes.OP_DUP, opcodes.OP_DUP,
opcodes.OP_HASH160, opcodes.OP_HASH160,
hash, hash,
@ -2250,7 +2340,7 @@ Script.createMultisig = function createMultisig(keys, m, n) {
code.push(n + 0x50); code.push(n + 0x50);
code.push(opcodes.OP_CHECKMULTISIG); code.push(opcodes.OP_CHECKMULTISIG);
return new Script(code); return Script.fromArray(code);
}; };
/** /**
@ -2261,7 +2351,7 @@ Script.createMultisig = function createMultisig(keys, m, n) {
Script.createScripthash = function createScripthash(hash) { Script.createScripthash = function createScripthash(hash) {
assert(hash.length === 20); assert(hash.length === 20);
return new Script([ return Script.fromArray([
opcodes.OP_HASH160, opcodes.OP_HASH160,
hash, hash,
opcodes.OP_EQUAL opcodes.OP_EQUAL
@ -2277,7 +2367,7 @@ Script.createScripthash = function createScripthash(hash) {
Script.createNulldata = function createNulldata(flags) { Script.createNulldata = function createNulldata(flags) {
assert(Buffer.isBuffer(flags)); assert(Buffer.isBuffer(flags));
assert(flags.length <= constants.script.MAX_OP_RETURN, 'Nulldata too large.'); assert(flags.length <= constants.script.MAX_OP_RETURN, 'Nulldata too large.');
return new Script([ return Script.fromArray([
opcodes.OP_RETURN, opcodes.OP_RETURN,
flags flags
]); ]);
@ -2293,13 +2383,13 @@ Script.createNulldata = function createNulldata(flags) {
Script.createWitnessProgram = function createWitnessProgram(version, data) { Script.createWitnessProgram = function createWitnessProgram(version, data) {
assert(typeof version === 'number' && version >= 0 && version <= 16); assert(typeof version === 'number' && version >= 0 && version <= 16);
assert(data.length >= 2 && data.length <= 32); assert(data.length >= 2 && data.length <= 32);
return new Script([version === 0 ? 0 : version + 0x50, data]); return Script.fromArray([version === 0 ? 0 : version + 0x50, data]);
}; };
/** /**
* Create a witness block commitment. * Create a witness block commitment.
* @param {Buffer} hash * @param {Buffer} hash
* @params {String|Buffer} flags * @param {String|Buffer} flags
* @returns {Script} * @returns {Script}
*/ */
@ -2316,7 +2406,7 @@ Script.createCommitment = function createCommitment(hash, flags) {
p.writeU32BE(0xaa21a9ed); p.writeU32BE(0xaa21a9ed);
p.writeHash(hash); p.writeHash(hash);
return new Script([ return Script.fromArray([
opcodes.OP_RETURN, opcodes.OP_RETURN,
p.render(), p.render(),
flags flags
@ -2390,8 +2480,8 @@ Script.prototype.isStandard = function isStandard() {
var m, n; var m, n;
if (type === 'multisig') { if (type === 'multisig') {
m = this.getSmall(0); m = Script.getSmall(this.raw[0]);
n = this.getSmall(-2); n = Script.getSmall(this.raw[this.raw.length - 2]);
if (n < 1 || n > 3) if (n < 1 || n > 3)
return false; return false;
@ -2947,7 +3037,7 @@ Script.getCoinbaseHeight = function getCoinbaseHeight(raw) {
height = Script.getSmall(raw[0]); height = Script.getSmall(raw[0]);
if (height != null) if (height !== -1)
return height; return height;
if (raw[0] <= 0x06 && raw.length >= 1 + raw[0]) if (raw[0] <= 0x06 && raw.length >= 1 + raw[0])
@ -3013,14 +3103,31 @@ Script.prototype.test = function test(filter) {
return false; return false;
}; };
/**
* Unshift an item onto the `code` array.
* @param {Number|String|BN|Buffer} data
* @returns {Number} Length.
*/
Script.prototype.unshift = function unshift(data) { Script.prototype.unshift = function unshift(data) {
return this.code.unshift(Opcode.from(data)); return this.code.unshift(Opcode.from(data));
}; };
/**
* Push an item onto the `code` array.
* @param {Number|String|BN|Buffer} data
* @returns {Number} Length.
*/
Script.prototype.push = function push(data) { Script.prototype.push = function push(data) {
return this.code.push(Opcode.from(data)); return this.code.push(Opcode.from(data));
}; };
/**
* Shift an item off of the `code` array.
* @returns {Buffer|Number}
*/
Script.prototype.shift = function shift() { Script.prototype.shift = function shift() {
var op = this.code.shift(); var op = this.code.shift();
if (!op) if (!op)
@ -3028,6 +3135,11 @@ Script.prototype.shift = function shift() {
return op.data || op.value; return op.data || op.value;
}; };
/**
* Pop an item off of the `code` array.
* @returns {Buffer|Number}
*/
Script.prototype.pop = function push(data) { Script.prototype.pop = function push(data) {
var op = this.code.pop(); var op = this.code.pop();
if (!op) if (!op)
@ -3035,6 +3147,12 @@ Script.prototype.pop = function push(data) {
return op.data || op.value; return op.data || op.value;
}; };
/**
* Remove an item from the `code` array.
* @param {Number} index
* @returns {Buffer|Number}
*/
Script.prototype.remove = function remove(i) { Script.prototype.remove = function remove(i) {
var op = this.code.splice(i, 1)[0]; var op = this.code.splice(i, 1)[0];
if (!op) if (!op)
@ -3042,11 +3160,23 @@ Script.prototype.remove = function remove(i) {
return op.data || op.value; return op.data || op.value;
}; };
/**
* Insert an item into the `code` array.
* @param {Number} index
* @param {Number|String|BN|Buffer} data
*/
Script.prototype.insert = function insert(i, data) { Script.prototype.insert = function insert(i, data) {
assert(i <= this.code.length, 'Index out of bounds.'); assert(i <= this.code.length, 'Index out of bounds.');
this.code.splice(i, 0, Opcode.from(data))[0]; this.code.splice(i, 0, Opcode.from(data))[0];
}; };
/**
* Get an item from the `code` array.
* @param {Number} index
* @returns {Buffer|Number}
*/
Script.prototype.get = function get(i) { Script.prototype.get = function get(i) {
var op = this.code[i]; var op = this.code[i];
if (!op) if (!op)
@ -3054,13 +3184,44 @@ Script.prototype.get = function get(i) {
return op.data || op.value; return op.data || op.value;
}; };
Script.prototype.getNumber = function getNumber(i) { /**
* Get a small integer from an opcode (OP_0-OP_16).
* @param {Number} index
* @returns {Number}
*/
Script.prototype.getSmall = function getSmall(i) {
var op = this.code[i]; var op = this.code[i];
if (!op)
return -1;
return Script.getSmall(op.value);
};
/**
* Get a number from the `code` array (5-byte limit).
* @params {Number} index
* @returns {BN}
*/
Script.prototype.getNumber = function getNumber(i) {
var small = this.getSmall(i);
var op = this.code[i];
if (small !== -1)
return new bn(small);
if (!op || !op.data || op.data.length > 5) if (!op || !op.data || op.data.length > 5)
return; return;
return Script.num(op.data, constants.flags.VERIFY_NONE, 5); return Script.num(op.data, constants.flags.VERIFY_NONE, 5);
}; };
/**
* Get a string from the `code` array (utf8).
* @params {Number} index
* @returns {String}
*/
Script.prototype.getString = function getString(i) { Script.prototype.getString = function getString(i) {
var op = this.code[i]; var op = this.code[i];
if (!op || !op.data) if (!op || !op.data)
@ -3068,6 +3229,12 @@ Script.prototype.getString = function getString(i) {
return op.data.toString('utf8'); return op.data.toString('utf8');
}; };
/**
* Set an item in the `code` array.
* @param {Number} index
* @param {Buffer|Number|String|BN} data
*/
Script.prototype.set = function set(i, data) { Script.prototype.set = function set(i, data) {
assert(i <= this.code.length, 'Index out of bounds.'); assert(i <= this.code.length, 'Index out of bounds.');
this.code[i] = Opcode.from(data); this.code[i] = Opcode.from(data);
@ -3123,20 +3290,6 @@ Script.isDummy = function isDummy(data) {
return Buffer.isBuffer(data) && data.length === 0; return Buffer.isBuffer(data) && data.length === 0;
}; };
/**
* Test whether the data element is a null dummy or an OP_0.
* @private
* @param {Buffer?} data
* @returns {Boolean}
*/
Script.isZero = function isZero(op) {
if (op === opcodes.OP_0)
return true;
return Script.isDummy(op);
};
/** /**
* Test whether the data element is a valid key if VERIFY_STRICTENC is enabled. * Test whether the data element is a valid key if VERIFY_STRICTENC is enabled.
* @param {Buffer} key * @param {Buffer} key
@ -3698,19 +3851,6 @@ Script.fromString = function fromString(code) {
return new Script(p.render()); return new Script(p.render());
}; };
/**
* Get a small integer from an opcode (OP_0-OP_16).
* @param {Number} index
* @returns {Number}
*/
Script.prototype.getSmall = function getSmall(i) {
if (i < 0)
i = this.code.length + i;
return Script.getSmall(this.code[i].value);
};
/** /**
* Get a small integer from an opcode (OP_0-OP_16). * Get a small integer from an opcode (OP_0-OP_16).
* @param {Number} index * @param {Number} index
@ -3719,7 +3859,7 @@ Script.prototype.getSmall = function getSmall(i) {
Script.getSmall = function getSmall(op) { Script.getSmall = function getSmall(op) {
if (typeof op !== 'number') if (typeof op !== 'number')
return null; return -1;
if (op === opcodes.OP_0) if (op === opcodes.OP_0)
return 0; return 0;
@ -4339,7 +4479,7 @@ Opcode.fromString = function fromString(data, enc) {
/** /**
* Instantiate a pushdata opcode from anything. * Instantiate a pushdata opcode from anything.
* @params {String|Buffer|Number|BN|Opcode} data * @param {String|Buffer|Number|BN|Opcode} data
* @returns {Opcode} * @returns {Opcode}
*/ */