Skip to content

Commit

Permalink
Re-enable several disabled opcodes
Browse files Browse the repository at this point in the history
Re-enabled opcodes are:

- OP_CAT
- OP_SUBSTR
- OP_LEFT
- OP_RIGHT
- OP_INVERT
- OP_AND
- OP_OR
- OP_XOR
- OP_LSHIFT
- OP_RSHIFT
  • Loading branch information
stevenroose committed Jan 11, 2019
1 parent 4ec1d91 commit 13e1103
Show file tree
Hide file tree
Showing 2 changed files with 257 additions and 27 deletions.
269 changes: 257 additions & 12 deletions src/script/interpreter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -293,7 +293,7 @@ bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript&
// static const CScriptNum bnFalse(0);
// static const CScriptNum bnTrue(1);
static const valtype vchFalse(0);
// static const valtype vchZero(0);
static const valtype vchZero(0);
static const valtype vchTrue(1, 1);

CScript::const_iterator pc = script.begin();
Expand Down Expand Up @@ -327,22 +327,26 @@ bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript&
if (opcode > OP_16 && ++nOpCount > MAX_OPS_PER_SCRIPT)
return set_error(serror, SCRIPT_ERR_OP_COUNT);

if (opcode == OP_CAT ||
opcode == OP_SUBSTR ||
opcode == OP_LEFT ||
opcode == OP_RIGHT ||
opcode == OP_INVERT ||
opcode == OP_AND ||
opcode == OP_OR ||
opcode == OP_XOR ||
// ELEMENTS:
// commented out opcodes are re-enabled in Elements
if (//opcode == OP_CAT ||
//opcode == OP_SUBSTR ||
//opcode == OP_LEFT ||
//opcode == OP_RIGHT ||
//opcode == OP_INVERT ||
//opcode == OP_AND ||
//opcode == OP_OR ||
//opcode == OP_XOR ||
//opcode == OP_LSHIFT ||
//opcode == OP_RSHIFT ||
opcode == OP_2MUL ||
opcode == OP_2DIV ||
opcode == OP_MUL ||
opcode == OP_DIV ||
opcode == OP_MOD ||
opcode == OP_LSHIFT ||
opcode == OP_RSHIFT)
opcode == OP_MOD
) {
return set_error(serror, SCRIPT_ERR_DISABLED_OPCODE); // Disabled opcodes.
}

// With SCRIPT_VERIFY_CONST_SCRIPTCODE, OP_CODESEPARATOR in non-segwit script is rejected even in an unexecuted branch
if (opcode == OP_CODESEPARATOR && sigversion == SigVersion::BASE && (flags & SCRIPT_VERIFY_CONST_SCRIPTCODE))
Expand Down Expand Up @@ -733,6 +737,26 @@ bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript&
}
break;

case OP_CAT:
{
if (stack.size() < 2)
return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION);
valtype vch1 = stacktop(-2);
valtype vch2 = stacktop(-1);

if (vch1.size() + vch2.size() > MAX_SCRIPT_ELEMENT_SIZE)
return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION);

valtype vch3;
vch3.reserve(vch1.size() + vch2.size());
vch3.insert(vch3.end(), vch1.begin(), vch1.end());
vch3.insert(vch3.end(), vch2.begin(), vch2.end());

popstack(stack);
popstack(stack);
stack.push_back(vch3);
}
break;

case OP_SIZE:
{
Expand All @@ -745,9 +769,230 @@ bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript&
break;


//
// String operators
//
case OP_LEFT:
case OP_RIGHT:
{
if (stack.size() < 2)
return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION);

valtype vch1 = stacktop(-2);
CScriptNum start(stacktop(-1), fRequireMinimal);

if (start < 0)
return set_error(serror, SCRIPT_ERR_UNKNOWN_ERROR);

valtype vch2;
switch (opcode) {
case OP_RIGHT:
{
if (start >= vch1.size())
vch2 = vchZero;
else
vch2.insert(vch2.begin(), vch1.begin() + start.getint(), vch1.end());
break;
}
case OP_LEFT:
{
if (start >= vch1.size())
vch2 = vch1;
else
vch2.insert(vch2.begin(), vch1.begin(), vch1.begin() + start.getint());
break;
}
default:
{
assert(!"invalid opcode");
break;
}
}
popstack(stack);
popstack(stack);
stack.push_back(vch2);
}
break;

case OP_SUBSTR:
{
if (stack.size() < 3)
return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION);

valtype vch1 = stacktop(-3);
CScriptNum start(stacktop(-2), fRequireMinimal);
CScriptNum length(stacktop(-1), fRequireMinimal);

if (length < 0 || start < 0)
return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION);

if (start >= vch1.size())
return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION);

if (length > vch1.size())
return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION);

if ((start + length) > vch1.size())
return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION);

valtype vch2;
vch2.insert(vch2.begin(), vch1.begin() + start.getint(), vch1.begin() + (start + length).getint());

popstack(stack);
popstack(stack);
popstack(stack);
stack.push_back(vch2);
}
break;


//
// Bitwise logic
//
case OP_RSHIFT:
{
if (stack.size() < 2)
return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION);
valtype vch1 = stacktop(-2);
CScriptNum bn(stacktop(-1), fRequireMinimal);

if (bn < 0)
return set_error(serror, SCRIPT_ERR_UNKNOWN_ERROR);

unsigned int full_bytes = bn.getint() / 8;
unsigned int bits = bn.getint() % 8;

if (full_bytes >= vch1.size()) {
popstack(stack);
popstack(stack);
stack.push_back(vchZero);
break;
}

valtype vch2;
vch2.insert(vch2.begin(), vch1.begin() + full_bytes, vch1.end());

uint16_t temp = 0;
for (int i=(vch2.size()-1);i>=0;--i) {
temp = (vch2[i] << (8 - bits)) | ((temp << 8) & 0xff00);
vch2[i] = (temp & 0xff00) >> 8;
}

// 0x0fff >> 4 == 0x00ff or 0xff, reduce to minimal representation
while (!vch2.empty() && vch2.back() == 0)
vch2.pop_back();

popstack(stack);
popstack(stack);
stack.push_back(vch2);
}
break;

case OP_LSHIFT:
{
if (stack.size() < 2)
return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION);
valtype vch1 = stacktop(-2);
CScriptNum bn(stacktop(-1), fRequireMinimal);

if (bn < 0)
return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION);

unsigned int full_bytes = bn.getint() / 8;
unsigned int bits = bn.getint() % 8;

if (vch1.size() + full_bytes + (bits ? 1 : 0) > MAX_SCRIPT_ELEMENT_SIZE)
return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION);

valtype vch2;
vch2.reserve(vch1.size() + full_bytes + 1);
vch2.insert(vch2.end(), full_bytes, 0);
vch2.insert(vch2.end(), vch1.begin(), vch1.end());
vch2.insert(vch2.end(), 1, 0);

uint16_t temp = 0;
for (size_t i=0;i<vch2.size();++i) {
temp = (vch2[i] << bits) | (temp >> 8);
vch2[i] = temp & 0xff;
}

// reduce to minimal representation
while (!vch2.empty() && vch2.back() == 0)
vch2.pop_back();

popstack(stack);
popstack(stack);
stack.push_back(vch2);
}
break;

case OP_INVERT:
{
if (stack.size() < 1)
return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION);
valtype& vch1 = stacktop(-1);
for (size_t i = 0; i < vch1.size(); ++i)
vch1[i] = ~vch1[i];
}
break;

case OP_AND:
{
// (x1 x2 -- x1 & x2)
if (stack.size() < 2)
return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION);
valtype& vch1 = stacktop(-1);
valtype& vch2 = stacktop(-2);
if (vch1.size() != vch2.size())
return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION);

valtype vch3(vch1);
for (size_t i = 0; i < vch1.size(); i++)
vch3[i] &= vch2[i];
popstack(stack);
popstack(stack);
stack.push_back(vch3);
}
break;

case OP_OR:
{
// (x1 x2 -- x1 | x2)
if (stack.size() < 2)
return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION);
valtype& vch1 = stacktop(-1);
valtype& vch2 = stacktop(-2);
if (vch1.size() != vch2.size())
return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION);

valtype vch3(vch1);
for (size_t i = 0; i < vch1.size(); i++)
vch3[i] |= vch2[i];
popstack(stack);
popstack(stack);
stack.push_back(vch3);
}
break;

case OP_XOR:
{
// (x1 x2 -- x1 ^ x2)
if (stack.size() < 2)
return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION);
valtype& vch1 = stacktop(-1);
valtype& vch2 = stacktop(-2);
if (vch1.size() != vch2.size())
return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION);

valtype vch3(vch1);
for (size_t i = 0; i < vch1.size(); i++)
vch3[i] ^= vch2[i];
popstack(stack);
popstack(stack);
stack.push_back(vch3);
}
break;

case OP_EQUAL:
case OP_EQUALVERIFY:
//case OP_NOTEQUAL: // use OP_NUMNOTEQUAL
Expand Down
15 changes: 0 additions & 15 deletions src/test/data/script_tests.json
Original file line number Diff line number Diff line change
Expand Up @@ -826,26 +826,13 @@
["NOP", "2SWAP 1", "P2SH,STRICTENC", "INVALID_STACK_OPERATION"],
["1", "2 3 2SWAP 1", "P2SH,STRICTENC", "INVALID_STACK_OPERATION"],

["'a' 'b'", "CAT", "P2SH,STRICTENC", "DISABLED_OPCODE", "CAT disabled"],
["'a' 'b' 0", "IF CAT ELSE 1 ENDIF", "P2SH,STRICTENC", "DISABLED_OPCODE", "CAT disabled"],
["'abc' 1 1", "SUBSTR", "P2SH,STRICTENC", "DISABLED_OPCODE", "SUBSTR disabled"],
["'abc' 1 1 0", "IF SUBSTR ELSE 1 ENDIF", "P2SH,STRICTENC", "DISABLED_OPCODE", "SUBSTR disabled"],
["'abc' 2 0", "IF LEFT ELSE 1 ENDIF", "P2SH,STRICTENC", "DISABLED_OPCODE", "LEFT disabled"],
["'abc' 2 0", "IF RIGHT ELSE 1 ENDIF", "P2SH,STRICTENC", "DISABLED_OPCODE", "RIGHT disabled"],

["NOP", "SIZE 1", "P2SH,STRICTENC", "INVALID_STACK_OPERATION"],

["'abc'", "IF INVERT ELSE 1 ENDIF", "P2SH,STRICTENC", "DISABLED_OPCODE", "INVERT disabled"],
["1 2 0 IF AND ELSE 1 ENDIF", "NOP", "P2SH,STRICTENC", "DISABLED_OPCODE", "AND disabled"],
["1 2 0 IF OR ELSE 1 ENDIF", "NOP", "P2SH,STRICTENC", "DISABLED_OPCODE", "OR disabled"],
["1 2 0 IF XOR ELSE 1 ENDIF", "NOP", "P2SH,STRICTENC", "DISABLED_OPCODE", "XOR disabled"],
["2 0 IF 2MUL ELSE 1 ENDIF", "NOP", "P2SH,STRICTENC", "DISABLED_OPCODE", "2MUL disabled"],
["2 0 IF 2DIV ELSE 1 ENDIF", "NOP", "P2SH,STRICTENC", "DISABLED_OPCODE", "2DIV disabled"],
["2 2 0 IF MUL ELSE 1 ENDIF", "NOP", "P2SH,STRICTENC", "DISABLED_OPCODE", "MUL disabled"],
["2 2 0 IF DIV ELSE 1 ENDIF", "NOP", "P2SH,STRICTENC", "DISABLED_OPCODE", "DIV disabled"],
["2 2 0 IF MOD ELSE 1 ENDIF", "NOP", "P2SH,STRICTENC", "DISABLED_OPCODE", "MOD disabled"],
["2 2 0 IF LSHIFT ELSE 1 ENDIF", "NOP", "P2SH,STRICTENC", "DISABLED_OPCODE", "LSHIFT disabled"],
["2 2 0 IF RSHIFT ELSE 1 ENDIF", "NOP", "P2SH,STRICTENC", "DISABLED_OPCODE", "RSHIFT disabled"],

["", "EQUAL NOT", "P2SH,STRICTENC", "INVALID_STACK_OPERATION", "EQUAL must error when there are no stack items"],
["0", "EQUAL NOT", "P2SH,STRICTENC", "INVALID_STACK_OPERATION", "EQUAL must error when there are not 2 stack items"],
Expand All @@ -863,8 +850,6 @@
["2 2MUL", "4 EQUAL", "P2SH,STRICTENC", "DISABLED_OPCODE", "disabled"],
["2 2DIV", "1 EQUAL", "P2SH,STRICTENC", "DISABLED_OPCODE", "disabled"],
["7 3 MOD", "1 EQUAL", "P2SH,STRICTENC", "DISABLED_OPCODE", "disabled"],
["2 2 LSHIFT", "8 EQUAL", "P2SH,STRICTENC", "DISABLED_OPCODE", "disabled"],
["2 1 RSHIFT", "1 EQUAL", "P2SH,STRICTENC", "DISABLED_OPCODE", "disabled"],

["1", "NOP1 CHECKLOCKTIMEVERIFY CHECKSEQUENCEVERIFY NOP4 NOP5 NOP6 NOP7 NOP8 NOP9 NOP10 2 EQUAL", "P2SH,STRICTENC", "EVAL_FALSE"],
["'NOP_1_to_10' NOP1 CHECKLOCKTIMEVERIFY CHECKSEQUENCEVERIFY NOP4 NOP5 NOP6 NOP7 NOP8 NOP9 NOP10","'NOP_1_to_11' EQUAL", "P2SH,STRICTENC", "EVAL_FALSE"],
Expand Down

0 comments on commit 13e1103

Please sign in to comment.