standard opcodes. remove tx methods.

This commit is contained in:
Christopher Jeffrey 2016-01-17 15:01:22 -08:00
parent 6f4609792f
commit 3745577837
5 changed files with 214 additions and 188 deletions

View File

@ -38,22 +38,54 @@ exports.filterFlags = {
};
exports.opcodes = {
0: 0,
// 'false': 0x00,
'0': 0x00,
pushdata1: 0x4c,
pushdata2: 0x4d,
pushdata4: 0x4e,
// negate1: 0x4f,
'1negate': 0x4f,
reserved: 0x50,
// 'true': 0x51,
'1': 0x51,
'2': 0x52,
'3': 0x53,
'4': 0x54,
'5': 0x55,
'6': 0x56,
'7': 0x57,
'8': 0x58,
'9': 0x59,
'10': 0x5a,
'11': 0x5b,
'12': 0x5c,
'13': 0x5d,
'14': 0x5e,
'15': 0x5f,
'16': 0x60,
nop: 0x61,
if_: 0x63,
ver: 0x62,
'if': 0x63,
notif: 0x64,
else_: 0x67,
verif: 0x65,
vernotif: 0x66,
'else': 0x67,
endif: 0x68,
verify: 0x69,
ret: 0x6a,
'return': 0x6a,
toaltstack: 0x6b,
fromaltstack: 0x6c,
'2drop': 0x6d,
'2dup': 0x6e,
'3dup': 0x6f,
'2over': 0x70,
'2rot': 0x71,
'2swap': 0x72,
ifdup: 0x73,
depth: 0x74,
drop: 0x75,
@ -65,14 +97,8 @@ exports.opcodes = {
rot: 0x7b,
swap: 0x7c,
tuck: 0x7d,
drop2: 0x6d,
dup2: 0x6e,
dup3: 0x6f,
over2: 0x70,
rot2: 0x71,
swap2: 0x72,
cat: 0x74,
cat: 0x7e,
substr: 0x7f,
left: 0x80,
right: 0x81,
@ -82,17 +108,20 @@ exports.opcodes = {
and: 0x84,
or: 0x85,
xor: 0x86,
eq: 0x87,
eqverify: 0x88,
equal: 0x87,
equalverify: 0x88,
add1: 0x8b,
sub1: 0x8c,
mul2: 0x8d,
div2: 0x8e,
reserved1: 0x89,
reserved2: 0x8a,
'1add': 0x8b,
'1sub': 0x8c,
'2mul': 0x8d,
'2div': 0x8e,
negate: 0x8f,
abs: 0x90,
not: 0x91,
noteq0: 0x92,
'0notequal': 0x92,
add: 0x93,
sub: 0x94,
mul: 0x95,
@ -102,13 +131,13 @@ exports.opcodes = {
rshift: 0x99,
booland: 0x9a,
boolor: 0x9b,
numeq: 0x9c,
numeqverify: 0x9d,
numneq: 0x9e,
lt: 0x9f,
gt: 0xa0,
lte: 0xa1,
gte: 0xa2,
numequal: 0x9c,
numequalverify: 0x9d,
numnotequal: 0x9e,
lessthan: 0x9f,
greaterthan: 0xa0,
lessthanorequal: 0xa1,
greaterthanorequal: 0xa2,
min: 0xa3,
max: 0xa4,
within: 0xa5,
@ -118,27 +147,36 @@ exports.opcodes = {
sha256: 0xa8,
hash160: 0xa9,
hash256: 0xaa,
codesep: 0xab,
codeseparator: 0xab,
checksig: 0xac,
checksigverify: 0xad,
checkmultisig: 0xae,
checkmultisigverify: 0xaf,
eval_: 0xb0,
checklocktimeverify: 0xb1
// 'eval': 0xb0,
nop1: 0xb0,
// nop2: 0xb1,
checklocktimeverify: 0xb1,
nop3: 0xb2,
nop4: 0xb3,
nop5: 0xb4,
nop6: 0xb5,
nop7: 0xb6,
nop8: 0xb7,
nop9: 0xb8,
nop10: 0xb9,
pubkeyhash: 0xfd,
pubkey: 0xfe,
invalidopcode: 0xff
};
exports.opcodes['-1'] = 0x50 + -1;
for (i = 1; i <= 16; i++)
exports.opcodes[i] = 0x50 + i;
for (i = 0; i <= 7; i++)
exports.opcodes['nop' + (i + 3)] = 0xb2 + i;
exports.opcodesByVal = new Array(256);
Object.keys(exports.opcodes).forEach(function(name) {
exports.opcodesByVal[exports.opcodes[name]] = name;
var val = exports.opcodes[name];
// if (val === 0x00 || (val >= 0x51 && val <= 0x60))
// name = +name;
exports.opcodesByVal[val] = name;
});
exports.hashType = {

View File

@ -37,14 +37,18 @@ script.decode = function decode(s) {
continue;
}
// Zero
if (b === 0) {
// OP_0, OP_FALSE
// Special case: this is an empty array
// because it can be seen as an empty pushdata.
if (b === 0x00) {
opcodes.push([]);
continue;
}
// Raw number (-1 and 1-16)
if (b === 0x4f || (b >= 0x51 && b <= 0x60)) {
// OP_1, OP_TRUE, OP_2-OP_16
// Special case: these get to be number
// literals. Note: 1negate is not included.
if (b >= 0x51 && b <= 0x60) {
opcodes.push(b - 0x50);
continue;
}
@ -126,8 +130,7 @@ script.encode = function encode(s) {
continue;
}
if (instr.length === 0) {
// OP_FALSE
res.push(0);
res.push(opcodes['0']);
} else if (1 <= instr.length && instr.length <= 0x4b) {
res = res.concat(instr.length, instr);
} else if (instr.length <= 0xff) {
@ -150,6 +153,47 @@ script.encode = function encode(s) {
return res;
};
script.normalize = function normalize(s) {
var i, op;
// Remove OP_ prefixes and lowercase
for (i = 0; i < s.length; i++) {
op = s[i];
if (typeof op === 'string') {
op = op.toLowerCase();
if (op.indexOf('op_') === 0)
op = op.slice(3);
}
s[i] = op;
}
// Convert OP_0 to array, convert OP_1-OP_16
// to number literals, convert -1 to OP_1NEGATE.
// Convert hex strings to arrays.
for (i = 0; i < s.length; i++) {
op = s[i];
if (op === '-1' || op === -1)
op = '1negate';
else if (op === '0' || op === 0 || op === 'false')
op = [];
else if (op === 'true')
op = 1;
else if (+op >= 1 && +op <= 16)
op = +op;
if (typeof op === 'string' && constants.opcodes[op] == null) {
if (op[0] === '[')
op = op.slice(1, -1);
op = utils.toArray(op, 'hex');
}
s[i] = op;
}
return s;
};
script.verify = function verify(input, output, tx, i, flags) {
var copy, res, redeem;
var stack = [];
@ -218,11 +262,11 @@ script.subscript = function subscript(s, lastSep) {
if (lastSep == null)
lastSep = -1;
assert(lastSep <= 0 || s[lastSep] === 'codesep');
assert(lastSep <= 0 || s[lastSep] === 'codeseparator');
res = [];
for (i = lastSep + 1; i < s.length; i++) {
if (s[i] !== 'codesep')
if (s[i] !== 'codeseparator')
res.push(s[i]);
}
@ -263,9 +307,9 @@ script._next = function _next(to, s, pc) {
while (s[pc]) {
o = s[pc];
if (o === 'if_' || o === 'notif')
if (o === 'if' || o === 'notif')
depth++;
else if (o === 'else_')
else if (o === 'else')
depth--;
else if (o === 'endif')
depth--;
@ -276,7 +320,7 @@ script._next = function _next(to, s, pc) {
if (depth === 0 && o === to)
return pc;
if (o === 'else_')
if (o === 'else')
depth++;
pc++;
@ -319,7 +363,7 @@ script.execute = function execute(data, stack, tx, index, flags, recurse) {
continue;
}
if (o === -1 || (o >= 1 && o <= 16)) {
if (o >= 1 && o <= 16) {
stack.push([o]);
continue;
}
@ -336,7 +380,11 @@ script.execute = function execute(data, stack, tx, index, flags, recurse) {
case 'nop10': {
break;
}
case 'if_':
case '1negate': {
stack.push([-1]);
break;
}
case 'if':
case 'notif': {
if (stack.length < 1)
return false;
@ -345,7 +393,7 @@ script.execute = function execute(data, stack, tx, index, flags, recurse) {
if (o === 'notif')
val = !val;
if_ = pc;
else_ = script._next('else_', s, pc);
else_ = script._next('else', s, pc);
endif = script._next('endif', s, pc);
// Splice out the statement blocks we don't need
if (val) {
@ -372,7 +420,7 @@ script.execute = function execute(data, stack, tx, index, flags, recurse) {
pc--;
break;
}
case 'else_': {
case 'else': {
return false;
}
case 'endif': {
@ -385,7 +433,7 @@ script.execute = function execute(data, stack, tx, index, flags, recurse) {
return false;
break;
}
case 'ret': {
case 'return': {
return false;
}
case 'toaltstack': {
@ -479,14 +527,14 @@ script.execute = function execute(data, stack, tx, index, flags, recurse) {
stack.splice(stack.length - 2, 0, stack[stack.length - 1]);
break;
}
case 'drop2': {
case '2drop': {
if (stack.length < 2)
return false;
stack.pop();
stack.pop();
break;
}
case 'dup2': {
case '2dup': {
if (stack.length < 2)
return false;
v1 = stack[stack.length - 2];
@ -495,7 +543,7 @@ script.execute = function execute(data, stack, tx, index, flags, recurse) {
stack.push(v2);
break;
}
case 'dup3': {
case '3dup': {
if (stack.length < 3)
return false;
v1 = stack[stack.length - 3];
@ -506,7 +554,7 @@ script.execute = function execute(data, stack, tx, index, flags, recurse) {
stack.push(v3);
break;
}
case 'over2': {
case '2over': {
if (stack.length < 4)
return false;
v1 = stack[stack.length - 4];
@ -515,7 +563,7 @@ script.execute = function execute(data, stack, tx, index, flags, recurse) {
stack.push(v2);
break;
}
case 'rot2': {
case '2rot': {
if (stack.length < 6)
return false;
v1 = stack[stack.length - 6];
@ -525,7 +573,7 @@ script.execute = function execute(data, stack, tx, index, flags, recurse) {
stack.push(v2);
break;
}
case 'swap2': {
case '2swap': {
if (stack.length < 4)
return false;
v4 = stack[stack.length - 4];
@ -544,20 +592,20 @@ script.execute = function execute(data, stack, tx, index, flags, recurse) {
stack.push(script.array(stack[stack.length - 1].length || 0));
break;
}
case 'add1':
case 'sub1':
case '1add':
case '1sub':
case 'negate':
case 'abs':
case 'not':
case 'noteq0': {
case '0notequal': {
if (stack.length < 1)
return false;
n = script.num(stack.pop());
switch (o) {
case 'add1':
case '1add':
n.iadd(1);
break;
case 'sub1':
case '1sub':
n.isub(1);
break;
case 'negate':
@ -570,7 +618,7 @@ script.execute = function execute(data, stack, tx, index, flags, recurse) {
case 'not':
n = n.cmpn(0) === 0;
break;
case 'noteq0':
case '0notequal':
n = n.cmpn(0) !== 0;
break;
default:
@ -585,13 +633,13 @@ script.execute = function execute(data, stack, tx, index, flags, recurse) {
case 'sub':
case 'booland':
case 'boolor':
case 'numeq':
case 'numeqverify':
case 'numneq':
case 'lt':
case 'gt':
case 'lte':
case 'gte':
case 'numequal':
case 'numequalverify':
case 'numnotequal':
case 'lessthan':
case 'greaterthan':
case 'lessthanorequal':
case 'greaterthanorequal':
case 'min':
case 'max': {
switch (o) {
@ -599,13 +647,13 @@ script.execute = function execute(data, stack, tx, index, flags, recurse) {
case 'sub':
case 'booland':
case 'boolor':
case 'numeq':
case 'numeqverify':
case 'numneq':
case 'lt':
case 'gt':
case 'lte':
case 'gte':
case 'numequal':
case 'numequalverify':
case 'numnotequal':
case 'lessthan':
case 'greaterthan':
case 'lessthanorequal':
case 'greaterthanorequal':
case 'min':
case 'max':
if (stack.length < 2)
@ -626,25 +674,25 @@ script.execute = function execute(data, stack, tx, index, flags, recurse) {
case 'boolor':
n = n1.cmpn(0) !== 0 || n2.cmpn(0) !== 0;
break;
case 'numeq':
case 'numequal':
n = n1.cmp(n2) === 0;
break;
case 'numeqverify':
case 'numequalverify':
n = n1.cmp(n2) === 0;
break;
case 'numneq':
case 'numnotequal':
n = n1.cmp(n2) !== 0;
break;
case 'lt':
case 'lessthan':
n = n1.cmp(n2) < 0;
break;
case 'gt':
case 'greaterthan':
n = n1.cmp(n2) > 0;
break;
case 'lte':
case 'lessthanorequal':
n = n1.cmp(n2) <= 0;
break;
case 'gte':
case 'greaterthanorequal':
n = n1.cmp(n2) >= 0;
break;
case 'min':
@ -659,7 +707,7 @@ script.execute = function execute(data, stack, tx, index, flags, recurse) {
if (typeof n === 'boolean')
n = script.num(+n);
res = n.cmpn(0) !== 0;
if (o === 'numeqverify') {
if (o === 'numequalverify') {
if (!res)
return false;
} else {
@ -679,7 +727,7 @@ script.execute = function execute(data, stack, tx, index, flags, recurse) {
break;
}
case 'codesep': {
case 'codeseparator': {
lastSep = pc;
break;
}
@ -713,12 +761,12 @@ script.execute = function execute(data, stack, tx, index, flags, recurse) {
stack.push(utils.ripesha(stack.pop()));
break;
}
case 'eqverify':
case 'eq': {
case 'equalverify':
case 'equal': {
if (stack.length < 2)
return false;
res = utils.isEqual(stack.pop(), stack.pop());
if (o === 'eqverify') {
if (o === 'equalverify') {
if (!res)
return false;
} else {
@ -886,7 +934,7 @@ script.execute = function execute(data, stack, tx, index, flags, recurse) {
break;
}
case 'eval_': {
case 'nop1': {
// OP_EVAL = OP_NOP1
if (!flags.allowEval)
break;
@ -904,7 +952,7 @@ script.execute = function execute(data, stack, tx, index, flags, recurse) {
evalScript = script.decode(evalScript);
res = evalScript.some(function(op) {
return op === 'codesep';
return op === 'codeseparator';
});
if (res)
@ -1023,7 +1071,7 @@ script.checkPush = function checkPush(op, value) {
return op >= constants.opcodes['1'] && op <= constants.opcodes['16'];
if (value.length === 1 && value[0] === -1)
return op === constants.opcodes['-1'];
return op === constants.opcodes['1negate'];
if (value.length <= 75)
return op === value.length;
@ -1037,24 +1085,26 @@ script.checkPush = function checkPush(op, value) {
return true;
};
script.redeem = function redeem(keys, m, n) {
script.createMultisig = function createMultisig(keys, m, n) {
if (keys.length !== n)
throw new Error(n + ' keys are required to generate redeem script');
throw new Error(n + ' keys are required to generate multisig script');
assert(m >= 1 && m <= n);
assert(n >= 1 && n <= 15);
while (keys.length < n)
keys.push([]);
keys = utils.sortKeys(keys);
return [m].concat(
keys,
utils.sortKeys(keys),
[n, 'checkmultisig']
);
};
script.redeem = function redeem(s) {
if (!Array.isArray(s[s.length - 1]))
return;
return bcoin.script.decode(s[s.length - 1]);
};
script.standard = function standard(s) {
return (script.isPubkey(s) && 'pubkey')
|| (script.isPubkeyhash(s) && 'pubkeyhash')
@ -1099,7 +1149,7 @@ script.isLockTime = function isLockTime(s, check) {
&& Array.isArray(s[0])
&& s[1] === 'checklocktimeverify'
&& s[2] === 'drop'
&& s[3] === 'codesep';
&& s[3] === 'codeseparator';
};
script.lockTime = function lockTime(s) {
@ -1292,7 +1342,7 @@ script.isPubkeyhash = function isPubkeyhash(s, hash) {
res = s[0] === 'dup'
&& s[1] === 'hash160'
&& script.isHash(s[2])
&& s[3] === 'eqverify'
&& s[3] === 'equalverify'
&& s[4] === 'checksig';
if (!res)
@ -1386,7 +1436,7 @@ script.isScripthash = function isScripthash(s, hash) {
res = s[0] === 'hash160'
&& script.isHash(s[1])
&& s[2] === 'eq';
&& s[2] === 'equal';
if (!res)
return false;
@ -1405,7 +1455,7 @@ script.isNulldata = function isNulldata(s) {
if (s.length !== 2)
return false;
res = s[0] === 'ret' && script.isData(s[1]);
res = s[0] === 'return' && script.isData(s[1]);
if (!res)
return false;
@ -1485,7 +1535,7 @@ script.isMultisigInput = function isMultisigInput(s, keys, tx, i) {
assert(keys.length >= 2);
assert(tx);
assert(i != null);
o = script.redeem(keys, s.length - 1, keys.length);
o = script.createMultisig(keys, s.length - 1, keys.length);
if (!script.verify(s, o, tx, i))
return false;
}
@ -1494,7 +1544,7 @@ script.isMultisigInput = function isMultisigInput(s, keys, tx, i) {
// var recovered = [];
// for (i = 1; i < s.length; i++) {
// var sig = s[i];
// var prev = script.redeem(keys, s.length - 1, keys.length);
// var prev = script.createMultisig(keys, s.length - 1, keys.length);
// var msg = tx.signatureHash(i, prev, s[s.length - 1]);
// var key = bcoin.ecdsa.recoverPubKey(msg, sig.slice(0, -1), 0).toArray();
// recovered.push(key);
@ -1889,9 +1939,7 @@ script.pushOnly = function pushOnly(s) {
var i, op;
for (i = 0; i < s.length; i++) {
op = s[i];
// if (Array.isArray(op) || (constants.opcodes[op] <= constants.opcodes['16']))
// continue;
if (Array.isArray(op) || (op >= -1 && op <= 16))
if (Array.isArray(op) || op === '1negate' || (op >= 1 && op <= 16))
continue;
if (constants.opcodes[op] == null)
return false;

View File

@ -450,68 +450,6 @@ TX.prototype.signInput = function signInput(index, key, type) {
return signatures === m;
};
TX.prototype.prevOut = function outputScript(i) {
var input;
if (typeof i !== 'number')
i = this.inputs.indexOf(i);
input = this.inputs[i];
if (!input.out.tx)
return;
return input.out.tx.outputs[input.out.index];
};
TX.prototype.outputScript = function outputScript(i) {
var output = this.prevOut(i);
if (!output)
return;
return output.script;
};
bcoin.script.redeemScript = function redeemScript(s) {
if (!Array.isArray(s[s.length - 1]))
return;
return bcoin.script.decode(s[s.length - 1]);
};
TX.prototype.redeemScript = function redeemScript(i) {
var input, prev;
if (typeof i !== 'number')
i = this.inputs.indexOf(i);
input = this.inputs[i];
prev = this.outputScript(i);
if (prev && bcoin.script.isScripthash(prev))
return bcoin.script.redeemScript(input.script);
return prev;
};
TX.prototype.finalize = function finalize() {
var i, input, s, len, j;
for (i = 0; i < this.inputs.length; i++) {
input = this.inputs[i];
s = input.out.tx.getSubscript(input.out.index);
len = s.length;
if (bcoin.script.isScripthash(s))
len--;
for (j = len - 1; j >= 1; j--) {
if (bcoin.script.isEmpty(input.script[j])) {
input.script.splice(j, 1);
len--;
}
}
}
};
TX.prototype.scriptSig = function scriptSig(index, key, pub, redeem, type) {
var input;
@ -621,7 +559,7 @@ TX.prototype.scriptOutput = function scriptOutput(index, options) {
if (!(n >= 1 && n <= (options.scripthash ? 15 : 3)))
return;
script = bcoin.script.redeem(keys, m, n);
script = bcoin.script.createMultisig(keys, m, n);
} else if (bcoin.wallet.validateAddress(options.address, 'scripthash')) {
// P2SH Transaction
// https://github.com/bitcoin/bips/blob/master/bip-0016.mediawiki
@ -629,7 +567,7 @@ TX.prototype.scriptOutput = function scriptOutput(index, options) {
script = [
'hash160',
bcoin.wallet.addr2hash(options.address, 'scripthash'),
'eq'
'equal'
];
} else if (options.address) {
// P2PKH Transaction
@ -638,7 +576,7 @@ TX.prototype.scriptOutput = function scriptOutput(index, options) {
'dup',
'hash160',
bcoin.wallet.addr2hash(options.address, 'pubkeyhash'),
'eqverify',
'equalverify',
'checksig'
];
} else if (options.key) {
@ -650,14 +588,14 @@ TX.prototype.scriptOutput = function scriptOutput(index, options) {
];
} else if (options.flags) {
// Nulldata Transaction
// ret [data]
// return [data]
flags = options.flags;
if (typeof flags === 'string')
flags = utils.ascii2array(flags);
assert(utils.isBuffer(flags));
assert(flags.length <= constants.script.maxOpReturn);
script = [
'ret',
'return',
flags
];
}
@ -670,14 +608,14 @@ TX.prototype.scriptOutput = function scriptOutput(index, options) {
bcoin.script.array(options.lock),
'checklocktimeverify',
'drop',
'codesep'
'codeseparator'
].concat(script);
}
hash = utils.ripesha(bcoin.script.encode(script));
script = [
'hash160',
hash,
'eq'
'equal'
];
}

View File

@ -261,12 +261,14 @@ Wallet.prototype.getScript = function getScript() {
'dup',
'hash160',
this.getKeyHash(),
'eqverify',
'equalverify',
'checksig'
]);
}
return bcoin.script.encode(bcoin.script.redeem(this.keys, this.m, this.n));
return bcoin.script.encode(
bcoin.script.createMultisig(this.keys, this.m, this.n)
);
};
Wallet.prototype.getScriptHash = function getScriptHash() {

View File

@ -47,7 +47,7 @@ describe('Script', function() {
it('should handle if statements correctly', function () {
var inputScript = [1, 2];
var prevOutScript = [2, 'eq', 'if_', 3, 'else_', 4, 'endif', 5];
var prevOutScript = [2, 'equal', 'if', 3, 'else', 4, 'endif', 5];
var stack = [];
bcoin.script.execute(inputScript, stack);
var res = bcoin.script.execute(prevOutScript, stack);
@ -55,7 +55,7 @@ describe('Script', function() {
assert.deepEqual(stack.slice(), [[1], [3], [5]]);
var inputScript = [1, 2];
var prevOutScript = [9, 'eq', 'if_', 3, 'else_', 4, 'endif', 5];
var prevOutScript = [9, 'equal', 'if', 3, 'else', 4, 'endif', 5];
var stack = [];
bcoin.script.execute(inputScript, stack);
var res = bcoin.script.execute(prevOutScript, stack);
@ -63,7 +63,7 @@ describe('Script', function() {
assert.deepEqual(stack.slice(), [[1], [4], [5]]);
var inputScript = [1, 2];
var prevOutScript = [2, 'eq', 'if_', 3, 'endif', 5];
var prevOutScript = [2, 'equal', 'if', 3, 'endif', 5];
var stack = [];
bcoin.script.execute(inputScript, stack);
var res = bcoin.script.execute(prevOutScript, stack);
@ -71,7 +71,7 @@ describe('Script', function() {
assert.deepEqual(stack.slice(), [[1], [3], [5]]);
var inputScript = [1, 2];
var prevOutScript = [9, 'eq', 'if_', 3, 'endif', 5];
var prevOutScript = [9, 'equal', 'if', 3, 'endif', 5];
var stack = [];
bcoin.script.execute(inputScript, stack);
var res = bcoin.script.execute(prevOutScript, stack);
@ -79,7 +79,7 @@ describe('Script', function() {
assert.deepEqual(stack.slice(), [[1], [5]]);
var inputScript = [1, 2];
var prevOutScript = [9, 'eq', 'notif', 3, 'endif', 5];
var prevOutScript = [9, 'equal', 'notif', 3, 'endif', 5];
var stack = [];
bcoin.script.execute(inputScript, stack);
var res = bcoin.script.execute(prevOutScript, stack);