better script conformance.
This commit is contained in:
parent
282639b5b1
commit
0ba31eeb59
@ -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,
|
||||
|
||||
@ -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() {
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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] ...
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user