diff options
Diffstat (limited to 'src/script')
-rw-r--r-- | src/script/interpreter.cpp | 63 | ||||
-rw-r--r-- | src/script/interpreter.h | 29 | ||||
-rw-r--r-- | src/script/ismine.cpp | 100 | ||||
-rw-r--r-- | src/script/ismine.h | 14 | ||||
-rw-r--r-- | src/script/script.cpp | 5 | ||||
-rw-r--r-- | src/script/script.h | 7 | ||||
-rw-r--r-- | src/script/script_error.cpp | 4 | ||||
-rw-r--r-- | src/script/script_error.h | 4 | ||||
-rw-r--r-- | src/script/sign.cpp | 408 | ||||
-rw-r--r-- | src/script/sign.h | 45 | ||||
-rw-r--r-- | src/script/standard.cpp | 160 | ||||
-rw-r--r-- | src/script/standard.h | 1 |
12 files changed, 424 insertions, 416 deletions
diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp index e0d193fa38..4b982d647d 100644 --- a/src/script/interpreter.cpp +++ b/src/script/interpreter.cpp @@ -336,6 +336,10 @@ bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript& opcode == OP_RSHIFT) 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)) + return set_error(serror, SCRIPT_ERR_OP_CODESEPARATOR); + if (fExec && 0 <= opcode && opcode <= OP_PUSHDATA4) { if (fRequireMinimal && !CheckMinimalPush(vchPushValue, opcode)) { return set_error(serror, SCRIPT_ERR_MINIMALDATA); @@ -899,6 +903,9 @@ bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript& case OP_CODESEPARATOR: { + // If SCRIPT_VERIFY_CONST_SCRIPTCODE flag is set, use of OP_CODESEPARATOR is rejected in pre-segwit + // script, even in an unexecuted branch (this is checked above the opcode case statement). + // Hash starts after the code separator pbegincodehash = pc; } @@ -919,7 +926,9 @@ bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript& // Drop the signature in pre-segwit scripts but not segwit scripts if (sigversion == SigVersion::BASE) { - FindAndDelete(scriptCode, CScript(vchSig)); + int found = FindAndDelete(scriptCode, CScript(vchSig)); + if (found > 0 && (flags & SCRIPT_VERIFY_CONST_SCRIPTCODE)) + return set_error(serror, SCRIPT_ERR_SIG_FINDANDDELETE); } if (!CheckSignatureEncoding(vchSig, flags, serror) || !CheckPubKeyEncoding(vchPubKey, flags, sigversion, serror)) { @@ -983,7 +992,9 @@ bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript& { valtype& vchSig = stacktop(-isig-k); if (sigversion == SigVersion::BASE) { - FindAndDelete(scriptCode, CScript(vchSig)); + int found = FindAndDelete(scriptCode, CScript(vchSig)); + if (found > 0 && (flags & SCRIPT_VERIFY_CONST_SCRIPTCODE)) + return set_error(serror, SCRIPT_ERR_SIG_FINDANDDELETE); } } @@ -1078,9 +1089,11 @@ namespace { * Wrapper that serializes like CTransaction, but with the modifications * required for the signature hash done in-place */ -class CTransactionSignatureSerializer { +template <class T> +class CTransactionSignatureSerializer +{ private: - const CTransaction& txTo; //!< reference to the spending transaction (the one being serialized) + const T& txTo; //!< reference to the spending transaction (the one being serialized) const CScript& scriptCode; //!< output script being consumed const unsigned int nIn; //!< input index of txTo being signed const bool fAnyoneCanPay; //!< whether the hashtype has the SIGHASH_ANYONECANPAY flag set @@ -1088,7 +1101,7 @@ private: const bool fHashNone; //!< whether the hashtype is SIGHASH_NONE public: - CTransactionSignatureSerializer(const CTransaction &txToIn, const CScript &scriptCodeIn, unsigned int nInIn, int nHashTypeIn) : + CTransactionSignatureSerializer(const T& txToIn, const CScript& scriptCodeIn, unsigned int nInIn, int nHashTypeIn) : txTo(txToIn), scriptCode(scriptCodeIn), nIn(nInIn), fAnyoneCanPay(!!(nHashTypeIn & SIGHASH_ANYONECANPAY)), fHashSingle((nHashTypeIn & 0x1f) == SIGHASH_SINGLE), @@ -1169,7 +1182,9 @@ public: } }; -uint256 GetPrevoutHash(const CTransaction& txTo) { +template <class T> +uint256 GetPrevoutHash(const T& txTo) +{ CHashWriter ss(SER_GETHASH, 0); for (const auto& txin : txTo.vin) { ss << txin.prevout; @@ -1177,7 +1192,9 @@ uint256 GetPrevoutHash(const CTransaction& txTo) { return ss.GetHash(); } -uint256 GetSequenceHash(const CTransaction& txTo) { +template <class T> +uint256 GetSequenceHash(const T& txTo) +{ CHashWriter ss(SER_GETHASH, 0); for (const auto& txin : txTo.vin) { ss << txin.nSequence; @@ -1185,7 +1202,9 @@ uint256 GetSequenceHash(const CTransaction& txTo) { return ss.GetHash(); } -uint256 GetOutputsHash(const CTransaction& txTo) { +template <class T> +uint256 GetOutputsHash(const T& txTo) +{ CHashWriter ss(SER_GETHASH, 0); for (const auto& txout : txTo.vout) { ss << txout; @@ -1195,7 +1214,8 @@ uint256 GetOutputsHash(const CTransaction& txTo) { } // namespace -PrecomputedTransactionData::PrecomputedTransactionData(const CTransaction& txTo) +template <class T> +PrecomputedTransactionData::PrecomputedTransactionData(const T& txTo) { // Cache is calculated only for transactions with witness if (txTo.HasWitness()) { @@ -1206,7 +1226,12 @@ PrecomputedTransactionData::PrecomputedTransactionData(const CTransaction& txTo) } } -uint256 SignatureHash(const CScript& scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType, const CAmount& amount, SigVersion sigversion, const PrecomputedTransactionData* cache) +// explicit instantiation +template PrecomputedTransactionData::PrecomputedTransactionData(const CTransaction& txTo); +template PrecomputedTransactionData::PrecomputedTransactionData(const CMutableTransaction& txTo); + +template <class T> +uint256 SignatureHash(const CScript& scriptCode, const T& txTo, unsigned int nIn, int nHashType, const CAmount& amount, SigVersion sigversion, const PrecomputedTransactionData* cache) { assert(nIn < txTo.vin.size()); @@ -1267,7 +1292,7 @@ uint256 SignatureHash(const CScript& scriptCode, const CTransaction& txTo, unsig } // Wrapper to serialize only the necessary parts of the transaction being signed - CTransactionSignatureSerializer txTmp(txTo, scriptCode, nIn, nHashType); + CTransactionSignatureSerializer<T> txTmp(txTo, scriptCode, nIn, nHashType); // Serialize and hash CHashWriter ss(SER_GETHASH, 0); @@ -1275,12 +1300,14 @@ uint256 SignatureHash(const CScript& scriptCode, const CTransaction& txTo, unsig return ss.GetHash(); } -bool TransactionSignatureChecker::VerifySignature(const std::vector<unsigned char>& vchSig, const CPubKey& pubkey, const uint256& sighash) const +template <class T> +bool GenericTransactionSignatureChecker<T>::VerifySignature(const std::vector<unsigned char>& vchSig, const CPubKey& pubkey, const uint256& sighash) const { return pubkey.Verify(sighash, vchSig); } -bool TransactionSignatureChecker::CheckSig(const std::vector<unsigned char>& vchSigIn, const std::vector<unsigned char>& vchPubKey, const CScript& scriptCode, SigVersion sigversion) const +template <class T> +bool GenericTransactionSignatureChecker<T>::CheckSig(const std::vector<unsigned char>& vchSigIn, const std::vector<unsigned char>& vchPubKey, const CScript& scriptCode, SigVersion sigversion) const { CPubKey pubkey(vchPubKey); if (!pubkey.IsValid()) @@ -1301,7 +1328,8 @@ bool TransactionSignatureChecker::CheckSig(const std::vector<unsigned char>& vch return true; } -bool TransactionSignatureChecker::CheckLockTime(const CScriptNum& nLockTime) const +template <class T> +bool GenericTransactionSignatureChecker<T>::CheckLockTime(const CScriptNum& nLockTime) const { // There are two kinds of nLockTime: lock-by-blockheight // and lock-by-blocktime, distinguished by whether @@ -1337,7 +1365,8 @@ bool TransactionSignatureChecker::CheckLockTime(const CScriptNum& nLockTime) con return true; } -bool TransactionSignatureChecker::CheckSequence(const CScriptNum& nSequence) const +template <class T> +bool GenericTransactionSignatureChecker<T>::CheckSequence(const CScriptNum& nSequence) const { // Relative lock times are supported by comparing the passed // in operand to the sequence number of the input. @@ -1383,6 +1412,10 @@ bool TransactionSignatureChecker::CheckSequence(const CScriptNum& nSequence) con return true; } +// explicit instantiation +template class GenericTransactionSignatureChecker<CTransaction>; +template class GenericTransactionSignatureChecker<CMutableTransaction>; + static bool VerifyWitnessProgram(const CScriptWitness& witness, int witversion, const std::vector<unsigned char>& program, unsigned int flags, const BaseSignatureChecker& checker, ScriptError* serror) { std::vector<std::vector<unsigned char> > stack; diff --git a/src/script/interpreter.h b/src/script/interpreter.h index 50c747900a..2d21aa81df 100644 --- a/src/script/interpreter.h +++ b/src/script/interpreter.h @@ -111,6 +111,10 @@ enum // Public keys in segregated witness scripts must be compressed // SCRIPT_VERIFY_WITNESS_PUBKEYTYPE = (1U << 15), + + // Making OP_CODESEPARATOR and FindAndDelete fail any non-segwit scripts + // + SCRIPT_VERIFY_CONST_SCRIPTCODE = (1U << 16), }; bool CheckSignatureEncoding(const std::vector<unsigned char> &vchSig, unsigned int flags, ScriptError* serror); @@ -120,7 +124,8 @@ struct PrecomputedTransactionData uint256 hashPrevouts, hashSequence, hashOutputs; bool ready = false; - explicit PrecomputedTransactionData(const CTransaction& tx); + template <class T> + explicit PrecomputedTransactionData(const T& tx); }; enum class SigVersion @@ -133,7 +138,8 @@ enum class SigVersion static constexpr size_t WITNESS_V0_SCRIPTHASH_SIZE = 32; static constexpr size_t WITNESS_V0_KEYHASH_SIZE = 20; -uint256 SignatureHash(const CScript &scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType, const CAmount& amount, SigVersion sigversion, const PrecomputedTransactionData* cache = nullptr); +template <class T> +uint256 SignatureHash(const CScript& scriptCode, const T& txTo, unsigned int nIn, int nHashType, const CAmount& amount, SigVersion sigversion, const PrecomputedTransactionData* cache = nullptr); class BaseSignatureChecker { @@ -156,10 +162,11 @@ public: virtual ~BaseSignatureChecker() {} }; -class TransactionSignatureChecker : public BaseSignatureChecker +template <class T> +class GenericTransactionSignatureChecker : public BaseSignatureChecker { private: - const CTransaction* txTo; + const T* txTo; unsigned int nIn; const CAmount amount; const PrecomputedTransactionData* txdata; @@ -168,21 +175,15 @@ protected: virtual bool VerifySignature(const std::vector<unsigned char>& vchSig, const CPubKey& vchPubKey, const uint256& sighash) const; public: - TransactionSignatureChecker(const CTransaction* txToIn, unsigned int nInIn, const CAmount& amountIn) : txTo(txToIn), nIn(nInIn), amount(amountIn), txdata(nullptr) {} - TransactionSignatureChecker(const CTransaction* txToIn, unsigned int nInIn, const CAmount& amountIn, const PrecomputedTransactionData& txdataIn) : txTo(txToIn), nIn(nInIn), amount(amountIn), txdata(&txdataIn) {} + GenericTransactionSignatureChecker(const T* txToIn, unsigned int nInIn, const CAmount& amountIn) : txTo(txToIn), nIn(nInIn), amount(amountIn), txdata(nullptr) {} + GenericTransactionSignatureChecker(const T* txToIn, unsigned int nInIn, const CAmount& amountIn, const PrecomputedTransactionData& txdataIn) : txTo(txToIn), nIn(nInIn), amount(amountIn), txdata(&txdataIn) {} bool CheckSig(const std::vector<unsigned char>& scriptSig, const std::vector<unsigned char>& vchPubKey, const CScript& scriptCode, SigVersion sigversion) const override; bool CheckLockTime(const CScriptNum& nLockTime) const override; bool CheckSequence(const CScriptNum& nSequence) const override; }; -class MutableTransactionSignatureChecker : public TransactionSignatureChecker -{ -private: - const CTransaction txTo; - -public: - MutableTransactionSignatureChecker(const CMutableTransaction* txToIn, unsigned int nInIn, const CAmount& amountIn) : TransactionSignatureChecker(&txTo, nInIn, amountIn), txTo(*txToIn) {} -}; +using TransactionSignatureChecker = GenericTransactionSignatureChecker<CTransaction>; +using MutableTransactionSignatureChecker = GenericTransactionSignatureChecker<CMutableTransaction>; bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript& script, unsigned int flags, const BaseSignatureChecker& checker, SigVersion sigversion, ScriptError* error = nullptr); bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const CScriptWitness* witness, unsigned int flags, const BaseSignatureChecker& checker, ScriptError* serror = nullptr); diff --git a/src/script/ismine.cpp b/src/script/ismine.cpp index fefa02fdef..8c26866483 100644 --- a/src/script/ismine.cpp +++ b/src/script/ismine.cpp @@ -28,6 +28,19 @@ enum class IsMineSigVersion WITNESS_V0 = 2 //! P2WSH witness script execution }; +/** + * This is an internal representation of isminetype + invalidity. + * Its order is significant, as we return the max of all explored + * possibilities. + */ +enum class IsMineResult +{ + NO = 0, //! Not ours + WATCH_ONLY = 1, //! Included in watch-only balance + SPENDABLE = 2, //! Included in all balances + INVALID = 3, //! Not spendable by anyone (uncompressed pubkey in segwit, P2SH inside P2SH or witness, witness inside witness) +}; + bool PermitsUncompressed(IsMineSigVersion sigversion) { return sigversion == IsMineSigVersion::TOP || sigversion == IsMineSigVersion::P2SH; @@ -42,17 +55,13 @@ bool HaveKeys(const std::vector<valtype>& pubkeys, const CKeyStore& keystore) return true; } -isminetype IsMineInner(const CKeyStore& keystore, const CScript& scriptPubKey, bool& isInvalid, IsMineSigVersion sigversion) +IsMineResult IsMineInner(const CKeyStore& keystore, const CScript& scriptPubKey, IsMineSigVersion sigversion) { - isInvalid = false; + IsMineResult ret = IsMineResult::NO; std::vector<valtype> vSolutions; txnouttype whichType; - if (!Solver(scriptPubKey, whichType, vSolutions)) { - if (keystore.HaveWatchOnly(scriptPubKey)) - return ISMINE_WATCH_UNSOLVABLE; - return ISMINE_NO; - } + Solver(scriptPubKey, whichType, vSolutions); CKeyID keyID; switch (whichType) @@ -64,23 +73,25 @@ isminetype IsMineInner(const CKeyStore& keystore, const CScript& scriptPubKey, b case TX_PUBKEY: keyID = CPubKey(vSolutions[0]).GetID(); if (!PermitsUncompressed(sigversion) && vSolutions[0].size() != 33) { - isInvalid = true; - return ISMINE_NO; + return IsMineResult::INVALID; + } + if (keystore.HaveKey(keyID)) { + ret = std::max(ret, IsMineResult::SPENDABLE); } - if (keystore.HaveKey(keyID)) - return ISMINE_SPENDABLE; break; case TX_WITNESS_V0_KEYHASH: { + if (sigversion == IsMineSigVersion::WITNESS_V0) { + // P2WPKH inside P2WSH is invalid. + return IsMineResult::INVALID; + } if (sigversion == IsMineSigVersion::TOP && !keystore.HaveCScript(CScriptID(CScript() << OP_0 << vSolutions[0]))) { // We do not support bare witness outputs unless the P2SH version of it would be // acceptable as well. This protects against matching before segwit activates. // This also applies to the P2WSH case. break; } - isminetype ret = IsMineInner(keystore, GetScriptForDestination(CKeyID(uint160(vSolutions[0]))), isInvalid, IsMineSigVersion::WITNESS_V0); - if (ret == ISMINE_SPENDABLE || ret == ISMINE_WATCH_SOLVABLE || (ret == ISMINE_NO && isInvalid)) - return ret; + ret = std::max(ret, IsMineInner(keystore, GetScriptForDestination(CKeyID(uint160(vSolutions[0]))), IsMineSigVersion::WITNESS_V0)); break; } case TX_PUBKEYHASH: @@ -88,26 +99,32 @@ isminetype IsMineInner(const CKeyStore& keystore, const CScript& scriptPubKey, b if (!PermitsUncompressed(sigversion)) { CPubKey pubkey; if (keystore.GetPubKey(keyID, pubkey) && !pubkey.IsCompressed()) { - isInvalid = true; - return ISMINE_NO; + return IsMineResult::INVALID; } } - if (keystore.HaveKey(keyID)) - return ISMINE_SPENDABLE; + if (keystore.HaveKey(keyID)) { + ret = std::max(ret, IsMineResult::SPENDABLE); + } break; case TX_SCRIPTHASH: { + if (sigversion != IsMineSigVersion::TOP) { + // P2SH inside P2WSH or P2SH is invalid. + return IsMineResult::INVALID; + } CScriptID scriptID = CScriptID(uint160(vSolutions[0])); CScript subscript; if (keystore.GetCScript(scriptID, subscript)) { - isminetype ret = IsMineInner(keystore, subscript, isInvalid, IsMineSigVersion::P2SH); - if (ret == ISMINE_SPENDABLE || ret == ISMINE_WATCH_SOLVABLE || (ret == ISMINE_NO && isInvalid)) - return ret; + ret = std::max(ret, IsMineInner(keystore, subscript, IsMineSigVersion::P2SH)); } break; } case TX_WITNESS_V0_SCRIPTHASH: { + if (sigversion == IsMineSigVersion::WITNESS_V0) { + // P2WSH inside P2WSH is invalid. + return IsMineResult::INVALID; + } if (sigversion == IsMineSigVersion::TOP && !keystore.HaveCScript(CScriptID(CScript() << OP_0 << vSolutions[0]))) { break; } @@ -116,9 +133,7 @@ isminetype IsMineInner(const CKeyStore& keystore, const CScript& scriptPubKey, b CScriptID scriptID = CScriptID(hash); CScript subscript; if (keystore.GetCScript(scriptID, subscript)) { - isminetype ret = IsMineInner(keystore, subscript, isInvalid, IsMineSigVersion::WITNESS_V0); - if (ret == ISMINE_SPENDABLE || ret == ISMINE_WATCH_SOLVABLE || (ret == ISMINE_NO && isInvalid)) - return ret; + ret = std::max(ret, IsMineInner(keystore, subscript, IsMineSigVersion::WITNESS_V0)); } break; } @@ -126,7 +141,9 @@ isminetype IsMineInner(const CKeyStore& keystore, const CScript& scriptPubKey, b case TX_MULTISIG: { // Never treat bare multisig outputs as ours (they can still be made watchonly-though) - if (sigversion == IsMineSigVersion::TOP) break; + if (sigversion == IsMineSigVersion::TOP) { + break; + } // Only consider transactions "mine" if we own ALL the // keys involved. Multi-signature transactions that are @@ -137,36 +154,37 @@ isminetype IsMineInner(const CKeyStore& keystore, const CScript& scriptPubKey, b if (!PermitsUncompressed(sigversion)) { for (size_t i = 0; i < keys.size(); i++) { if (keys[i].size() != 33) { - isInvalid = true; - return ISMINE_NO; + return IsMineResult::INVALID; } } } - if (HaveKeys(keys, keystore)) - return ISMINE_SPENDABLE; + if (HaveKeys(keys, keystore)) { + ret = std::max(ret, IsMineResult::SPENDABLE); + } break; } } - if (keystore.HaveWatchOnly(scriptPubKey)) { - // TODO: This could be optimized some by doing some work after the above solver - SignatureData sigs; - return ProduceSignature(keystore, DUMMY_SIGNATURE_CREATOR, scriptPubKey, sigs) ? ISMINE_WATCH_SOLVABLE : ISMINE_WATCH_UNSOLVABLE; + if (ret == IsMineResult::NO && keystore.HaveWatchOnly(scriptPubKey)) { + ret = std::max(ret, IsMineResult::WATCH_ONLY); } - return ISMINE_NO; + return ret; } } // namespace -isminetype IsMine(const CKeyStore& keystore, const CScript& scriptPubKey, bool& isInvalid) -{ - return IsMineInner(keystore, scriptPubKey, isInvalid, IsMineSigVersion::TOP); -} - isminetype IsMine(const CKeyStore& keystore, const CScript& scriptPubKey) { - bool isInvalid = false; - return IsMine(keystore, scriptPubKey, isInvalid); + switch (IsMineInner(keystore, scriptPubKey, IsMineSigVersion::TOP)) { + case IsMineResult::INVALID: + case IsMineResult::NO: + return ISMINE_NO; + case IsMineResult::WATCH_ONLY: + return ISMINE_WATCH_ONLY; + case IsMineResult::SPENDABLE: + return ISMINE_SPENDABLE; + } + assert(false); } isminetype IsMine(const CKeyStore& keystore, const CTxDestination& dest) diff --git a/src/script/ismine.h b/src/script/ismine.h index 8573bdfbd2..4246da49fe 100644 --- a/src/script/ismine.h +++ b/src/script/ismine.h @@ -17,23 +17,13 @@ class CScript; enum isminetype { ISMINE_NO = 0, - //! Indicates that we don't know how to create a scriptSig that would solve this if we were given the appropriate private keys - ISMINE_WATCH_UNSOLVABLE = 1, - //! Indicates that we know how to create a scriptSig that would solve this if we were given the appropriate private keys - ISMINE_WATCH_SOLVABLE = 2, - ISMINE_WATCH_ONLY = ISMINE_WATCH_SOLVABLE | ISMINE_WATCH_UNSOLVABLE, - ISMINE_SPENDABLE = 4, + ISMINE_WATCH_ONLY = 1, + ISMINE_SPENDABLE = 2, ISMINE_ALL = ISMINE_WATCH_ONLY | ISMINE_SPENDABLE }; /** used for bitflags of isminetype */ typedef uint8_t isminefilter; -/* isInvalid becomes true when the script is found invalid by consensus or policy. This will terminate the recursion - * and return ISMINE_NO immediately, as an invalid script should never be considered as "mine". This is needed as - * different SIGVERSION may have different network rules. Currently the only use of isInvalid is indicate uncompressed - * keys in SigVersion::WITNESS_V0 script, but could also be used in similar cases in the future - */ -isminetype IsMine(const CKeyStore& keystore, const CScript& scriptPubKey, bool& isInvalid); isminetype IsMine(const CKeyStore& keystore, const CScript& scriptPubKey); isminetype IsMine(const CKeyStore& keystore, const CTxDestination& dest); diff --git a/src/script/script.cpp b/src/script/script.cpp index 7f25d915a8..c84c7b8ec1 100644 --- a/src/script/script.cpp +++ b/src/script/script.cpp @@ -141,11 +141,6 @@ const char* GetOpName(opcodetype opcode) case OP_INVALIDOPCODE : return "OP_INVALIDOPCODE"; - // Note: - // The template matching params OP_SMALLINTEGER/etc are defined in opcodetype enum - // as kind of implementation hack, they are *NOT* real opcodes. If found in real - // Script, just let the default: case deal with them. - default: return "OP_UNKNOWN"; } diff --git a/src/script/script.h b/src/script/script.h index d8b7c06013..a4f377dd94 100644 --- a/src/script/script.h +++ b/src/script/script.h @@ -181,13 +181,6 @@ enum opcodetype OP_NOP9 = 0xb8, OP_NOP10 = 0xb9, - - // template matching params - OP_SMALLINTEGER = 0xfa, - OP_PUBKEYS = 0xfb, - OP_PUBKEYHASH = 0xfd, - OP_PUBKEY = 0xfe, - OP_INVALIDOPCODE = 0xff, }; diff --git a/src/script/script_error.cpp b/src/script/script_error.cpp index dbceb1f740..ceda740580 100644 --- a/src/script/script_error.cpp +++ b/src/script/script_error.cpp @@ -89,6 +89,10 @@ const char* ScriptErrorString(const ScriptError serror) return "Witness provided for non-witness script"; case SCRIPT_ERR_WITNESS_PUBKEYTYPE: return "Using non-compressed keys in segwit"; + case SCRIPT_ERR_OP_CODESEPARATOR: + return "Using OP_CODESEPARATOR in non-witness script"; + case SCRIPT_ERR_SIG_FINDANDDELETE: + return "Signature is found in scriptCode"; case SCRIPT_ERR_UNKNOWN_ERROR: case SCRIPT_ERR_ERROR_COUNT: default: break; diff --git a/src/script/script_error.h b/src/script/script_error.h index 3200e94707..6982a087f4 100644 --- a/src/script/script_error.h +++ b/src/script/script_error.h @@ -64,6 +64,10 @@ typedef enum ScriptError_t SCRIPT_ERR_WITNESS_UNEXPECTED, SCRIPT_ERR_WITNESS_PUBKEYTYPE, + /* Constant scriptCode */ + SCRIPT_ERR_OP_CODESEPARATOR, + SCRIPT_ERR_SIG_FINDANDDELETE, + SCRIPT_ERR_ERROR_COUNT } ScriptError; diff --git a/src/script/sign.cpp b/src/script/sign.cpp index ac35f17f3e..60a8a2655d 100644 --- a/src/script/sign.cpp +++ b/src/script/sign.cpp @@ -14,9 +14,9 @@ typedef std::vector<unsigned char> valtype; -TransactionSignatureCreator::TransactionSignatureCreator(const CTransaction* txToIn, unsigned int nInIn, const CAmount& amountIn, int nHashTypeIn) : txTo(txToIn), nIn(nInIn), nHashType(nHashTypeIn), amount(amountIn), checker(txTo, nIn, amountIn) {} +MutableTransactionSignatureCreator::MutableTransactionSignatureCreator(const CMutableTransaction* txToIn, unsigned int nInIn, const CAmount& amountIn, int nHashTypeIn) : txTo(txToIn), nIn(nInIn), nHashType(nHashTypeIn), amount(amountIn), checker(txTo, nIn, amountIn) {} -bool TransactionSignatureCreator::CreateSig(const SigningProvider& provider, std::vector<unsigned char>& vchSig, const CKeyID& address, const CScript& scriptCode, SigVersion sigversion) const +bool MutableTransactionSignatureCreator::CreateSig(const SigningProvider& provider, std::vector<unsigned char>& vchSig, const CKeyID& address, const CScript& scriptCode, SigVersion sigversion) const { CKey key; if (!provider.GetKey(address, key)) @@ -33,27 +33,51 @@ bool TransactionSignatureCreator::CreateSig(const SigningProvider& provider, std return true; } -static bool Sign1(const SigningProvider& provider, const CKeyID& address, const BaseSignatureCreator& creator, const CScript& scriptCode, std::vector<valtype>& ret, SigVersion sigversion) +static bool GetCScript(const SigningProvider& provider, const SignatureData& sigdata, const CScriptID& scriptid, CScript& script) { - std::vector<unsigned char> vchSig; - if (!creator.CreateSig(provider, vchSig, address, scriptCode, sigversion)) - return false; - ret.push_back(vchSig); - return true; + if (provider.GetCScript(scriptid, script)) { + return true; + } + // Look for scripts in SignatureData + if (CScriptID(sigdata.redeem_script) == scriptid) { + script = sigdata.redeem_script; + return true; + } else if (CScriptID(sigdata.witness_script) == scriptid) { + script = sigdata.witness_script; + return true; + } + return false; } -static bool SignN(const SigningProvider& provider, const std::vector<valtype>& multisigdata, const BaseSignatureCreator& creator, const CScript& scriptCode, std::vector<valtype>& ret, SigVersion sigversion) +static bool GetPubKey(const SigningProvider& provider, const SignatureData& sigdata, const CKeyID& address, CPubKey& pubkey) { - int nSigned = 0; - int nRequired = multisigdata.front()[0]; - for (unsigned int i = 1; i < multisigdata.size()-1 && nSigned < nRequired; i++) - { - const valtype& pubkey = multisigdata[i]; - CKeyID keyID = CPubKey(pubkey).GetID(); - if (Sign1(provider, keyID, creator, scriptCode, ret, sigversion)) - ++nSigned; + if (provider.GetPubKey(address, pubkey)) { + return true; + } + // Look for pubkey in all partial sigs + const auto it = sigdata.signatures.find(address); + if (it != sigdata.signatures.end()) { + pubkey = it->second.first; + return true; } - return nSigned==nRequired; + return false; +} + +static bool CreateSig(const BaseSignatureCreator& creator, SignatureData& sigdata, const SigningProvider& provider, std::vector<unsigned char>& sig_out, const CKeyID& keyid, const CScript& scriptcode, SigVersion sigversion) +{ + const auto it = sigdata.signatures.find(keyid); + if (it != sigdata.signatures.end()) { + sig_out = it->second.second; + return true; + } + if (creator.CreateSig(provider, sig_out, keyid, scriptcode, sigversion)) { + CPubKey pubkey; + GetPubKey(provider, sigdata, keyid, pubkey); + auto i = sigdata.signatures.emplace(keyid, SigPair(pubkey, sig_out)); + assert(i.second); + return true; + } + return false; } /** @@ -63,17 +87,17 @@ static bool SignN(const SigningProvider& provider, const std::vector<valtype>& m * Returns false if scriptPubKey could not be completely satisfied. */ static bool SignStep(const SigningProvider& provider, const BaseSignatureCreator& creator, const CScript& scriptPubKey, - std::vector<valtype>& ret, txnouttype& whichTypeRet, SigVersion sigversion) + std::vector<valtype>& ret, txnouttype& whichTypeRet, SigVersion sigversion, SignatureData& sigdata) { CScript scriptRet; uint160 h160; ret.clear(); + std::vector<unsigned char> sig; std::vector<valtype> vSolutions; if (!Solver(scriptPubKey, whichTypeRet, vSolutions)) return false; - CKeyID keyID; switch (whichTypeRet) { case TX_NONSTANDARD: @@ -81,37 +105,47 @@ static bool SignStep(const SigningProvider& provider, const BaseSignatureCreator case TX_WITNESS_UNKNOWN: return false; case TX_PUBKEY: - keyID = CPubKey(vSolutions[0]).GetID(); - return Sign1(provider, keyID, creator, scriptPubKey, ret, sigversion); - case TX_PUBKEYHASH: - keyID = CKeyID(uint160(vSolutions[0])); - if (!Sign1(provider, keyID, creator, scriptPubKey, ret, sigversion)) - return false; - else - { - CPubKey vch; - provider.GetPubKey(keyID, vch); - ret.push_back(ToByteVector(vch)); - } + if (!CreateSig(creator, sigdata, provider, sig, CPubKey(vSolutions[0]).GetID(), scriptPubKey, sigversion)) return false; + ret.push_back(std::move(sig)); + return true; + case TX_PUBKEYHASH: { + CKeyID keyID = CKeyID(uint160(vSolutions[0])); + if (!CreateSig(creator, sigdata, provider, sig, keyID, scriptPubKey, sigversion)) return false; + ret.push_back(std::move(sig)); + CPubKey pubkey; + GetPubKey(provider, sigdata, keyID, pubkey); + ret.push_back(ToByteVector(pubkey)); return true; + } case TX_SCRIPTHASH: - if (provider.GetCScript(uint160(vSolutions[0]), scriptRet)) { + if (GetCScript(provider, sigdata, uint160(vSolutions[0]), scriptRet)) { ret.push_back(std::vector<unsigned char>(scriptRet.begin(), scriptRet.end())); return true; } return false; - case TX_MULTISIG: + case TX_MULTISIG: { + size_t required = vSolutions.front()[0]; ret.push_back(valtype()); // workaround CHECKMULTISIG bug - return (SignN(provider, vSolutions, creator, scriptPubKey, ret, sigversion)); - + for (size_t i = 1; i < vSolutions.size() - 1; ++i) { + CPubKey pubkey = CPubKey(vSolutions[i]); + if (ret.size() < required + 1 && CreateSig(creator, sigdata, provider, sig, pubkey.GetID(), scriptPubKey, sigversion)) { + ret.push_back(std::move(sig)); + } + } + bool ok = ret.size() == required + 1; + for (size_t i = 0; i + ret.size() < required + 1; ++i) { + ret.push_back(valtype()); + } + return ok; + } case TX_WITNESS_V0_KEYHASH: ret.push_back(vSolutions[0]); return true; case TX_WITNESS_V0_SCRIPTHASH: CRIPEMD160().Write(&vSolutions[0][0], vSolutions[0].size()).Finalize(h160.begin()); - if (provider.GetCScript(h160, scriptRet)) { + if (GetCScript(provider, sigdata, h160, scriptRet)) { ret.push_back(std::vector<unsigned char>(scriptRet.begin(), scriptRet.end())); return true; } @@ -139,9 +173,11 @@ static CScript PushAll(const std::vector<valtype>& values) bool ProduceSignature(const SigningProvider& provider, const BaseSignatureCreator& creator, const CScript& fromPubKey, SignatureData& sigdata) { + if (sigdata.complete) return true; + std::vector<valtype> result; txnouttype whichType; - bool solved = SignStep(provider, creator, fromPubKey, result, whichType, SigVersion::BASE); + bool solved = SignStep(provider, creator, fromPubKey, result, whichType, SigVersion::BASE, sigdata); bool P2SH = false; CScript subscript; sigdata.scriptWitness.stack.clear(); @@ -152,7 +188,8 @@ bool ProduceSignature(const SigningProvider& provider, const BaseSignatureCreato // the final scriptSig is the signatures from that // and then the serialized subscript: subscript = CScript(result[0].begin(), result[0].end()); - solved = solved && SignStep(provider, creator, subscript, result, whichType, SigVersion::BASE) && whichType != TX_SCRIPTHASH; + sigdata.redeem_script = subscript; + solved = solved && SignStep(provider, creator, subscript, result, whichType, SigVersion::BASE, sigdata) && whichType != TX_SCRIPTHASH; P2SH = true; } @@ -161,15 +198,16 @@ bool ProduceSignature(const SigningProvider& provider, const BaseSignatureCreato CScript witnessscript; witnessscript << OP_DUP << OP_HASH160 << ToByteVector(result[0]) << OP_EQUALVERIFY << OP_CHECKSIG; txnouttype subType; - solved = solved && SignStep(provider, creator, witnessscript, result, subType, SigVersion::WITNESS_V0); + solved = solved && SignStep(provider, creator, witnessscript, result, subType, SigVersion::WITNESS_V0, sigdata); sigdata.scriptWitness.stack = result; result.clear(); } else if (solved && whichType == TX_WITNESS_V0_SCRIPTHASH) { CScript witnessscript(result[0].begin(), result[0].end()); + sigdata.witness_script = witnessscript; txnouttype subType; - solved = solved && SignStep(provider, creator, witnessscript, result, subType, SigVersion::WITNESS_V0) && subType != TX_SCRIPTHASH && subType != TX_WITNESS_V0_SCRIPTHASH && subType != TX_WITNESS_V0_KEYHASH; + solved = solved && SignStep(provider, creator, witnessscript, result, subType, SigVersion::WITNESS_V0, sigdata) && subType != TX_SCRIPTHASH && subType != TX_WITNESS_V0_SCRIPTHASH && subType != TX_WITNESS_V0_KEYHASH; result.push_back(std::vector<unsigned char>(witnessscript.begin(), witnessscript.end())); sigdata.scriptWitness.stack = result; result.clear(); @@ -181,106 +219,29 @@ bool ProduceSignature(const SigningProvider& provider, const BaseSignatureCreato sigdata.scriptSig = PushAll(result); // Test solution - return solved && VerifyScript(sigdata.scriptSig, fromPubKey, &sigdata.scriptWitness, STANDARD_SCRIPT_VERIFY_FLAGS, creator.Checker()); -} - -SignatureData DataFromTransaction(const CMutableTransaction& tx, unsigned int nIn) -{ - SignatureData data; - assert(tx.vin.size() > nIn); - data.scriptSig = tx.vin[nIn].scriptSig; - data.scriptWitness = tx.vin[nIn].scriptWitness; - return data; + sigdata.complete = solved && VerifyScript(sigdata.scriptSig, fromPubKey, &sigdata.scriptWitness, STANDARD_SCRIPT_VERIFY_FLAGS, creator.Checker()); + return sigdata.complete; } -void UpdateInput(CTxIn& input, const SignatureData& data) +class SignatureExtractorChecker final : public BaseSignatureChecker { - input.scriptSig = data.scriptSig; - input.scriptWitness = data.scriptWitness; -} - -void UpdateTransaction(CMutableTransaction& tx, unsigned int nIn, const SignatureData& data) -{ - assert(tx.vin.size() > nIn); - UpdateInput(tx.vin[nIn], data); -} - -bool SignSignature(const SigningProvider &provider, const CScript& fromPubKey, CMutableTransaction& txTo, unsigned int nIn, const CAmount& amount, int nHashType) -{ - assert(nIn < txTo.vin.size()); +private: + SignatureData& sigdata; + BaseSignatureChecker& checker; - CTransaction txToConst(txTo); - TransactionSignatureCreator creator(&txToConst, nIn, amount, nHashType); - - SignatureData sigdata; - bool ret = ProduceSignature(provider, creator, fromPubKey, sigdata); - UpdateTransaction(txTo, nIn, sigdata); - return ret; -} - -bool SignSignature(const SigningProvider &provider, const CTransaction& txFrom, CMutableTransaction& txTo, unsigned int nIn, int nHashType) -{ - assert(nIn < txTo.vin.size()); - CTxIn& txin = txTo.vin[nIn]; - assert(txin.prevout.n < txFrom.vout.size()); - const CTxOut& txout = txFrom.vout[txin.prevout.n]; - - return SignSignature(provider, txout.scriptPubKey, txTo, nIn, txout.nValue, nHashType); -} +public: + SignatureExtractorChecker(SignatureData& sigdata, BaseSignatureChecker& checker) : sigdata(sigdata), checker(checker) {} + bool CheckSig(const std::vector<unsigned char>& scriptSig, const std::vector<unsigned char>& vchPubKey, const CScript& scriptCode, SigVersion sigversion) const override; +}; -static std::vector<valtype> CombineMultisig(const CScript& scriptPubKey, const BaseSignatureChecker& checker, - const std::vector<valtype>& vSolutions, - const std::vector<valtype>& sigs1, const std::vector<valtype>& sigs2, SigVersion sigversion) +bool SignatureExtractorChecker::CheckSig(const std::vector<unsigned char>& scriptSig, const std::vector<unsigned char>& vchPubKey, const CScript& scriptCode, SigVersion sigversion) const { - // Combine all the signatures we've got: - std::set<valtype> allsigs; - for (const valtype& v : sigs1) - { - if (!v.empty()) - allsigs.insert(v); - } - for (const valtype& v : sigs2) - { - if (!v.empty()) - allsigs.insert(v); - } - - // Build a map of pubkey -> signature by matching sigs to pubkeys: - assert(vSolutions.size() > 1); - unsigned int nSigsRequired = vSolutions.front()[0]; - unsigned int nPubKeys = vSolutions.size()-2; - std::map<valtype, valtype> sigs; - for (const valtype& sig : allsigs) - { - for (unsigned int i = 0; i < nPubKeys; i++) - { - const valtype& pubkey = vSolutions[i+1]; - if (sigs.count(pubkey)) - continue; // Already got a sig for this pubkey - - if (checker.CheckSig(sig, pubkey, scriptPubKey, sigversion)) - { - sigs[pubkey] = sig; - break; - } - } - } - // Now build a merged CScript: - unsigned int nSigsHave = 0; - std::vector<valtype> result; result.push_back(valtype()); // pop-one-too-many workaround - for (unsigned int i = 0; i < nPubKeys && nSigsHave < nSigsRequired; i++) - { - if (sigs.count(vSolutions[i+1])) - { - result.push_back(sigs[vSolutions[i+1]]); - ++nSigsHave; - } + if (checker.CheckSig(scriptSig, vchPubKey, scriptCode, sigversion)) { + CPubKey pubkey(vchPubKey); + sigdata.signatures.emplace(pubkey.GetID(), SigPair(pubkey, scriptSig)); + return true; } - // Fill any missing with OP_0: - for (unsigned int i = nSigsHave; i < nSigsRequired; i++) - result.push_back(valtype()); - - return result; + return false; } namespace @@ -305,89 +266,115 @@ struct Stacks }; } -static Stacks CombineSignatures(const CScript& scriptPubKey, const BaseSignatureChecker& checker, - const txnouttype txType, const std::vector<valtype>& vSolutions, - Stacks sigs1, Stacks sigs2, SigVersion sigversion) +// Extracts signatures and scripts from incomplete scriptSigs. Please do not extend this, use PSBT instead +SignatureData DataFromTransaction(const CMutableTransaction& tx, unsigned int nIn, const CTxOut& txout) { - switch (txType) - { - case TX_NONSTANDARD: - case TX_NULL_DATA: - case TX_WITNESS_UNKNOWN: - // Don't know anything about this, assume bigger one is correct: - if (sigs1.script.size() >= sigs2.script.size()) - return sigs1; - return sigs2; - case TX_PUBKEY: - case TX_PUBKEYHASH: - // Signatures are bigger than placeholders or empty scripts: - if (sigs1.script.empty() || sigs1.script[0].empty()) - return sigs2; - return sigs1; - case TX_WITNESS_V0_KEYHASH: - // Signatures are bigger than placeholders or empty scripts: - if (sigs1.witness.empty() || sigs1.witness[0].empty()) - return sigs2; - return sigs1; - case TX_SCRIPTHASH: - if (sigs1.script.empty() || sigs1.script.back().empty()) - return sigs2; - else if (sigs2.script.empty() || sigs2.script.back().empty()) - return sigs1; - else - { - // Recur to combine: - valtype spk = sigs1.script.back(); - CScript pubKey2(spk.begin(), spk.end()); - - txnouttype txType2; - std::vector<std::vector<unsigned char> > vSolutions2; - Solver(pubKey2, txType2, vSolutions2); - sigs1.script.pop_back(); - sigs2.script.pop_back(); - Stacks result = CombineSignatures(pubKey2, checker, txType2, vSolutions2, sigs1, sigs2, sigversion); - result.script.push_back(spk); - return result; - } - case TX_MULTISIG: - return Stacks(CombineMultisig(scriptPubKey, checker, vSolutions, sigs1.script, sigs2.script, sigversion)); - case TX_WITNESS_V0_SCRIPTHASH: - if (sigs1.witness.empty() || sigs1.witness.back().empty()) - return sigs2; - else if (sigs2.witness.empty() || sigs2.witness.back().empty()) - return sigs1; - else - { - // Recur to combine: - CScript pubKey2(sigs1.witness.back().begin(), sigs1.witness.back().end()); - txnouttype txType2; - std::vector<valtype> vSolutions2; - Solver(pubKey2, txType2, vSolutions2); - sigs1.witness.pop_back(); - sigs1.script = sigs1.witness; - sigs1.witness.clear(); - sigs2.witness.pop_back(); - sigs2.script = sigs2.witness; - sigs2.witness.clear(); - Stacks result = CombineSignatures(pubKey2, checker, txType2, vSolutions2, sigs1, sigs2, SigVersion::WITNESS_V0); - result.witness = result.script; - result.script.clear(); - result.witness.push_back(valtype(pubKey2.begin(), pubKey2.end())); - return result; + SignatureData data; + assert(tx.vin.size() > nIn); + data.scriptSig = tx.vin[nIn].scriptSig; + data.scriptWitness = tx.vin[nIn].scriptWitness; + Stacks stack(data); + + // Get signatures + MutableTransactionSignatureChecker tx_checker(&tx, nIn, txout.nValue); + SignatureExtractorChecker extractor_checker(data, tx_checker); + if (VerifyScript(data.scriptSig, txout.scriptPubKey, &data.scriptWitness, STANDARD_SCRIPT_VERIFY_FLAGS, extractor_checker)) { + data.complete = true; + return data; + } + + // Get scripts + txnouttype script_type; + std::vector<std::vector<unsigned char>> solutions; + Solver(txout.scriptPubKey, script_type, solutions); + SigVersion sigversion = SigVersion::BASE; + CScript next_script = txout.scriptPubKey; + + if (script_type == TX_SCRIPTHASH && !stack.script.empty() && !stack.script.back().empty()) { + // Get the redeemScript + CScript redeem_script(stack.script.back().begin(), stack.script.back().end()); + data.redeem_script = redeem_script; + next_script = std::move(redeem_script); + + // Get redeemScript type + Solver(next_script, script_type, solutions); + stack.script.pop_back(); + } + if (script_type == TX_WITNESS_V0_SCRIPTHASH && !stack.witness.empty() && !stack.witness.back().empty()) { + // Get the witnessScript + CScript witness_script(stack.witness.back().begin(), stack.witness.back().end()); + data.witness_script = witness_script; + next_script = std::move(witness_script); + + // Get witnessScript type + Solver(next_script, script_type, solutions); + stack.witness.pop_back(); + stack.script = std::move(stack.witness); + stack.witness.clear(); + sigversion = SigVersion::WITNESS_V0; + } + if (script_type == TX_MULTISIG && !stack.script.empty()) { + // Build a map of pubkey -> signature by matching sigs to pubkeys: + assert(solutions.size() > 1); + unsigned int num_pubkeys = solutions.size()-2; + unsigned int last_success_key = 0; + for (const valtype& sig : stack.script) { + for (unsigned int i = last_success_key; i < num_pubkeys; ++i) { + const valtype& pubkey = solutions[i+1]; + // We either have a signature for this pubkey, or we have found a signature and it is valid + if (data.signatures.count(CPubKey(pubkey).GetID()) || extractor_checker.CheckSig(sig, pubkey, next_script, sigversion)) { + last_success_key = i + 1; + break; + } + } } - default: - return Stacks(); } + + return data; +} + +void UpdateInput(CTxIn& input, const SignatureData& data) +{ + input.scriptSig = data.scriptSig; + input.scriptWitness = data.scriptWitness; +} + +void SignatureData::MergeSignatureData(SignatureData sigdata) +{ + if (complete) return; + if (sigdata.complete) { + *this = std::move(sigdata); + return; + } + if (redeem_script.empty() && !sigdata.redeem_script.empty()) { + redeem_script = sigdata.redeem_script; + } + if (witness_script.empty() && !sigdata.witness_script.empty()) { + witness_script = sigdata.witness_script; + } + signatures.insert(std::make_move_iterator(sigdata.signatures.begin()), std::make_move_iterator(sigdata.signatures.end())); } -SignatureData CombineSignatures(const CScript& scriptPubKey, const BaseSignatureChecker& checker, - const SignatureData& scriptSig1, const SignatureData& scriptSig2) +bool SignSignature(const SigningProvider &provider, const CScript& fromPubKey, CMutableTransaction& txTo, unsigned int nIn, const CAmount& amount, int nHashType) +{ + assert(nIn < txTo.vin.size()); + + MutableTransactionSignatureCreator creator(&txTo, nIn, amount, nHashType); + + SignatureData sigdata; + bool ret = ProduceSignature(provider, creator, fromPubKey, sigdata); + UpdateInput(txTo.vin.at(nIn), sigdata); + return ret; +} + +bool SignSignature(const SigningProvider &provider, const CTransaction& txFrom, CMutableTransaction& txTo, unsigned int nIn, int nHashType) { - txnouttype txType; - std::vector<std::vector<unsigned char> > vSolutions; - Solver(scriptPubKey, txType, vSolutions); + assert(nIn < txTo.vin.size()); + CTxIn& txin = txTo.vin[nIn]; + assert(txin.prevout.n < txFrom.vout.size()); + const CTxOut& txout = txFrom.vout[txin.prevout.n]; - return CombineSignatures(scriptPubKey, checker, txType, vSolutions, Stacks(scriptSig1), Stacks(scriptSig2), SigVersion::BASE).Output(); + return SignSignature(provider, txout.scriptPubKey, txTo, nIn, txout.nValue, nHashType); } namespace { @@ -423,6 +410,7 @@ public: } const BaseSignatureCreator& DUMMY_SIGNATURE_CREATOR = DummySignatureCreator(); +const SigningProvider& DUMMY_SIGNING_PROVIDER = SigningProvider(); bool IsSolvable(const SigningProvider& provider, const CScript& script) { diff --git a/src/script/sign.h b/src/script/sign.h index cf3651c1de..3666859641 100644 --- a/src/script/sign.h +++ b/src/script/sign.h @@ -21,11 +21,13 @@ class SigningProvider { public: virtual ~SigningProvider() {} - virtual bool GetCScript(const CScriptID &scriptid, CScript& script) const =0; - virtual bool GetPubKey(const CKeyID &address, CPubKey& pubkey) const =0; - virtual bool GetKey(const CKeyID &address, CKey& key) const =0; + virtual bool GetCScript(const CScriptID &scriptid, CScript& script) const { return false; } + virtual bool GetPubKey(const CKeyID &address, CPubKey& pubkey) const { return false; } + virtual bool GetKey(const CKeyID &address, CKey& key) const { return false; } }; +extern const SigningProvider& DUMMY_SIGNING_PROVIDER; + /** Interface for signature creators. */ class BaseSignatureCreator { public: @@ -37,35 +39,38 @@ public: }; /** A signature creator for transactions. */ -class TransactionSignatureCreator : public BaseSignatureCreator { - const CTransaction* txTo; +class MutableTransactionSignatureCreator : public BaseSignatureCreator { + const CMutableTransaction* txTo; unsigned int nIn; int nHashType; CAmount amount; - const TransactionSignatureChecker checker; + const MutableTransactionSignatureChecker checker; public: - TransactionSignatureCreator(const CTransaction* txToIn, unsigned int nInIn, const CAmount& amountIn, int nHashTypeIn=SIGHASH_ALL); + MutableTransactionSignatureCreator(const CMutableTransaction* txToIn, unsigned int nInIn, const CAmount& amountIn, int nHashTypeIn = SIGHASH_ALL); const BaseSignatureChecker& Checker() const override { return checker; } bool CreateSig(const SigningProvider& provider, std::vector<unsigned char>& vchSig, const CKeyID& keyid, const CScript& scriptCode, SigVersion sigversion) const override; }; -class MutableTransactionSignatureCreator : public TransactionSignatureCreator { - CTransaction tx; - -public: - MutableTransactionSignatureCreator(const CMutableTransaction* txToIn, unsigned int nInIn, const CAmount& amountIn, int nHashTypeIn) : TransactionSignatureCreator(&tx, nInIn, amountIn, nHashTypeIn), tx(*txToIn) {} -}; - /** A signature creator that just produces 72-byte empty signatures. */ extern const BaseSignatureCreator& DUMMY_SIGNATURE_CREATOR; +typedef std::pair<CPubKey, std::vector<unsigned char>> SigPair; + +// This struct contains information from a transaction input and also contains signatures for that input. +// The information contained here can be used to create a signature and is also filled by ProduceSignature +// in order to construct final scriptSigs and scriptWitnesses. struct SignatureData { - CScript scriptSig; - CScriptWitness scriptWitness; + bool complete = false; ///< Stores whether the scriptSig and scriptWitness are complete + CScript scriptSig; ///< The scriptSig of an input. Contains complete signatures or the traditional partial signatures format + CScript redeem_script; ///< The redeemScript (if any) for the input + CScript witness_script; ///< The witnessScript (if any) for the input. witnessScripts are used in P2WSH outputs. + CScriptWitness scriptWitness; ///< The scriptWitness of an input. Contains complete signatures or the traditional partial signatures format. scriptWitness is part of a transaction input per BIP 144. + std::map<CKeyID, SigPair> signatures; ///< BIP 174 style partial signatures for the input. May contain all signatures necessary for producing a final scriptSig or scriptWitness. SignatureData() {} explicit SignatureData(const CScript& script) : scriptSig(script) {} + void MergeSignatureData(SignatureData sigdata); }; /** Produce a script signature using a generic signature creator. */ @@ -75,12 +80,8 @@ bool ProduceSignature(const SigningProvider& provider, const BaseSignatureCreato bool SignSignature(const SigningProvider &provider, const CScript& fromPubKey, CMutableTransaction& txTo, unsigned int nIn, const CAmount& amount, int nHashType); bool SignSignature(const SigningProvider &provider, const CTransaction& txFrom, CMutableTransaction& txTo, unsigned int nIn, int nHashType); -/** Combine two script signatures using a generic signature checker, intelligently, possibly with OP_0 placeholders. */ -SignatureData CombineSignatures(const CScript& scriptPubKey, const BaseSignatureChecker& checker, const SignatureData& scriptSig1, const SignatureData& scriptSig2); - -/** Extract signature data from a transaction, and insert it. */ -SignatureData DataFromTransaction(const CMutableTransaction& tx, unsigned int nIn); -void UpdateTransaction(CMutableTransaction& tx, unsigned int nIn, const SignatureData& data); +/** Extract signature data from a transaction input, and insert it. */ +SignatureData DataFromTransaction(const CMutableTransaction& tx, unsigned int nIn, const CTxOut& txout); void UpdateInput(CTxIn& input, const SignatureData& data); /* Check whether we know how to sign for an output like this, assuming we diff --git a/src/script/standard.cpp b/src/script/standard.cpp index 76778112aa..f0b2c62a91 100644 --- a/src/script/standard.cpp +++ b/src/script/standard.cpp @@ -5,6 +5,7 @@ #include <script/standard.h> +#include <crypto/sha256.h> #include <pubkey.h> #include <script/script.h> #include <util.h> @@ -18,6 +19,11 @@ unsigned nMaxDatacarrierBytes = MAX_OP_RETURN_RELAY; CScriptID::CScriptID(const CScript& in) : uint160(Hash160(in.begin(), in.end())) {} +WitnessV0ScriptHash::WitnessV0ScriptHash(const CScript& in) +{ + CSHA256().Write(in.data(), in.size()).Finalize(begin()); +} + const char* GetTxnOutputType(txnouttype t) { switch (t) @@ -35,22 +41,54 @@ const char* GetTxnOutputType(txnouttype t) return nullptr; } -bool Solver(const CScript& scriptPubKey, txnouttype& typeRet, std::vector<std::vector<unsigned char> >& vSolutionsRet) +static bool MatchPayToPubkey(const CScript& script, valtype& pubkey) { - // Templates - static std::multimap<txnouttype, CScript> mTemplates; - if (mTemplates.empty()) - { - // Standard tx, sender provides pubkey, receiver adds signature - mTemplates.insert(std::make_pair(TX_PUBKEY, CScript() << OP_PUBKEY << OP_CHECKSIG)); + if (script.size() == CPubKey::PUBLIC_KEY_SIZE + 2 && script[0] == CPubKey::PUBLIC_KEY_SIZE && script.back() == OP_CHECKSIG) { + pubkey = valtype(script.begin() + 1, script.begin() + CPubKey::PUBLIC_KEY_SIZE + 1); + return CPubKey::ValidSize(pubkey); + } + if (script.size() == CPubKey::COMPRESSED_PUBLIC_KEY_SIZE + 2 && script[0] == CPubKey::COMPRESSED_PUBLIC_KEY_SIZE && script.back() == OP_CHECKSIG) { + pubkey = valtype(script.begin() + 1, script.begin() + CPubKey::COMPRESSED_PUBLIC_KEY_SIZE + 1); + return CPubKey::ValidSize(pubkey); + } + return false; +} - // Bitcoin address tx, sender provides hash of pubkey, receiver provides signature and pubkey - mTemplates.insert(std::make_pair(TX_PUBKEYHASH, CScript() << OP_DUP << OP_HASH160 << OP_PUBKEYHASH << OP_EQUALVERIFY << OP_CHECKSIG)); +static bool MatchPayToPubkeyHash(const CScript& script, valtype& pubkeyhash) +{ + if (script.size() == 25 && script[0] == OP_DUP && script[1] == OP_HASH160 && script[2] == 20 && script[23] == OP_EQUALVERIFY && script[24] == OP_CHECKSIG) { + pubkeyhash = valtype(script.begin () + 3, script.begin() + 23); + return true; + } + return false; +} - // Sender provides N pubkeys, receivers provides M signatures - mTemplates.insert(std::make_pair(TX_MULTISIG, CScript() << OP_SMALLINTEGER << OP_PUBKEYS << OP_SMALLINTEGER << OP_CHECKMULTISIG)); +/** Test for "small positive integer" script opcodes - OP_1 through OP_16. */ +static constexpr bool IsSmallInteger(opcodetype opcode) +{ + return opcode >= OP_1 && opcode <= OP_16; +} + +static bool MatchMultisig(const CScript& script, unsigned int& required, std::vector<valtype>& pubkeys) +{ + opcodetype opcode; + valtype data; + CScript::const_iterator it = script.begin(); + if (script.size() < 1 || script.back() != OP_CHECKMULTISIG) return false; + + if (!script.GetOp(it, opcode, data) || !IsSmallInteger(opcode)) return false; + required = CScript::DecodeOP_N(opcode); + while (script.GetOp(it, opcode, data) && CPubKey::ValidSize(data)) { + pubkeys.emplace_back(std::move(data)); } + if (!IsSmallInteger(opcode)) return false; + unsigned int keys = CScript::DecodeOP_N(opcode); + if (pubkeys.size() != keys || keys < required) return false; + return (it + 1 == script.end()); +} +bool Solver(const CScript& scriptPubKey, txnouttype& typeRet, std::vector<std::vector<unsigned char> >& vSolutionsRet) +{ vSolutionsRet.clear(); // Shortcut for pay-to-script-hash, which are more constrained than the other types: @@ -82,6 +120,7 @@ bool Solver(const CScript& scriptPubKey, txnouttype& typeRet, std::vector<std::v vSolutionsRet.push_back(std::move(witnessprogram)); return true; } + typeRet = TX_NONSTANDARD; return false; } @@ -95,84 +134,27 @@ bool Solver(const CScript& scriptPubKey, txnouttype& typeRet, std::vector<std::v return true; } - // Scan templates - const CScript& script1 = scriptPubKey; - for (const std::pair<txnouttype, CScript>& tplate : mTemplates) - { - const CScript& script2 = tplate.second; - vSolutionsRet.clear(); + std::vector<unsigned char> data; + if (MatchPayToPubkey(scriptPubKey, data)) { + typeRet = TX_PUBKEY; + vSolutionsRet.push_back(std::move(data)); + return true; + } - opcodetype opcode1, opcode2; - std::vector<unsigned char> vch1, vch2; + if (MatchPayToPubkeyHash(scriptPubKey, data)) { + typeRet = TX_PUBKEYHASH; + vSolutionsRet.push_back(std::move(data)); + return true; + } - // Compare - CScript::const_iterator pc1 = script1.begin(); - CScript::const_iterator pc2 = script2.begin(); - while (true) - { - if (pc1 == script1.end() && pc2 == script2.end()) - { - // Found a match - typeRet = tplate.first; - if (typeRet == TX_MULTISIG) - { - // Additional checks for TX_MULTISIG: - unsigned char m = vSolutionsRet.front()[0]; - unsigned char n = vSolutionsRet.back()[0]; - if (m < 1 || n < 1 || m > n || vSolutionsRet.size()-2 != n) - return false; - } - return true; - } - if (!script1.GetOp(pc1, opcode1, vch1)) - break; - if (!script2.GetOp(pc2, opcode2, vch2)) - break; - - // Template matching opcodes: - if (opcode2 == OP_PUBKEYS) - { - while (CPubKey::ValidSize(vch1)) - { - vSolutionsRet.push_back(vch1); - if (!script1.GetOp(pc1, opcode1, vch1)) - break; - } - if (!script2.GetOp(pc2, opcode2, vch2)) - break; - // Normal situation is to fall through - // to other if/else statements - } - - if (opcode2 == OP_PUBKEY) - { - if (!CPubKey::ValidSize(vch1)) - break; - vSolutionsRet.push_back(vch1); - } - else if (opcode2 == OP_PUBKEYHASH) - { - if (vch1.size() != sizeof(uint160)) - break; - vSolutionsRet.push_back(vch1); - } - else if (opcode2 == OP_SMALLINTEGER) - { // Single-byte small integer pushed onto vSolutions - if (opcode1 == OP_0 || - (opcode1 >= OP_1 && opcode1 <= OP_16)) - { - char n = (char)CScript::DecodeOP_N(opcode1); - vSolutionsRet.push_back(valtype(1, n)); - } - else - break; - } - else if (opcode1 != opcode2 || vch1 != vch2) - { - // Others must match exactly - break; - } - } + unsigned int required; + std::vector<std::vector<unsigned char>> keys; + if (MatchMultisig(scriptPubKey, required, keys)) { + typeRet = TX_MULTISIG; + vSolutionsRet.push_back({static_cast<unsigned char>(required)}); // safe as required is in range 1..16 + vSolutionsRet.insert(vSolutionsRet.end(), keys.begin(), keys.end()); + vSolutionsRet.push_back({static_cast<unsigned char>(keys.size())}); // safe as size is in range 1..16 + return true; } vSolutionsRet.clear(); @@ -353,9 +335,7 @@ CScript GetScriptForWitness(const CScript& redeemscript) return GetScriptForDestination(WitnessV0KeyHash(vSolutions[0])); } } - uint256 hash; - CSHA256().Write(&redeemscript[0], redeemscript.size()).Finalize(hash.begin()); - return GetScriptForDestination(WitnessV0ScriptHash(hash)); + return GetScriptForDestination(WitnessV0ScriptHash(redeemscript)); } bool IsValidDestination(const CTxDestination& dest) { diff --git a/src/script/standard.h b/src/script/standard.h index 4922b7236b..1380030871 100644 --- a/src/script/standard.h +++ b/src/script/standard.h @@ -77,6 +77,7 @@ struct WitnessV0ScriptHash : public uint256 { WitnessV0ScriptHash() : uint256() {} explicit WitnessV0ScriptHash(const uint256& hash) : uint256(hash) {} + explicit WitnessV0ScriptHash(const CScript& script); using uint256::uint256; }; |