better script conformance.

This commit is contained in:
Christopher Jeffrey 2016-01-14 15:58:22 -08:00
parent 282639b5b1
commit 0ba31eeb59
5 changed files with 414 additions and 353 deletions

View File

@ -184,62 +184,43 @@ Input.prototype.__defineGetter__('scriptaddr', function() {
// Schema and defaults for data object:
// {
// type: null,
// subtype: null,
// type: String,
// subtype: String,
// side: 'input',
// signatures: [],
// keys: [],
// hashes: [],
// addresses: [],
// redeem: null,
// scripthash: null,
// scriptaddress: null,
// m: 0,
// n: 0,
// height: -1,
// flags: null,
// text: null,
// lock: 0,
// value: new bn(0),
// script: s,
// seq: input.seq,
// prev: null,
// index: null,
// _script: null,
// none: false
// signatures: Array,
// keys: Array,
// hashes: Array,
// addresses: Array,
// redeem: Array,
// scripthash: Array,
// scriptaddress: String,
// m: Number,
// n: Number,
// height: Number,
// flags: Array,
// text: String,
// lock: Number,
// value: bn,
// script: Array,
// seq: Number,
// prev: String,
// index: Number,
// none: Boolean
// }
Input.getData = function getData(input) {
var s, sub, def, signature, key, hash, address, redeem, data, output, val;
if (Array.isArray(input)) {
input = {
out: {
tx: null,
hash: utils.toHex(constants.oneHash),
index: 0
},
script: input,
seq: 0xffffffff
};
}
var def, data, output;
if (!input || !input.script)
return;
s = input.script;
sub = bcoin.script.subscript(input.script);
def = {
side: 'input',
value: new bn(0),
script: s,
script: input.script,
seq: input.seq
};
if (bcoin.script.lockTime(sub))
sub = sub.slice(3);
if (input.out) {
def.prev = input.out.hash;
def.index = input.out.index;
@ -247,100 +228,22 @@ Input.getData = function getData(input) {
if (input.out && +input.out.hash === 0) {
data = bcoin.script.coinbase(input.script);
return utils.merge(def, {
return utils.merge(def, data, {
type: 'coinbase',
height: data.height != null ? data.height : -1,
flags: data.flags,
text: data.text,
none: true
});
}
if (input.out && input.out.tx) {
output = input.out.tx.outputs[input.out.index];
data = bcoin.output.getData(output);
if (data.type === 'pubkey' ) {
data.signatures = [sub[0]];
} else if (data.type === 'pubkeyhash') {
data.signatures = [sub[0]];
data.keys = [sub[1]];
} else if (data.type === 'multisig') {
data.signatures = sub.slice(1);
} else if (data.type === 'scripthash') {
// We work backwards here: scripthash is one of the few cases
// where we get more data from the input than the output.
val = bcoin.input.getData({
out: { hash: input.out.hash, index: input.out.index },
script: input.script,
seq: input.seq
});
val.lock = data.lock;
val.value = data.value;
val.script = data.script;
data = val;
if (output) {
data = bcoin.script.getData(input.script, output.script);
data.value = output.value;
return utils.merge(def, data);
}
return utils.merge(data, {
seq: def.seq,
prev: def.prev,
index: def.index,
_script: def.script
});
}
if (bcoin.script.isPubkeyInput(s)) {
return utils.merge(def, {
type: 'pubkey',
signatures: [sub[0]],
none: true
});
}
if (bcoin.script.isPubkeyhashInput(s)) {
key = sub[1];
hash = utils.ripesha(key);
address = bcoin.wallet.hash2addr(hash);
return utils.merge(def, {
type: 'pubkeyhash',
signatures: [sub[0]],
keys: [key],
hashes: [hash],
addresses: [address]
});
}
if (bcoin.script.isMultisigInput(s)) {
signature = sub.slice(1);
return utils.merge(def, {
type: 'multisig',
signatures: signature,
m: signature.length,
none: true
});
}
if (bcoin.script.isScripthashInput(s)) {
redeem = sub[sub.length - 1];
hash = utils.ripesha(redeem);
address = bcoin.wallet.hash2addr(hash, 'scripthash');
redeem = bcoin.script.decode(redeem);
val = bcoin.input.getData(sub.slice(0, -1));
data = bcoin.output.getData(redeem);
return utils.merge(val, data, {
type: 'scripthash',
subtype: data.type,
side: 'input',
redeem: redeem,
scripthash: hash,
scriptaddress: address,
script: s,
seq: input.seq
});
}
return utils.merge(def, {
type: 'unknown',
none: true
});
return utils.merge(def, bcoin.script.getInputData(input.script));
};
Input.prototype.inspect = function inspect() {
@ -356,6 +259,7 @@ Input.prototype.inspect = function inspect() {
type: this.type,
subtype: this.data.subtype,
address: this.address,
addresses: this.addresses,
signatures: this.signatures.map(utils.toHex),
keys: this.keys.map(utils.toHex),
text: this.text,

View File

@ -105,10 +105,7 @@ Output.prototype.__defineGetter__('n', function() {
});
Output.prototype.__defineGetter__('lock', function() {
var lock = bcoin.script.lockTime(this.script);
if (!lock)
return 0;
return lock.toNumber();
return bcoin.script.lockTime(this.script);
});
Output.prototype.__defineGetter__('text', function() {
@ -154,123 +151,43 @@ Output.prototype.__defineGetter__('scriptaddr', function() {
// Schema and defaults for data object:
// {
// type: null,
// subtype: null,
// type: String,
// subtype: String,
// side: 'output',
// signatures: [],
// keys: [],
// hashes: [],
// addresses: [],
// redeem: null,
// scripthash: null,
// scriptaddress: null,
// m: 0,
// n: 0,
// height: -1,
// flags: null,
// text: null,
// lock: lock ? lock.toNumber() : 0,
// value: output.value,
// script: s,
// seq: null,
// prev: null,
// index: null,
// _script: null,
// none: false
// signatures: Array,
// keys: Array,
// hashes: Array,
// addresses: Array,
// redeem: Array,
// scripthash: Array,
// scriptaddress: String,
// m: Number,
// n: Number,
// height: Number,
// flags: Array,
// text: String,
// lock: Number,
// value: bn,
// script: Array,
// seq: Number,
// prev: String,
// index: Number,
// none: Boolean
// }
Output.getData = function getData(output) {
var s, sub, lock, def, key, hash, address, keys, data;
if (Array.isArray(output)) {
output = {
script: output,
value: new bn(0)
};
}
var def;
if (!output || !output.script)
return;
s = output.script;
sub = bcoin.script.subscript(output.script);
lock = bcoin.script.lockTime(s);
def = {
side: 'output',
value: output.value,
script: s
script: output.script
};
if (lock) {
sub = sub.slice(3);
def.lock = lock.toNumber();
}
if (bcoin.script.isPubkey(s)) {
key = sub[0];
hash = utils.ripesha(key);
address = bcoin.wallet.hash2addr(hash);
return utils.merge(def, {
type: 'pubkey',
keys: [key],
hashes: [hash],
addresses: [address]
});
}
if (bcoin.script.isPubkeyhash(s)) {
hash = sub[2];
address = bcoin.wallet.hash2addr(hash);
return utils.merge(def, {
type: 'pubkeyhash',
hashes: [hash],
addresses: [address]
});
}
if (bcoin.script.isMultisig(s)) {
keys = sub.slice(1, -2);
hash = keys.map(function(key) {
return utils.ripesha(key);
});
address = hash.map(function(hash) {
return bcoin.wallet.hash2addr(hash);
});
return utils.merge(def, {
type: 'multisig',
keys: keys,
hashes: hash,
addresses: address,
m: new bn(sub[0]).toNumber(),
n: new bn(sub[sub.length - 2]).toNumber()
});
}
if (bcoin.script.isScripthash(s)) {
hash = sub[1];
address = bcoin.wallet.hash2addr(hash, 'scripthash');
return utils.merge(def, {
type: 'scripthash',
scripthash: hash,
scriptaddress: address
});
}
if (bcoin.script.isNulldata(s)) {
data = bcoin.script.nulldata(s);
return utils.merge(def, {
type: 'nulldata',
flags: data,
text: utils.array2utf8(data),
none: true
});
}
return utils.merge(def, {
type: 'unknown',
none: true
});
return utils.merge(def, bcoin.script.getOutputData(output.script));
};
Output.prototype.inspect = function inspect() {

View File

@ -168,7 +168,7 @@ script.verify = function verify(input, output, tx, i, flags) {
res = script.execute(output, stack, tx, i, flags);
// Verify the script did not fail as well as the stack values
if (!res || stack.length === 0 || new bn(stack.pop()).cmpn(0) === 0)
if (!res || stack.length === 0 || script.num(stack.pop()).cmpn(0) === 0)
return false;
// If the script is P2SH, execute the real output script
@ -196,7 +196,7 @@ script.verify = function verify(input, output, tx, i, flags) {
res = script.execute(redeem, stack, tx, i, flags);
// Verify the script did not fail as well as the stack values
if (!res || stack.length === 0 || new bn(stack.pop()).cmpn(0) === 0)
if (!res || stack.length === 0 || script.num(stack.pop()).cmpn(0) === 0)
return false;
}
@ -351,7 +351,7 @@ script.execute = function execute(s, stack, tx, index, flags, recurse) {
if (stack.length < 1)
return false;
v = stack.pop();
val = new bn(v).cmpn(0) !== 0;
val = script.num(v).cmpn(0) !== 0;
if (o === 'notif')
val = !val;
if_ = pc;
@ -391,7 +391,7 @@ script.execute = function execute(s, stack, tx, index, flags, recurse) {
case 'verify': {
if (stack.length === 0)
return false;
if (new bn(stack.pop()).cmpn(0) === 0)
if (script.num(stack.pop()).cmpn(0) === 0)
return false;
break;
}
@ -413,12 +413,12 @@ script.execute = function execute(s, stack, tx, index, flags, recurse) {
case 'ifdup': {
if (stack.length === 0)
return false;
if (new bn(stack[stack.length - 1]).cmpn(0) !== 0)
stack.push(new bn(stack[stack.length - 1]).toArray());
if (script.num(stack[stack.length - 1]).cmpn(0) !== 0)
stack.push(script.numArray(stack[stack.length - 1]));
break;
}
case 'depth': {
stack.push(new bn(stack.length).toArray());
stack.push(script.numArray(stack.length));
break;
}
case 'drop': {
@ -452,7 +452,7 @@ script.execute = function execute(s, stack, tx, index, flags, recurse) {
v = stack.pop();
if (v.length > 6)
return false;
n = new bn(v).toNumber();
n = script.num(v, true);
if (n < 0 || n >= stack.length)
return false;
v = stack[-n - 1];
@ -551,7 +551,7 @@ script.execute = function execute(s, stack, tx, index, flags, recurse) {
case 'size': {
if (stack.length < 1)
return false;
stack.push(new bn(stack[stack.length - 1].length || 0).toArray());
stack.push(script.numArray(stack[stack.length - 1].length || 0));
break;
}
case 'add1':
@ -562,7 +562,7 @@ script.execute = function execute(s, stack, tx, index, flags, recurse) {
case 'noteq0': {
if (stack.length < 1)
return false;
n = new bn(stack.pop());
n = script.num(stack.pop());
switch (o) {
case 'add1':
n.iadd(1);
@ -587,8 +587,8 @@ script.execute = function execute(s, stack, tx, index, flags, recurse) {
return false;
}
if (typeof n === 'boolean')
n = new bn(+n);
stack.push(n.toArray());
n = script.num(+n);
stack.push(script.numArray(n));
break;
}
case 'add':
@ -620,9 +620,9 @@ script.execute = function execute(s, stack, tx, index, flags, recurse) {
case 'max':
if (stack.length < 2)
return false;
n2 = new bn(stack.pop());
n1 = new bn(stack.pop());
n = new bn(0);
n2 = script.num(stack.pop());
n1 = script.num(stack.pop());
n = script.num(0);
switch (o) {
case 'add':
n = n1.add(n2);
@ -667,21 +667,21 @@ script.execute = function execute(s, stack, tx, index, flags, recurse) {
return false;
}
if (typeof n === 'boolean')
n = new bn(+n);
n = script.num(+n);
res = n.cmpn(0) !== 0;
if (o === 'numeqverify') {
if (!res)
return false;
} else {
stack.push(n.toArray());
stack.push(script.numArray(n));
}
break;
case 'within':
if (stack.length < 3)
return false;
n3 = new bn(stack.pop());
n2 = new bn(stack.pop());
n1 = new bn(stack.pop());
n3 = script.num(stack.pop());
n2 = script.num(stack.pop());
n1 = script.num(stack.pop());
val = n2.cmp(n1) <= 0 && n1.cmp(n3) < 0;
stack.push(val.cmpn(0) !== 0 ? [ 1 ] : []);
break;
@ -778,16 +778,11 @@ script.execute = function execute(s, stack, tx, index, flags, recurse) {
if (!tx || stack.length < 3)
return false;
n = stack.pop();
n = script.num(stack.pop(), true);
if (!Array.isArray(n))
if (!(n >= 1 && n <= 15))
return false;
if (n.length !== 1 || !(n[0] >= 1 && n[0] <= 15))
return false;
n = n[0];
if (stack.length < n + 1)
return false;
@ -800,16 +795,11 @@ script.execute = function execute(s, stack, tx, index, flags, recurse) {
keys.push(key);
}
m = stack.pop();
m = script.num(stack.pop(), true);
if (!Array.isArray(m))
if (!(m >= 1 && m <= n))
return false;
if (m.length !== 1 || !(m[0] >= 1 && m[0] <= n))
return false;
m = m[0];
if (stack.length < m + 1)
return false;
@ -878,7 +868,7 @@ script.execute = function execute(s, stack, tx, index, flags, recurse) {
if (lock.length > 6)
return false;
lock = new bn(lock).toNumber();
lock = script.num(lock, true);
if (lock < 0)
return false;
@ -948,7 +938,40 @@ script.num = function num(value, useNum) {
assert(utils.isBuffer(value));
value = new bn(value, 'le');
if (script.requireminimal && value.length > 0) {
// If the low bits on the last byte are unset,
// fail if The value's second to last byte does
// not have the high bit set. A number can't
// justify having the last byte's low bits unset
// unless they ran out of space for the sign bit
// in the second to last bit. This also takes
// care of negative We also fail on [0] to avoid
// negative zero (also avoids positive zero).
if (!(value[value.length - 1] & 0x7f)) {
if (value.length === 1 || !(value[value.length - 2] & 0x80)) {
// We should technically fail here by
// throwing and catching, but return zero for now.
return useNum ? 0 : new bn(0, 'le');
}
}
}
// Optimize by avoiding big numbers
if (useNum && value.length <= 1)
return value.length === 0 ? 0 : value[0];
// If we are signed, do (~num + 1) to get
// the positive counterpart and set bn's
// negative flag.
if (value[value.length - 1] & 0x80) {
if (utils.isNegZero(value, 'le')) {
value = new bn(0, 'le');
} else {
value = new bn(value, 'le').notn(64).addn(1).neg();
}
} else {
value = new bn(value, 'le');
}
if (useNum) {
try {
@ -961,6 +984,55 @@ script.num = function num(value, useNum) {
return value;
};
script.numArray = function(value) {
if (Array.isArray(value))
return value.slice();
if (utils.isFinite(value))
value = new bn(value, 'le');
assert(value instanceof bn);
// Convert the number to the
// negative byte representation.
if (value.isNeg()) {
if (value.cmpn(0) === 0)
value = new bn(0);
else
value = value.neg().notn(64).addn(1);
}
if (value.cmpn(0) === 0)
return [];
return value.toArray('le');
};
script.checkPush = function checkPush(op, value) {
if (!script.requireminimal)
return true;
if (value.length === 1 && value[0] === 0)
return op === constants.opcodes['0'];
if (value.length === 1 && value[0] >= 1 && value[0] <= 16)
return op >= constants.opcodes['1'] && op <= constants.opcodes['16'];
if (value.length === 1 && value[0] === -1)
return op === constants.opcodes['-1'];
if (value.length <= 75)
return op === value.length;
if (value.length <= 255)
return op === constants.opcodes.pushdata1;
if (value.length <= 65535)
return op === constants.opcodes.pushdata2;
return true;
};
script.redeem = function redeem(keys, m, n) {
if (keys.length !== n)
throw new Error(n + ' keys are required to generate redeem script');
@ -989,17 +1061,18 @@ script.standard = function standard(s) {
};
script.isStandard = function isStandard(s) {
var type = script.standard(s);
var m, n;
if (type === 'multisig') {
m = new bn(s[0]).toNumber();
n = new bn(s[s.length - 2]).toNumber();
if (script.isMultisig(s)) {
m = s[0];
n = s[s.length - 2];
if (n < 1 || n > 3)
return false;
if (m < 1 || m > n)
return false;
} else if (type === 'nulldata') {
} else if (script.isNulldata(s)) {
if (script.size(s) > constants.script.maxOpReturnBytes)
return false;
}
@ -1010,53 +1083,183 @@ script.isStandard = function isStandard(s) {
script.size = function size(s) {
if (s._raw)
return s._raw.length;
return bcoin.script.encode(s).length;
return script.encode(s).length;
};
script.isEncoded = function isEncoded(s) {
return utils.isBytes(s);
};
script.normalize = function normalize(s) {
if (script.isEncoded(s))
s = script.decode(s);
s = script.subscript(s);
if (script.lockTime(s))
s = s.slice(3);
return s;
script.isLockTime = function isLockTime(s, check) {
return s.length > 4
&& Array.isArray(s[0])
&& s[1] === 'checklocktimeverify'
&& s[2] === 'drop'
&& s[3] === 'codesep';
};
script.lockTime = function lockTime(s) {
var lock = s[0];
var res = s.length > 3
&& Array.isArray(s[0])
&& s[1] === 'checklocktimeverify'
&& s[2] === 'drop';
if (!res)
if (!script.isLockTime(s))
return false;
// Number can only store 6 & 5/8 bytes
if (lock.length > 6)
lock = lock.slice(0, 6);
return new bn(lock);
return script.num(s[0], true);
};
script.spendable = function spendable(s, lockTime) {
if (!script.standard(s))
return false;
var lock = script.lockTime(s);
if (lock && lock.toNumber() > lockTime)
if (script.lockTime(s) > lockTime)
return false;
return true;
};
script.getData = function getData(s, prev) {
var output;
if (prev && !script.isScripthash(prev)) {
output = script.getOutputData(prev);
output.side = 'input';
if (output.type === 'pubkey') {
output.signatures = [s[0]];
} else if (output.type === 'pubkeyhash') {
output.signatures = [s[0]];
output.keys = [s[1]];
} else if (output.type === 'multisig') {
output.signatures = s.slice(1);
}
return output;
}
return script.getInputData(s);
};
script.getInputData = function getInputData(s) {
var sig, key, hash, raw, redeem, lock, hash, address, input, output;
if (script.isPubkeyInput(s)) {
key = s[0];
return {
type: 'pubkey',
side: 'input',
signatures: [sig],
none: true
};
}
if (script.isPubkeyhashInput(s)) {
sig = s[0];
key = s[1];
hash = bcoin.wallet.key2hash(key);
address = bcoin.wallet.hash2addr(hash, 'pubkeyhash');
return {
type: 'pubkeyhash',
side: 'input',
signatures: [sig],
keys: [key],
hashes: [hash],
addresses: [address]
};
}
if (script.isMultisigInput(s)) {
sig = s.slice(1);
return {
type: 'multisig',
side: 'input',
signatures: sig,
m: sig.length,
none: true
};
}
if (script.isScripthashInput(s)) {
raw = s[s.length - 1];
redeem = script.decode(raw);
lock = script.lockTime(redeem);
hash = bcoin.wallet.key2hash(raw);
address = bcoin.wallet.hash2addr(hash, 'scripthash');
input = script.getInputData(s.slice(0, -1));
delete input.none;
output = script.getOutputData(script.subscript(redeem));
return utils.merge(input, output, {
type: 'scripthash',
side: 'input',
subtype: output.type,
redeem: redeem,
scripthash: hash,
scriptaddress: address,
lock: lock
});
}
return {
type: 'unknown',
none: true
};
};
script.getOutputData = function getOutputData(s) {
var key, hash, address;
if (script.isPubkey(s)) {
key = s[0];
hash = bcoin.wallet.key2hash(key);
address = bcoin.wallet.hash2addr(hash, 'pubkey');
return {
type: 'pubkey',
side: 'output',
keys: [key],
hashes: [hash],
addresses: [address]
};
}
if (script.isPubkeyhash(s)) {
hash = s[2];
return {
type: 'pubkeyhash',
side: 'output',
hashes: [hash],
addresses: [bcoin.wallet.hash2addr(hash, 'pubkeyhash')]
};
}
if (script.isMultisig(s)) {
key = s.slice(1, -2);
hash = key.map(function(key) {
return bcoin.wallet.key2hash(key);
});
address = hash.map(function(hash) {
return bcoin.wallet.hash2addr(hash, 'multisig');
});
return {
type: 'multisig',
side: 'output',
keys: key,
hashes: hash,
addresses: address,
m: s[0],
n: s[s.length - 2]
};
}
if (script.isScripthash(s)) {
hash = s[1];
return {
type: 'scripthash',
side: 'output',
scripthash: hash,
scriptaddress: bcoin.wallet.hash2addr(hash, 'scripthash')
};
}
return {
type: 'unknown',
none: true
};
};
script.isPubkey = function isPubkey(s, key) {
var res;
@ -1068,10 +1271,12 @@ script.isPubkey = function isPubkey(s, key) {
if (!res)
return false;
if (key)
return utils.isEqual(s[0], key);
if (key) {
if (!utils.isEqual(s[0], key))
return false;
}
return true;
return s[0];
};
script.isPubkeyhash = function isPubkeyhash(s, hash) {
@ -1089,10 +1294,12 @@ script.isPubkeyhash = function isPubkeyhash(s, hash) {
if (!res)
return false;
if (hash)
return utils.isEqual(s[2], hash);
if (hash) {
if (!utils.isEqual(s[2], hash))
return false;
}
return true;
return s[2];
};
script.isMultisig = function isMultisig(s, keys) {
@ -1107,6 +1314,15 @@ script.isMultisig = function isMultisig(s, keys) {
n = s[s.length - 2];
if (Array.isArray(n)) {
if (n.length !== 0)
return false;
n = 0;
}
if (typeof n !== 'number')
return false;
// Bitcoind technically doesn't check for the
// 15 limit here. It just counts the sigops
// later.
@ -1115,6 +1331,15 @@ script.isMultisig = function isMultisig(s, keys) {
m = s[0];
if (Array.isArray(m)) {
if (m.length !== 0)
return false;
m = 0;
}
if (typeof m !== 'number')
return false;
if (!(m >= 1 && m <= n))
return false;
@ -1126,21 +1351,27 @@ script.isMultisig = function isMultisig(s, keys) {
return false;
}
if (!keys)
return true;
if (keys) {
keys = utils.sortKeys(keys);
keys = utils.sortKeys(keys);
for (i = 1; i < n + 1; i++) {
for (j = 0; j < keys.length; j++) {
if (utils.isEqual(s[i], keys[j])) {
total++;
break;
for (i = 1; i < n + 1; i++) {
for (j = 0; j < keys.length; j++) {
if (utils.isEqual(s[i], keys[j])) {
total++;
break;
}
}
}
if (total !== n)
return false;
}
return total === n;
return {
keys: s.slice(1, n + 1),
m: m,
n: n
};
};
script.isScripthash = function isScripthash(s, hash) {
@ -1156,23 +1387,25 @@ script.isScripthash = function isScripthash(s, hash) {
if (!res)
return false;
if (hash)
return utils.isEqual(s[1], hash);
if (hash) {
if (!utils.isEqual(s[1], hash))
return false;
}
return true;
return s[1];
};
script.isNulldata = function isNulldata(s) {
var res;
if (s.length !== 2)
return false;
return s[0] === 'ret'
res = s[0] === 'ret'
&& Array.isArray(s[1])
&& s[1].length <= constants.script.maxOpReturn;
};
script.nulldata = function nulldata(s) {
if (!script.isNulldata(s))
if (!res)
return false;
return s[1];
@ -1187,7 +1420,7 @@ script.standardInput = function standardInput(s) {
};
script.isPubkeyInput = function isPubkeyInput(s, key, tx, i) {
if (s.length !== 1 || !Array.isArray(s[0]))
if (s.length !== 1)
return false;
if (!script.isSignature(s[0]))
@ -1199,14 +1432,15 @@ script.isPubkeyInput = function isPubkeyInput(s, key, tx, i) {
if (key) {
assert(tx);
assert(i != null);
return script.verify(s, [key, 'checksig'], tx, i);
if (!script.verify(s, [key, 'checksig'], tx, i))
return false;
}
return true;
return s[0];
};
script.isPubkeyhashInput = function isPubkeyhashInput(s, key) {
if (s.length !== 2 || !Array.isArray(s[0]) || !Array.isArray(s[1]))
if (s.length !== 2)
return false;
if (!script.isSignature(s[0]))
@ -1215,10 +1449,12 @@ script.isPubkeyhashInput = function isPubkeyhashInput(s, key) {
if (!script.isKey(s[1]))
return false;
if (key)
return utils.isEqual(s[1], key);
if (key) {
if (!utils.isEqual(s[1], key))
return false;
}
return true;
return s[1];
};
script.isMultisigInput = function isMultisigInput(s, keys, tx, i) {
@ -1248,7 +1484,8 @@ script.isMultisigInput = function isMultisigInput(s, keys, tx, i) {
assert(tx);
assert(i != null);
o = script.redeem(keys, s.length - 1, keys.length);
return script.verify(s, o, tx, i);
if (!script.verify(s, o, tx, i))
return false;
}
// We also also try to recover the keys from the signatures.
@ -1261,7 +1498,10 @@ script.isMultisigInput = function isMultisigInput(s, keys, tx, i) {
// recovered.push(key);
// }
return true;
return {
signatures: s.slice(1),
m: s.length - 1
};
};
script.isScripthashInput = function isScripthashInput(s, data) {
@ -1297,12 +1537,16 @@ script.isScripthashInput = function isScripthashInput(s, data) {
// Check data against last array in case
// a raw redeem script was passed in.
if (data && utils.isEqual(data, raw))
return true;
return raw;
// Test against all other script types
return script.isPubkey(redeem, data)
|| script.isPubkeyhash(redeem, data)
|| script.isMultisig(redeem, data);
if (!script.isPubkey(redeem, data)
&& !script.isPubkeyhash(redeem, data)
&& !script.isMultisig(redeem, data)) {
return false;
}
return raw;
};
script.coinbaseBits = function coinbaseBits(s, block) {
@ -1319,7 +1563,7 @@ script.coinbaseBits = function coinbaseBits(s, block) {
if (s[0].length > 6)
return { type: 'value', value: s[0] };
value = new bn(s[0].slice().reverse()).toNumber();
value = script.num(s[0], true);
// Test for bits and ts
if (block && block.version < 2) {
@ -1366,7 +1610,7 @@ script.coinbase = function coinbase(s, block) {
data = script.coinbaseBits(s, block);
if (Array.isArray(s[1]))
extraNonce = new bn(s[1]);
extraNonce = script.num(s[1]);
flags = s.slice(2);
@ -1377,6 +1621,9 @@ script.coinbase = function coinbase(s, block) {
flags.map(utils.array2utf8).join('')
.replace(/[\u0000-\u0019\u007f-\u00ff]/g, '');
if (coinbase.height == null)
coinbase.height = -1;
return coinbase;
};
@ -1684,7 +1931,7 @@ script.args = function args(s) {
keys = s.slice(1, -2);
if (!pub)
return -1;
m = new bn(s[0]).toNumber();
m = s[0];
if (keys.length < 1 || m < 1)
return -1;
return m + 1;

View File

@ -197,8 +197,6 @@ TX.prototype.scriptInput = function scriptInput(index, pub, redeem) {
// Grab `n` value (number of keys).
n = s[s.length - 2];
if (Array.isArray(n))
n = n[0];
// Fill script with `n` signature slots.
for (i = 0; i < n; i++)
@ -315,13 +313,9 @@ TX.prototype.signInput = function signInput(index, key, type) {
// Grab `m` value (number of sigs required).
m = s[0];
if (Array.isArray(m))
m = m[0];
// Grab `n` value (number of keys).
n = s[s.length - 2];
if (Array.isArray(n))
n = n[0];
// Something is very wrong here. Abort.
if (len - 1 > n)
@ -565,9 +559,10 @@ TX.prototype.scriptOutput = function scriptOutput(index, options) {
if (options.scripthash) {
if (options.lock != null) {
script = [
new bn(options.lock).toArray(),
bcoin.script.numArray(options.lock),
'checklocktimeverify',
'drop'
'drop',
'codesep'
].concat(script);
}
hash = utils.ripesha(bcoin.script.encode(script));
@ -757,8 +752,6 @@ TX.prototype.maxSize = function maxSize() {
// Bare Multisig
// Get the previous m value:
m = s[0];
if (Array.isArray(m))
m = m[0];
// OP_0
size += 1;
// OP_PUSHDATA0 [signature] ...

View File

@ -704,7 +704,7 @@ utils.isBytes = function isBytes(data) {
return false;
for (i = 0; i < data.length; i++) {
if (typeof data[i] !== 'number')
if (typeof data[i] !== 'number' || data[i] < 0x00 || data[i] > 0xff)
return false;
}