diff --git a/src/Makefile.test.include b/src/Makefile.test.include --- a/src/Makefile.test.include +++ b/src/Makefile.test.include @@ -58,6 +58,7 @@ test/mempool_tests.cpp \ test/merkle_tests.cpp \ test/miner_tests.cpp \ + test/monolith_opcodes_string.cpp \ test/multisig_tests.cpp \ test/net_tests.cpp \ test/netbase_tests.cpp \ diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp --- a/src/script/interpreter.cpp +++ b/src/script/interpreter.cpp @@ -298,10 +298,10 @@ if (fEnabledOpCodesMonolith) { switch (opcode) { case OP_CAT: - return true; + return false; case OP_SPLIT: - return true; + return false; case OP_BIN2NUM: return true; @@ -1238,6 +1238,65 @@ } } break; + // + // Byte string operations + // + case OP_CAT: { + // (x1 x2 -- out) + 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_PUSH_SIZE); + } + vch1.insert(vch1.end(), vch2.begin(), vch2.end()); + popstack(stack); + } break; + + case OP_SPLIT: { + // (in position -- x1 x2) + if (stack.size() < 2) { + return set_error( + serror, SCRIPT_ERR_INVALID_STACK_OPERATION); + } + valtype vch = stacktop(-2); + int64_t nPosition = + CScriptNum(stacktop(-1), fRequireMinimal).getint(); + + // if nPosition is less than 0 or is larger than the + // input then throw error + if (nPosition < 0 || + static_cast(nPosition) > vch.size()) { + return set_error(serror, + SCRIPT_ERR_INVALID_SPLIT_RANGE); + } + + popstack(stack); + popstack(stack); + + // initialize outputs + if (nPosition == 0) { + stack.push_back(valtype()); + stack.push_back(vch); + } else if (static_cast(nPosition) == + vch.size()) { + stack.push_back(vch); + stack.push_back(valtype()); + } else { + valtype vchOut1, vchOut2; + vchOut1.insert(vchOut1.end(), vch.begin(), + vch.begin() + nPosition); + vchOut2.insert(vchOut2.end(), + vch.begin() + nPosition, vch.end()); + stack.emplace_back(move(vchOut1)); + stack.emplace_back(move(vchOut2)); + } + } break; + default: return set_error(serror, SCRIPT_ERR_BAD_OPCODE); } diff --git a/src/script/script_error.h b/src/script/script_error.h --- a/src/script/script_error.h +++ b/src/script/script_error.h @@ -20,6 +20,9 @@ SCRIPT_ERR_SIG_COUNT, SCRIPT_ERR_PUBKEY_COUNT, + /* Operands checks */ + SCRIPT_ERR_INVALID_SPLIT_RANGE, + /* Failed verify operations */ SCRIPT_ERR_VERIFY, SCRIPT_ERR_EQUALVERIFY, diff --git a/src/script/script_error.cpp b/src/script/script_error.cpp --- a/src/script/script_error.cpp +++ b/src/script/script_error.cpp @@ -34,6 +34,8 @@ return "Signature count negative or greater than pubkey count"; case SCRIPT_ERR_PUBKEY_COUNT: return "Pubkey count negative or limit exceeded"; + case SCRIPT_ERR_INVALID_SPLIT_RANGE: + return "Invalid OP_SPLIT range"; case SCRIPT_ERR_BAD_OPCODE: return "Opcode missing or not understood"; case SCRIPT_ERR_DISABLED_OPCODE: diff --git a/src/test/CMakeLists.txt b/src/test/CMakeLists.txt --- a/src/test/CMakeLists.txt +++ b/src/test/CMakeLists.txt @@ -35,6 +35,7 @@ mempool_tests.cpp merkle_tests.cpp miner_tests.cpp + monolith_opcodes_string.cpp multisig_tests.cpp net_tests.cpp netbase_tests.cpp diff --git a/src/test/data/script_tests.json b/src/test/data/script_tests.json --- a/src/test/data/script_tests.json +++ b/src/test/data/script_tests.json @@ -818,13 +818,61 @@ ["1", "2 3 2SWAP 1", "P2SH,STRICTENC", "INVALID_STACK_OPERATION"], ["'a' 'b'", "CAT", "P2SH,STRICTENC", "DISABLED_OPCODE", "CAT disabled"], -["'a' 'b'", "CAT", "P2SH,STRICTENC,MONOLITH_OPCODES", "DISABLED_OPCODE", "CAT disabled"], +["'ab' 'a' 'b'", "CAT EQUAL", "P2SH,STRICTENC,MONOLITH_OPCODES", "OK", "CAT enabled"], ["'a' 'b' 0", "IF CAT ELSE 1 ENDIF", "P2SH,STRICTENC", "DISABLED_OPCODE", "CAT disabled"], -["'a' 'b' 0", "IF CAT ELSE 1 ENDIF", "P2SH,STRICTENC,MONOLITH_OPCODES", "DISABLED_OPCODE", "CAT disabled"], -["'abc' 1 1", "SPLIT", "P2SH,STRICTENC", "DISABLED_OPCODE", "SPLIT disabled"], -["'abc' 1 1", "SPLIT", "P2SH,STRICTENC,MONOLITH_OPCODES", "DISABLED_OPCODE", "SPLIT disabled"], -["'abc' 1 1 0", "IF SPLIT ELSE 1 ENDIF", "P2SH,STRICTENC", "DISABLED_OPCODE", "SPLIT disabled"], -["'abc' 1 1 0", "IF SPLIT ELSE 1 ENDIF", "P2SH,STRICTENC,MONOLITH_OPCODES", "DISABLED_OPCODE", "SPLIT disabled"], +["'ab' 'a' 'b' 1", "IF CAT ELSE 1 ENDIF EQUAL", "P2SH,STRICTENC,MONOLITH_OPCODES", "OK", "CAT enabled"], +["'ab' 'a' 'b'", "CAT EQUAL", "P2SH,STRICTENC,MONOLITH_OPCODES", "OK", "CAT enabled"], +["'ab' 'a' 'b' 1", "IF CAT ELSE 1 ENDIF EQUAL", "P2SH,STRICTENC,MONOLITH_OPCODES", "OK", "CAT enabled"], +[ + "'a' 'zngyivniryrgefgnvqwfwqplmramujzilzyrsdvinxfkfmuowdpuzycnzbupwwpzrfxsbyrhdlsyixyzysodseayvvrtbsfxtikrjwkbduulrjyjlwlaigomhyohsukawdwbrpuacdijzzgxhataguajvuopuktvtklwhsxqvzzfttpdgnxtnbpsiqecxurlczqmoxznlsuejvneiyejetcxlblzrydscnrbydnqytorstjtuzlbbtbyzfiniuehbisqnqhvexylhohjiyiknzgjowvobsrwcxyfowqcvakgdolwpltfcxtrhuysrrvtprzpsucgogsjapdkrbobpxccqgkdumskaleycwsbkabdkuukqiyizceduplmauszwjdzptvmthxocwrignxjogxsvrsjrrlecvdmazlpfkgmskiqqitrevuwiisvpxvkeypzaqjwwiozvmahmtvtjpbolwrymvzfstopzcexalirwbbcqgjvfjfuirrcnlgcfyqnafhh'", + "CAT", + "P2SH,STRICTENC,MONOLITH_OPCODES", "PUSH_SIZE", "CAT errors properly" +], +[ + "'' 'zngyivniryrgefgnvqwfwqplmramujzilzyrsdvinxfkfmuowdpuzycnzbupwwpzrfxsbyrhdlsyixyzysodseayvvrtbsfxtikrjwkbduulrjyjlwlaigomhyohsukawdwbrpuacdijzzgxhataguajvuopuktvtklwhsxqvzzfttpdgnxtnbpsiqecxurlczqmoxznlsuejvneiyejetcxlblzrydscnrbydnqytorstjtuzlbbtbyzfiniuehbisqnqhvexylhohjiyiknzgjowvobsrwcxyfowqcvakgdolwpltfcxtrhuysrrvtprzpsucgogsjapdkrbobpxccqgkdumskaleycwsbkabdkuukqiyizceduplmauszwjdzptvmthxocwrignxjogxsvrsjrrlecvdmazlpfkgmskiqqitrevuwiisvpxvkeypzaqjwwiozvmahmtvtjpbolwrymvzfstopzcexalirwbbcqgjvfjfuirrcnlgcfyqnafhh'", + "CAT", + "P2SH,STRICTENC,MONOLITH_OPCODES", "OK", "CAT null works" +], +[ "'zngyivniryrgefgnvqwfwqplmramujzilzyrsdvinxfkfmuowdpuzycnzbupwwpzrfxsbyrhdlsyixyzysodseayvvrtbsfxtikrjwkbduulrjyjlwlaigomhyohsukawdwbrpuacdijzzgxhataguajvuopuktvtklwhsxqvzzfttpdgnxtnbpsiqecxurlczqmoxznlsuejvneiyejetcxlblzrydscnrbydnqytorstjtuzlbbtbyzfiniuehbisqnqhvexylhohjiyiknzgjowvobsrwcxyfowqcvakgdolwpltfcxtrhuysrrvtprzpsucgogsjapdkrbobpxccqgkdumskaleycwsbkabdkuukqiyizceduplmauszwjdzptvmthxocwrignxjogxsvrsjrrlecvdmazlpfkgmskiqqitrevuwiisvpxvkeypzaqjwwiozvmahmtvtjpbolwrymvzfstopzcexalirwbbcqgjvfjfuirrcnlgcfyqnafhh' ''", + "CAT", + "P2SH,STRICTENC,MONOLITH_OPCODES", "OK", "CAT null and maxlength element works in other direction" +], +["'a' 0", "CAT 'a' EQUAL", "P2SH,STRICTENC,MONOLITH_OPCODES", "OK", "CAT with null produces a"], +["0 'a'", "CAT 'a' EQUAL", "P2SH,STRICTENC,MONOLITH_OPCODES", "OK", "CAT with null produces a"], +["0 0", "CAT 0 EQUAL", "P2SH,STRICTENC,MONOLITH_OPCODES", "OK", "CAT with null with null produces null"], +["0 0", "CAT DEPTH 1 EQUAL", "P2SH,STRICTENC,MONOLITH_OPCODES", "OK", "Stack depth correct"], +["'a'", "CAT", "P2SH,STRICTENC,MONOLITH_OPCODES", "INVALID_STACK_OPERATION", "Not enough operands"], +["", "CAT", "P2SH,STRICTENC,MONOLITH_OPCODES", "INVALID_STACK_OPERATION", "Not enough operands"], + + +["'abc' 1", "SPLIT", "P2SH,STRICTENC", "DISABLED_OPCODE", "SPLIT disabled"], +["'a' 'abc' 1", "SPLIT DROP EQUAL", "P2SH,STRICTENC,MONOLITH_OPCODES", "OK", "SPLIT enabled"], +["'abc' 1 0", "IF SPLIT ELSE 1 ENDIF", "P2SH,STRICTENC", "DISABLED_OPCODE", "SPLIT disabled"], +["'a' 'abc' 1 1", "IF SPLIT ELSE 1 ENDIF DROP EQUAL", "P2SH,STRICTENC,MONOLITH_OPCODES", "OK", "SPLIT enabled"], +["'bc' 'abc' 1", "SPLIT SWAP DROP EQUAL", "P2SH,STRICTENC,MONOLITH_OPCODES", "OK", "SPLIT enabled"], +["'bc' 'abc' 1 1", "IF SPLIT ELSE 1 ENDIF SWAP DROP EQUAL", "P2SH,STRICTENC,MONOLITH_OPCODES", "OK", "SPLIT enabled"], + +["1", "SPLIT", "P2SH,STRICTENC,MONOLITH_OPCODES", "INVALID_STACK_OPERATION", "SPLIT errors property on invalid stack"], +["1 'ab'", "SPLIT", "P2SH,STRICTENC,MONOLITH_OPCODES", "SPLIT_RANGE", "SPLIT errors property on invalid range"], +["'abcd' 'abcd'", "2 SPLIT CAT EQUAL", "P2SH,STRICTENC,MONOLITH_OPCODES", "OK", "CAT + SPLIT works"], +["'abcd' 'abcd'", "0 SPLIT CAT EQUAL", "P2SH,STRICTENC,MONOLITH_OPCODES", "OK", "CAT + SPLIT works"], +["'abcd' 'abcd'", "4 SPLIT CAT EQUAL", "P2SH,STRICTENC,MONOLITH_OPCODES", "OK", "CAT + SPLIT works"], +["'abcd' 'abcd'", "5 SPLIT CAT EQUAL", "P2SH,STRICTENC,MONOLITH_OPCODES", "SPLIT_RANGE", "SPLIT on invalid index"], +["'abcd' 'abcd'", "-5 SPLIT CAT EQUAL", "P2SH,STRICTENC,MONOLITH_OPCODES", "SPLIT_RANGE", "SPLIT on invalid index"], +["'ab' 0", "SPLIT 'ab' EQUALVERIFY 0 EQUAL", "P2SH,STRICTENC,MONOLITH_OPCODES", "OK", "SPLIT at zero outputs empty array and original element"], +["'ab' 2", "SPLIT 0 EQUALVERIFY 'ab' EQUAL", "P2SH,STRICTENC,MONOLITH_OPCODES", "OK", "SPLIT at zero outputs original element and empty array"], +["0 0", "SPLIT 0 EQUALVERIFY 0 EQUAL", "P2SH,STRICTENC,MONOLITH_OPCODES", "OK", "SPLIT OP_0 at zero outputs two empty arrays"], +["0 0", "SPLIT CAT 0 EQUAL", "P2SH,STRICTENC,MONOLITH_OPCODES", "OK", "CAT two empty arrays outputs a single empty array"], + +["0x02 0x0041", "1 SPLIT CAT 0x02 0x0041 EQUAL", "P2SH,STRICTENC,MONOLITH_OPCODES", "OK", "SPLIT + CAT of padded array works"], +["0x02 0x0000", "1 SPLIT CAT 0x02 0x0000 EQUAL", "P2SH,STRICTENC,MONOLITH_OPCODES", "OK", "SPLIT + CAT of zero array works"], + +["0 0", "SPLIT DEPTH 2 EQUAL", "P2SH,STRICTENC,MONOLITH_OPCODES", "OK", "Stack depth correct"], +["1", "SPLIT", "P2SH,STRICTENC,MONOLITH_OPCODES", "INVALID_STACK_OPERATION", "Not enough operands"], +["", "SPLIT", "P2SH,STRICTENC,MONOLITH_OPCODES", "INVALID_STACK_OPERATION", "Not enough operands"], + +["'ba'", "CAT", "P2SH,STRICTENC,MONOLITH_OPCODES", "INVALID_STACK_OPERATION", "CAT fails correctly"], + ["'abc' 2 0", "IF NUM2BIN ELSE 1 ENDIF", "P2SH,STRICTENC", "DISABLED_OPCODE", "NUM2BIN disabled"], ["'abc' 2 0", "IF NUM2BIN ELSE 1 ENDIF", "P2SH,STRICTENC,MONOLITH_OPCODES", "DISABLED_OPCODE", "NUM2BIN disabled"], ["'abc' 2 0", "IF BIN2NUM ELSE 1 ENDIF", "P2SH,STRICTENC", "DISABLED_OPCODE", "BIN2NUM disabled"], diff --git a/src/test/monolith_opcodes_string.cpp b/src/test/monolith_opcodes_string.cpp new file mode 100644 diff --git a/src/test/script_tests.cpp b/src/test/script_tests.cpp --- a/src/test/script_tests.cpp +++ b/src/test/script_tests.cpp @@ -62,6 +62,8 @@ {SCRIPT_ERR_STACK_SIZE, "STACK_SIZE"}, {SCRIPT_ERR_SIG_COUNT, "SIG_COUNT"}, {SCRIPT_ERR_PUBKEY_COUNT, "PUBKEY_COUNT"}, + {SCRIPT_ERR_PUBKEY_COUNT, "PUBKEY_COUNT"}, + {SCRIPT_ERR_INVALID_SPLIT_RANGE, "SPLIT_RANGE"}, {SCRIPT_ERR_VERIFY, "VERIFY"}, {SCRIPT_ERR_EQUALVERIFY, "EQUALVERIFY"}, {SCRIPT_ERR_CHECKMULTISIGVERIFY, "CHECKMULTISIGVERIFY"},