From 31ec6ae92a5d9910a26d90a6ff20bab27dee5826 Mon Sep 17 00:00:00 2001 From: Antoine Poinsot Date: Sat, 25 Sep 2021 15:21:57 +0200 Subject: [PATCH 1/7] script: make IsPushdataOp non-static We'll need it for Miniscript --- src/script/standard.cpp | 5 ----- src/script/standard.h | 5 +++++ 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/script/standard.cpp b/src/script/standard.cpp index b77c78769fd..e25155d3dd7 100644 --- a/src/script/standard.cpp +++ b/src/script/standard.cpp @@ -91,11 +91,6 @@ static constexpr bool IsSmallInteger(opcodetype opcode) return opcode >= OP_1 && opcode <= OP_16; } -static constexpr bool IsPushdataOp(opcodetype opcode) -{ - return opcode > OP_FALSE && opcode <= OP_PUSHDATA4; -} - /** Retrieve a minimally-encoded number in range [min,max] from an (opcode, data) pair, * whether it's OP_n or through a push. */ static std::optional GetScriptNumber(opcodetype opcode, valtype data, int min, int max) diff --git a/src/script/standard.h b/src/script/standard.h index 75bfe2db380..f0b143c52bf 100644 --- a/src/script/standard.h +++ b/src/script/standard.h @@ -162,6 +162,11 @@ bool IsValidDestination(const CTxDestination& dest); /** Get the name of a TxoutType as a string */ std::string GetTxnOutputType(TxoutType t); +constexpr bool IsPushdataOp(opcodetype opcode) +{ + return opcode > OP_FALSE && opcode <= OP_PUSHDATA4; +} + /** * Parse a scriptPubKey and identify script type for standard scripts. If * successful, returns script type and parsed pubkeys or hashes, depending on From f4e289f384efdda6c3f56e1e1c30820a91ac2612 Mon Sep 17 00:00:00 2001 From: Antoine Poinsot Date: Mon, 27 Sep 2021 13:12:03 +0200 Subject: [PATCH 2/7] script: move CheckMinimalPush from interpreter to script.h It is used by Miniscript. --- src/script/interpreter.cpp | 25 ------------------------- src/script/interpreter.h | 2 -- src/script/script.cpp | 25 +++++++++++++++++++++++++ src/script/script.h | 2 ++ 4 files changed, 27 insertions(+), 27 deletions(-) diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp index 11b1a1c8875..5b630d77407 100644 --- a/src/script/interpreter.cpp +++ b/src/script/interpreter.cpp @@ -225,31 +225,6 @@ bool static CheckPubKeyEncoding(const valtype &vchPubKey, unsigned int flags, co return true; } -bool CheckMinimalPush(const valtype& data, opcodetype opcode) { - // Excludes OP_1NEGATE, OP_1-16 since they are by definition minimal - assert(0 <= opcode && opcode <= OP_PUSHDATA4); - if (data.size() == 0) { - // Should have used OP_0. - return opcode == OP_0; - } else if (data.size() == 1 && data[0] >= 1 && data[0] <= 16) { - // Should have used OP_1 .. OP_16. - return false; - } else if (data.size() == 1 && data[0] == 0x81) { - // Should have used OP_1NEGATE. - return false; - } else if (data.size() <= 75) { - // Must have used a direct push (opcode indicating number of bytes pushed + those bytes). - return opcode == data.size(); - } else if (data.size() <= 255) { - // Must have used OP_PUSHDATA. - return opcode == OP_PUSHDATA1; - } else if (data.size() <= 65535) { - // Must have used OP_PUSHDATA2. - return opcode == OP_PUSHDATA2; - } - return true; -} - int FindAndDelete(CScript& script, const CScript& b) { int nFound = 0; diff --git a/src/script/interpreter.h b/src/script/interpreter.h index cf1953ad223..fbd904fb7b6 100644 --- a/src/script/interpreter.h +++ b/src/script/interpreter.h @@ -344,8 +344,6 @@ bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const C size_t CountWitnessSigOps(const CScript& scriptSig, const CScript& scriptPubKey, const CScriptWitness* witness, unsigned int flags); -bool CheckMinimalPush(const std::vector& data, opcodetype opcode); - int FindAndDelete(CScript& script, const CScript& b); #endif // BITCOIN_SCRIPT_INTERPRETER_H diff --git a/src/script/script.cpp b/src/script/script.cpp index 9a6419088b4..88b4bc2f44a 100644 --- a/src/script/script.cpp +++ b/src/script/script.cpp @@ -339,3 +339,28 @@ bool IsOpSuccess(const opcodetype& opcode) (opcode >= 141 && opcode <= 142) || (opcode >= 149 && opcode <= 153) || (opcode >= 187 && opcode <= 254); } + +bool CheckMinimalPush(const std::vector& data, opcodetype opcode) { + // Excludes OP_1NEGATE, OP_1-16 since they are by definition minimal + assert(0 <= opcode && opcode <= OP_PUSHDATA4); + if (data.size() == 0) { + // Should have used OP_0. + return opcode == OP_0; + } else if (data.size() == 1 && data[0] >= 1 && data[0] <= 16) { + // Should have used OP_1 .. OP_16. + return false; + } else if (data.size() == 1 && data[0] == 0x81) { + // Should have used OP_1NEGATE. + return false; + } else if (data.size() <= 75) { + // Must have used a direct push (opcode indicating number of bytes pushed + those bytes). + return opcode == data.size(); + } else if (data.size() <= 255) { + // Must have used OP_PUSHDATA. + return opcode == OP_PUSHDATA1; + } else if (data.size() <= 65535) { + // Must have used OP_PUSHDATA2. + return opcode == OP_PUSHDATA2; + } + return true; +} diff --git a/src/script/script.h b/src/script/script.h index a89c9873061..c722374113e 100644 --- a/src/script/script.h +++ b/src/script/script.h @@ -576,4 +576,6 @@ struct CScriptWitness /** Test for OP_SUCCESSx opcodes as defined by BIP342. */ bool IsOpSuccess(const opcodetype& opcode); +bool CheckMinimalPush(const std::vector& data, opcodetype opcode); + #endif // BITCOIN_SCRIPT_SCRIPT_H From 4fe29368c0ded0e62f437cab3a7c904f7fd3ad67 Mon Sep 17 00:00:00 2001 From: Antoine Poinsot Date: Thu, 17 Mar 2022 13:36:58 +0100 Subject: [PATCH 3/7] script: expose getter for CScriptNum, add a BuildScript helper Some prep work for Miniscript. BuildScript is an efficient way to build Scripts in a generic manner (by concatenating OPs, data, and other Scripts). Co-Authored-By: Pieter Wuille --- src/script/script.h | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/src/script/script.h b/src/script/script.h index c722374113e..3b799ad6377 100644 --- a/src/script/script.h +++ b/src/script/script.h @@ -335,6 +335,8 @@ public: return m_value; } + int64_t GetInt64() const { return m_value; } + std::vector getvch() const { return serialize(m_value); @@ -578,4 +580,29 @@ bool IsOpSuccess(const opcodetype& opcode); bool CheckMinimalPush(const std::vector& data, opcodetype opcode); +/** Build a script by concatenating other scripts, or any argument accepted by CScript::operator<<. */ +template +CScript BuildScript(Ts&&... inputs) +{ + CScript ret; + int cnt{0}; + + ([&ret, &cnt] (Ts&& input) { + cnt++; + if constexpr (std::is_same_v>, CScript>) { + // If it is a CScript, extend ret with it. Move or copy the first element instead. + if (cnt == 0) { + ret = std::forward(input); + } else { + ret.insert(ret.end(), input.begin(), input.end()); + } + } else { + // Otherwise invoke CScript::operator<<. + ret << input; + } + } (std::forward(inputs)), ...); + + return ret; +} + #endif // BITCOIN_SCRIPT_SCRIPT_H From 1ddaa66eae67b102f5e37d212d366a5dcad4aa26 Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Wed, 28 Aug 2019 16:23:03 -0700 Subject: [PATCH 4/7] Miniscript: type system, script creation, text notation, tests More information about Miniscript can be found at https://bitcoin.sipa.be/miniscript/ (the website source is hosted at https://github.com/sipa/miniscript/). This commit defines all fragments, their composition, parsing from string representation and conversion to Script. Co-Authored-By: Antoine Poinsot Co-Authored-By: Sanket Kanjalkar Co-Authored-By: Samuel Dobson --- src/Makefile.am | 2 + src/Makefile.test.include | 1 + src/script/miniscript.cpp | 295 ++++++++++ src/script/miniscript.h | 1020 +++++++++++++++++++++++++++++++++ src/test/miniscript_tests.cpp | 247 ++++++++ 5 files changed, 1565 insertions(+) create mode 100644 src/script/miniscript.cpp create mode 100644 src/script/miniscript.h create mode 100644 src/test/miniscript_tests.cpp diff --git a/src/Makefile.am b/src/Makefile.am index e940736b71a..651722cfd05 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -221,6 +221,7 @@ BITCOIN_CORE_H = \ scheduler.h \ script/descriptor.h \ script/keyorigin.h \ + script/miniscript.h \ script/sigcache.h \ script/sign.h \ script/signingprovider.h \ @@ -589,6 +590,7 @@ libbitcoin_common_a_SOURCES = \ rpc/util.cpp \ scheduler.cpp \ script/descriptor.cpp \ + script/miniscript.cpp \ script/sign.cpp \ script/signingprovider.cpp \ script/standard.cpp \ diff --git a/src/Makefile.test.include b/src/Makefile.test.include index a7505b9bcf5..23bb967c20c 100644 --- a/src/Makefile.test.include +++ b/src/Makefile.test.include @@ -103,6 +103,7 @@ BITCOIN_TESTS =\ test/merkle_tests.cpp \ test/merkleblock_tests.cpp \ test/miner_tests.cpp \ + test/miniscript_tests.cpp \ test/minisketch_tests.cpp \ test/multisig_tests.cpp \ test/net_peer_eviction_tests.cpp \ diff --git a/src/script/miniscript.cpp b/src/script/miniscript.cpp new file mode 100644 index 00000000000..8074be6cde1 --- /dev/null +++ b/src/script/miniscript.cpp @@ -0,0 +1,295 @@ +// Copyright (c) 2019 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include +#include +#include