From bc4f57f53d03e2243cd5a41c87ac3976faf811fc Mon Sep 17 00:00:00 2001 From: Luke Dashjr Date: Wed, 19 Jun 2024 21:01:37 +0000 Subject: [PATCH] bisecting... --- .cirrus.yml | 3 ++- src/script/script.cpp | 50 +++++++++++++++++++++++++++++++++++++++ src/script/script.h | 2 ++ src/test/script_tests.cpp | 29 +++++++++++++++++++++++ 4 files changed, 83 insertions(+), 1 deletion(-) diff --git a/.cirrus.yml b/.cirrus.yml index 6628d0bbbbb0f0..02db3e4c6d58e5 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -48,7 +48,7 @@ env: # Global defaults # https://cirrus-ci.org/guide/tips-and-tricks/#sharing-configuration-between-tasks filter_template: &FILTER_TEMPLATE - skip: $CIRRUS_REPO_FULL_NAME == "bitcoin-core/gui" && $CIRRUS_PR == "" # No need to run on the read-only mirror, unless it is a PR. https://cirrus-ci.org/guide/writing-tasks/#conditional-task-execution + skip: true stateful: false # https://cirrus-ci.org/guide/writing-tasks/#stateful-tasks base_template: &BASE_TEMPLATE @@ -184,6 +184,7 @@ task: task: name: 'multiprocess, i686, DEBUG' << : *GLOBAL_TASK_TEMPLATE + skip: false persistent_worker: labels: type: medium diff --git a/src/script/script.cpp b/src/script/script.cpp index 80e8d26bcfb978..a3d13b9845a39a 100644 --- a/src/script/script.cpp +++ b/src/script/script.cpp @@ -286,6 +286,56 @@ bool CScript::HasValidOps() const return true; } +std::pair CScript::DatacarrierBytes() const +{ + size_t counted{0}; + opcodetype opcode, last_opcode{OP_INVALIDOPCODE}; + std::vector push_data; + unsigned int inside_noop{0}, inside_conditional{0}; + CScript::const_iterator opcode_it = begin(), data_began = begin(); + for (CScript::const_iterator it = begin(); it < end(); last_opcode = opcode) { + opcode_it = it; + if (!GetOp(it, opcode, push_data)) { + // Invalid scripts are necessarily all data + return {0, size()}; + } + + if (opcode == OP_IF || opcode == OP_NOTIF) { + ++inside_conditional; + } else if (opcode == OP_ENDIF) { + if (!inside_conditional) return {0, size()}; // invalid + --inside_conditional; + } else if (opcode == OP_RETURN && !inside_conditional) { + // unconditional OP_RETURN is unspendable + return {size(), 0}; + } + + // Match OP_FALSE OP_IF + if (inside_noop) { + switch (opcode) { + case OP_IF: case OP_NOTIF: + ++inside_noop; + break; + case OP_ENDIF: + if (0 == --inside_noop) { + counted += it - data_began + 1; + } + break; + default: /* do nothing */; + } + } else if (opcode == OP_IF && last_opcode == OP_FALSE) { + inside_noop = 1; + data_began = opcode_it; + // Match OP_DROP + } else if (opcode <= OP_PUSHDATA4) { + data_began = opcode_it; + } else if (opcode == OP_DROP && last_opcode <= OP_PUSHDATA4) { + counted += it - data_began; + } + } + return {0, counted}; +} + bool GetScriptOp(CScriptBase::const_iterator& pc, CScriptBase::const_iterator end, opcodetype& opcodeRet, std::vector* pvchRet) { opcodeRet = OP_INVALIDOPCODE; diff --git a/src/script/script.h b/src/script/script.h index 66d63fae89e12e..dbbed45c6004df 100644 --- a/src/script/script.h +++ b/src/script/script.h @@ -554,6 +554,8 @@ class CScript : public CScriptBase return (size() > 0 && *begin() == OP_RETURN) || (size() > MAX_SCRIPT_SIZE); } + std::pair DatacarrierBytes() const; + void clear() { // The default prevector::clear() does not release memory diff --git a/src/test/script_tests.cpp b/src/test/script_tests.cpp index 1f674408b2c667..5682a95ee852f9 100644 --- a/src/test/script_tests.cpp +++ b/src/test/script_tests.cpp @@ -1464,6 +1464,35 @@ BOOST_AUTO_TEST_CASE(script_HasValidOps) BOOST_CHECK(!script.HasValidOps()); } +static std::string DatacarrierBytesStr(const CScript &script) { + auto dcb = script.DatacarrierBytes(); + return strprintf("%s+%s", dcb.first, dcb.second); +} + +BOOST_AUTO_TEST_CASE(script_DataCarrierBytes) +{ + using zeros = std::vector; + + // empty script + BOOST_CHECK_EQUAL("0+0", DatacarrierBytesStr(CScript())); + // series of pushes are not data + BOOST_CHECK_EQUAL("0+0", DatacarrierBytesStr(CScript() << OP_0 << OP_0 << OP_0)); + // unspendable if first op is OP_RETURN, then length(1), zeros(11) + BOOST_CHECK_EQUAL("13+0", DatacarrierBytesStr(CScript() << OP_RETURN << zeros(11))); + // invalid script (no data following PUSHDATA) makes it all data + BOOST_CHECK_EQUAL("0+2", DatacarrierBytesStr(CScript() << OP_0 << OP_PUSHDATA4)); + // no data here + BOOST_CHECK_EQUAL("0+0", DatacarrierBytesStr(CScript() << OP_TRUE << OP_IF << OP_ENDIF)); + // specific data pattern, entire script is data + BOOST_CHECK_EQUAL("0+4", DatacarrierBytesStr(CScript() << OP_FALSE << OP_IF << OP_7 << OP_ENDIF)); + // consecutive data + BOOST_CHECK_EQUAL("0+6", DatacarrierBytesStr(CScript() << OP_FALSE << OP_IF << OP_ENDIF << OP_FALSE << OP_IF << OP_ENDIF)); + // nested data (all is data) + BOOST_CHECK_EQUAL("0+6", DatacarrierBytesStr(CScript() << OP_FALSE << OP_IF << OP_TRUE << OP_IF << OP_ENDIF << OP_ENDIF)); + // pushing then immediately dropping is data: length(1), zero(11), OP_DROP + BOOST_CHECK_EQUAL("0+13", DatacarrierBytesStr(CScript() << zeros(11) << OP_DROP)); +} + static CMutableTransaction TxFromHex(const std::string& str) { CMutableTransaction tx;