From c35693257ca59b80659cfc4a965311f028c2d751 Mon Sep 17 00:00:00 2001 From: instagibbs Date: Wed, 12 Oct 2016 12:19:56 -0400 Subject: [PATCH] [HF] Re-activated some disabled Bitcoin OPs and enabled new opcodes --- src/core_read.cpp | 2 +- src/script/interpreter.cpp | 369 +++++++++++++++++++++++++++++++++++-- src/script/script.cpp | 4 + src/script/script.h | 4 + 4 files changed, 366 insertions(+), 13 deletions(-) diff --git a/src/core_read.cpp b/src/core_read.cpp index a8d667e3bc..1b4117eac7 100644 --- a/src/core_read.cpp +++ b/src/core_read.cpp @@ -28,7 +28,7 @@ CScript ParseScript(const std::string& s) if (mapOpNames.empty()) { - for (int op = 0; op <= OP_NOP10; op++) + for (int op = 0; op <= 0xff; op++) { // Allow OP_RESERVED to get into mapOpNames if (op < OP_NOP && op != OP_RESERVED) diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp index 1699534f2a..6f9f9aa5d8 100644 --- a/src/script/interpreter.cpp +++ b/src/script/interpreter.cpp @@ -332,21 +332,11 @@ bool EvalScript(vector >& stack, const CScript& script, un 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 || - opcode == OP_2MUL || + if (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. if (fExec && 0 <= opcode && opcode <= OP_PUSHDATA4) { @@ -740,6 +730,26 @@ bool EvalScript(vector >& stack, const CScript& script, un } 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: { @@ -751,10 +761,254 @@ bool EvalScript(vector >& stack, const CScript& script, un } 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: + case OP_SUBSTR_LAZY: + { + 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 (opcode == OP_SUBSTR_LAZY) { + if (start < 0) + start = 0; + + if (length < 0) + length = 0; + + if (start >= vch1.size()) { + popstack(stack); + popstack(stack); + popstack(stack); + stack.push_back(vchZero); + break; + } + + if (length > MAX_SCRIPT_ELEMENT_SIZE) + length = MAX_SCRIPT_ELEMENT_SIZE; + + // start + length cannot overflow because of the restrictions immediately abo + if (start + length > vch1.size()) { + length = CScriptNum(vch1.size()) - start; + } + } + + 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> 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 @@ -1071,6 +1325,97 @@ bool EvalScript(vector >& stack, const CScript& script, un } break; + case OP_CHECKSIGFROMSTACK: + case OP_CHECKSIGFROMSTACKVERIFY: + { + // (sig data pubkey -- bool) + if (stack.size() < 3) + return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION); + + valtype& vchSig = stacktop(-3); + valtype& vchData = stacktop(-2); + valtype& vchPubKey = stacktop(-1); + + // Sigs from stack have no hash type, so we disable strictenc check + if (!CheckSignatureEncoding(vchSig, (flags & ~SCRIPT_VERIFY_STRICTENC), serror) || !CheckPubKeyEncoding(vchPubKey, flags, sigversion, serror)) { + //serror is set + return false; + } + + valtype vchHash(32); + CSHA256().Write(vchData.data(), vchData.size()).Finalize(vchHash.data()); + uint256 hash(vchHash); + + CPubKey pubkey(vchPubKey); + bool fSuccess = pubkey.Verify(hash, vchSig); + + popstack(stack); + popstack(stack); + popstack(stack); + stack.push_back(fSuccess ? vchTrue : vchFalse); + if (opcode == OP_CHECKSIGFROMSTACKVERIFY) + popstack(stack); + + if (!fSuccess) + return set_error(serror, SCRIPT_ERR_CHECKSIGVERIFY); + } + break; + + case OP_DETERMINISTICRANDOM: + { + if (stack.size() < 3) + return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION); + + valtype vchSeed = stacktop(-3); + CScriptNum bnMin(stacktop(-2), fRequireMinimal); + CScriptNum bnMax(stacktop(-1), fRequireMinimal); + + if (bnMin > bnMax) + return set_error(serror, SCRIPT_ERR_UNKNOWN_ERROR); + + if (bnMin == bnMax) { + popstack(stack); + popstack(stack); + popstack(stack); + stack.push_back(bnMin.getvch()); + break; + } + + // The range of the random source must be a multiple of the modulus + // to give every possible output value an equal possibility + uint64_t nMax = (bnMax-bnMin).getint(); + uint64_t nRange = (std::numeric_limits::max() / nMax) * nMax; + uint64_t nRand; + + valtype vchHash(32, 0); + uint64_t nCounter = 0; + int nHashIndex = 3; + CSHA256 hasher; + hasher.Write(vchSeed.data(), vchSeed.size()); + do { + if (nHashIndex >= 3) { + //TODO this isn't endian safe + CSHA256(hasher).Write((const unsigned char*)&nCounter, sizeof(nCounter)).Finalize(vchHash.data()); + nHashIndex = 0; + nCounter++; + } + + nRand = 0; + for (size_t i=0; i<8; ++i) + nRand |= ((uint64_t)vchHash[(nHashIndex*8) + i]) << (8*i); + + nHashIndex++; + } while (nRand > nRange); + CScriptNum result(nRand % nMax); + result += bnMin.getint(); + + popstack(stack); + popstack(stack); + popstack(stack); + stack.push_back(result.getvch()); + } + break; + case OP_WITHDRAWPROOFVERIFY: { // In the make-withdraw case, reads the following from the stack: diff --git a/src/script/script.cpp b/src/script/script.cpp index 6c141e26ad..290e8c3570 100644 --- a/src/script/script.cpp +++ b/src/script/script.cpp @@ -80,6 +80,7 @@ const char* GetOpName(opcodetype opcode) // splice ops case OP_CAT : return "OP_CAT"; case OP_SUBSTR : return "OP_SUBSTR"; + case OP_SUBSTR_LAZY : return "OP_SUBSTR_LAZY"; case OP_LEFT : return "OP_LEFT"; case OP_RIGHT : return "OP_RIGHT"; case OP_SIZE : return "OP_SIZE"; @@ -134,6 +135,9 @@ const char* GetOpName(opcodetype opcode) case OP_CHECKSIGVERIFY : return "OP_CHECKSIGVERIFY"; case OP_CHECKMULTISIG : return "OP_CHECKMULTISIG"; case OP_CHECKMULTISIGVERIFY : return "OP_CHECKMULTISIGVERIFY"; + case OP_DETERMINISTICRANDOM : return "OP_DETERMINISTICRANDOM"; + case OP_CHECKSIGFROMSTACK : return "OP_CHECKSIGFROMSTACK"; + case OP_CHECKSIGFROMSTACKVERIFY: return "OP_CHECKSIGFROMSTACKVERIFY"; // expansion case OP_NOP1 : return "OP_NOP1"; diff --git a/src/script/script.h b/src/script/script.h index 1d2046b6d7..44d5e13d32 100644 --- a/src/script/script.h +++ b/src/script/script.h @@ -107,6 +107,7 @@ enum opcodetype // splice ops OP_CAT = 0x7e, OP_SUBSTR = 0x7f, + OP_SUBSTR_LAZY = 0xc3, OP_LEFT = 0x80, OP_RIGHT = 0x81, OP_SIZE = 0x82, @@ -164,6 +165,9 @@ enum opcodetype OP_CHECKSIGVERIFY = 0xad, OP_CHECKMULTISIG = 0xae, OP_CHECKMULTISIGVERIFY = 0xaf, + OP_DETERMINISTICRANDOM = 0xc0, + OP_CHECKSIGFROMSTACK = 0xc1, + OP_CHECKSIGFROMSTACKVERIFY = 0xc2, // expansion OP_NOP1 = 0xb0,