From 99088d60d8a7747c6d1a7fd5d8cd388be1b3e138 Mon Sep 17 00:00:00 2001 From: Peter Todd Date: Mon, 29 Sep 2014 01:00:01 -0400 Subject: Make CScriptNum() take nMaxNumSize as an argument While the existing numeric opcodes are all limited to 4-byte bignum arguments, new opcodes will need different limits. --- src/script/script.h | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'src/script') diff --git a/src/script/script.h b/src/script/script.h index ed456f5c5..a4b9de304 100644 --- a/src/script/script.h +++ b/src/script/script.h @@ -195,7 +195,10 @@ public: m_value = n; } - explicit CScriptNum(const std::vector& vch, bool fRequireMinimal) + static const size_t nDefaultMaxNumSize = 4; + + explicit CScriptNum(const std::vector& vch, bool fRequireMinimal, + const size_t nMaxNumSize = nDefaultMaxNumSize) { if (vch.size() > nMaxNumSize) { throw scriptnum_error("script number overflow"); @@ -318,8 +321,6 @@ public: return result; } - static const size_t nMaxNumSize = 4; - private: static int64_t set_vch(const std::vector& vch) { -- cgit v1.2.3 From 48e9c57cf06352f890eac4285ae022d8746cf3fd Mon Sep 17 00:00:00 2001 From: Peter Todd Date: Mon, 29 Sep 2014 01:02:59 -0400 Subject: Move LOCKTIME_THRESHOLD to src/script/script.h Will now be needed by CHECKLOCKTIMEVERIFY code. --- src/script/script.h | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'src/script') diff --git a/src/script/script.h b/src/script/script.h index a4b9de304..45a06acc9 100644 --- a/src/script/script.h +++ b/src/script/script.h @@ -18,6 +18,10 @@ static const unsigned int MAX_SCRIPT_ELEMENT_SIZE = 520; // bytes +// Threshold for nLockTime: below this value it is interpreted as block number, +// otherwise as UNIX timestamp. +static const unsigned int LOCKTIME_THRESHOLD = 500000000; // Tue Nov 5 00:53:20 1985 UTC + template std::vector ToByteVector(const T& in) { -- cgit v1.2.3 From 9b4e7d9a5ec0f69c175d23dc9c94ed723147cf45 Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Fri, 1 May 2015 15:21:06 +0200 Subject: Add DummySignatureCreator which just creates zeroed sigs --- src/script/sign.cpp | 36 ++++++++++++++++++++++++++++++++++++ src/script/sign.h | 8 ++++++++ 2 files changed, 44 insertions(+) (limited to 'src/script') diff --git a/src/script/sign.cpp b/src/script/sign.cpp index eab629cd9..4543ca303 100644 --- a/src/script/sign.cpp +++ b/src/script/sign.cpp @@ -275,3 +275,39 @@ CScript CombineSignatures(const CScript& scriptPubKey, const BaseSignatureChecke return CombineSignatures(scriptPubKey, checker, txType, vSolutions, stack1, stack2); } + +namespace { +/** Dummy signature checker which accepts all signatures. */ +class DummySignatureChecker : public BaseSignatureChecker +{ +public: + DummySignatureChecker() {} + + bool CheckSig(const std::vector& scriptSig, const std::vector& vchPubKey, const CScript& scriptCode) const + { + return true; + } +}; +const DummySignatureChecker dummyChecker; +} + +const BaseSignatureChecker& DummySignatureCreator::Checker() const +{ + return dummyChecker; +} + +bool DummySignatureCreator::CreateSig(std::vector& vchSig, const CKeyID& keyid, const CScript& scriptCode) const +{ + // Create a dummy signature that is a valid DER-encoding + vchSig.assign(72, '\000'); + vchSig[0] = 0x30; + vchSig[1] = 69; + vchSig[2] = 0x02; + vchSig[3] = 33; + vchSig[4] = 0x01; + vchSig[4 + 33] = 0x02; + vchSig[5 + 33] = 32; + vchSig[6 + 33] = 0x01; + vchSig[6 + 33 + 32] = SIGHASH_ALL; + return true; +} diff --git a/src/script/sign.h b/src/script/sign.h index 0c4cf61e5..13f45007d 100644 --- a/src/script/sign.h +++ b/src/script/sign.h @@ -43,6 +43,14 @@ public: bool CreateSig(std::vector& vchSig, const CKeyID& keyid, const CScript& scriptCode) const; }; +/** A signature creator that just produces 72-byte empty signatyres. */ +class DummySignatureCreator : public BaseSignatureCreator { +public: + DummySignatureCreator(const CKeyStore* keystoreIn) : BaseSignatureCreator(keystoreIn) {} + const BaseSignatureChecker& Checker() const; + bool CreateSig(std::vector& vchSig, const CKeyID& keyid, const CScript& scriptCode) const; +}; + /** Produce a script signature using a generic signature creator. */ bool ProduceSignature(const BaseSignatureCreator& creator, const CScript& scriptPubKey, CScript& scriptSig); -- cgit v1.2.3 From bc60b2b4b401f0adff5b8b9678903ff8feb5867b Mon Sep 17 00:00:00 2001 From: Peter Todd Date: Mon, 29 Sep 2014 03:44:25 -0400 Subject: Replace NOP2 with CHECKLOCKTIMEVERIFY (BIP65) CHECKLOCKTIMEVERIFY -> Fails if tx.nLockTime < nLockTime, allowing the funds in a txout to be locked until some block height or block time in the future is reached. Only the logic and unittests are implemented; this commit does not have any actual soft-fork logic in it. Thanks to Pieter Wuille for rebase. Credit goes to Gregory Maxwell for the suggestion of comparing the argument against the transaction nLockTime rather than the current time/blockheight directly. --- src/script/interpreter.cpp | 83 +++++++++++++++++++++++++++++++++++++++++++-- src/script/interpreter.h | 11 ++++++ src/script/script.h | 1 + src/script/script_error.cpp | 4 +++ src/script/script_error.h | 4 +++ 5 files changed, 101 insertions(+), 2 deletions(-) (limited to 'src/script') diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp index 84a7432fd..0b78fdf5a 100644 --- a/src/script/interpreter.cpp +++ b/src/script/interpreter.cpp @@ -335,9 +335,51 @@ bool EvalScript(vector >& stack, const CScript& script, un // Control // case OP_NOP: - break; + break; + + case OP_CHECKLOCKTIMEVERIFY: + { + if (!(flags & SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY)) { + // not enabled; treat as a NOP2 + if (flags & SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_NOPS) { + return set_error(serror, SCRIPT_ERR_DISCOURAGE_UPGRADABLE_NOPS); + } + break; + } + + if (stack.size() < 1) + return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION); + + // Note that elsewhere numeric opcodes are limited to + // operands in the range -2**31+1 to 2**31-1, however it is + // legal for opcodes to produce results exceeding that + // range. This limitation is implemented by CScriptNum's + // default 4-byte limit. + // + // If we kept to that limit we'd have a year 2038 problem, + // even though the nLockTime field in transactions + // themselves is uint32 which only becomes meaningless + // after the year 2106. + // + // Thus as a special case we tell CScriptNum to accept up + // to 5-byte bignums, which are good until 2**39-1, well + // beyond the 2**32-1 limit of the nLockTime field itself. + const CScriptNum nLockTime(stacktop(-1), fRequireMinimal, 5); + + // In the rare event that the argument may be < 0 due to + // some arithmetic being done first, you can always use + // 0 MAX CHECKLOCKTIMEVERIFY. + if (nLockTime < 0) + return set_error(serror, SCRIPT_ERR_NEGATIVE_LOCKTIME); + + // Actually compare the specified lock time with the transaction. + if (!checker.CheckLockTime(nLockTime)) + return set_error(serror, SCRIPT_ERR_UNSATISFIED_LOCKTIME); + + break; + } - case OP_NOP1: case OP_NOP2: case OP_NOP3: case OP_NOP4: case OP_NOP5: + case OP_NOP1: case OP_NOP3: case OP_NOP4: case OP_NOP5: case OP_NOP6: case OP_NOP7: case OP_NOP8: case OP_NOP9: case OP_NOP10: { if (flags & SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_NOPS) @@ -1084,6 +1126,43 @@ bool TransactionSignatureChecker::CheckSig(const vector& vchSigIn return true; } +bool TransactionSignatureChecker::CheckLockTime(const CScriptNum& nLockTime) const +{ + // There are two times of nLockTime: lock-by-blockheight + // and lock-by-blocktime, distinguished by whether + // nLockTime < LOCKTIME_THRESHOLD. + // + // We want to compare apples to apples, so fail the script + // unless the type of nLockTime being tested is the same as + // the nLockTime in the transaction. + if (!( + (txTo->nLockTime < LOCKTIME_THRESHOLD && nLockTime < LOCKTIME_THRESHOLD) || + (txTo->nLockTime >= LOCKTIME_THRESHOLD && nLockTime >= LOCKTIME_THRESHOLD) + )) + return false; + + // Now that we know we're comparing apples-to-apples, the + // comparison is a simple numeric one. + if (nLockTime > (int64_t)txTo->nLockTime) + return false; + + // Finally the nLockTime feature can be disabled and thus + // CHECKLOCKTIMEVERIFY bypassed if every txin has been + // finalized by setting nSequence to maxint. The + // transaction would be allowed into the blockchain, making + // the opcode ineffective. + // + // Testing if this vin is not final is sufficient to + // prevent this condition. Alternatively we could test all + // inputs, but testing just this input minimizes the data + // required to prove correct CHECKLOCKTIMEVERIFY execution. + if (txTo->vin[nIn].IsFinal()) + return false; + + return true; +} + + bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, unsigned int flags, const BaseSignatureChecker& checker, ScriptError* serror) { set_error(serror, SCRIPT_ERR_UNKNOWN_ERROR); diff --git a/src/script/interpreter.h b/src/script/interpreter.h index fc64438f6..35d572f0a 100644 --- a/src/script/interpreter.h +++ b/src/script/interpreter.h @@ -76,6 +76,11 @@ enum // (softfork safe, BIP62 rule 6) // Note: CLEANSTACK should never be used without P2SH. SCRIPT_VERIFY_CLEANSTACK = (1U << 8), + + // Verify CHECKLOCKTIMEVERIFY + // + // See BIP65 for details. + SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY = (1U << 9), }; uint256 SignatureHash(const CScript &scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType); @@ -88,6 +93,11 @@ public: return false; } + virtual bool CheckLockTime(const CScriptNum& nLockTime) const + { + return false; + } + virtual ~BaseSignatureChecker() {} }; @@ -103,6 +113,7 @@ protected: public: TransactionSignatureChecker(const CTransaction* txToIn, unsigned int nInIn) : txTo(txToIn), nIn(nInIn) {} bool CheckSig(const std::vector& scriptSig, const std::vector& vchPubKey, const CScript& scriptCode) const; + bool CheckLockTime(const CScriptNum& nLockTime) const; }; class MutableTransactionSignatureChecker : public TransactionSignatureChecker diff --git a/src/script/script.h b/src/script/script.h index 45a06acc9..be2a57c3b 100644 --- a/src/script/script.h +++ b/src/script/script.h @@ -154,6 +154,7 @@ enum opcodetype // expansion OP_NOP1 = 0xb0, OP_NOP2 = 0xb1, + OP_CHECKLOCKTIMEVERIFY = OP_NOP2, OP_NOP3 = 0xb2, OP_NOP4 = 0xb3, OP_NOP5 = 0xb4, diff --git a/src/script/script_error.cpp b/src/script/script_error.cpp index d8ecfde1d..f1aa1fb40 100644 --- a/src/script/script_error.cpp +++ b/src/script/script_error.cpp @@ -47,6 +47,10 @@ const char* ScriptErrorString(const ScriptError serror) return "OP_RETURN was encountered"; case SCRIPT_ERR_UNBALANCED_CONDITIONAL: return "Invalid OP_IF construction"; + case SCRIPT_ERR_NEGATIVE_LOCKTIME: + return "Negative locktime"; + case SCRIPT_ERR_UNSATISFIED_LOCKTIME: + return "Locktime requirement not satisfied"; case SCRIPT_ERR_SIG_HASHTYPE: return "Signature hash type missing or not understood"; case SCRIPT_ERR_SIG_DER: diff --git a/src/script/script_error.h b/src/script/script_error.h index 6365680b2..bb10b8a29 100644 --- a/src/script/script_error.h +++ b/src/script/script_error.h @@ -35,6 +35,10 @@ typedef enum ScriptError_t SCRIPT_ERR_INVALID_ALTSTACK_OPERATION, SCRIPT_ERR_UNBALANCED_CONDITIONAL, + /* OP_CHECKLOCKTIMEVERIFY */ + SCRIPT_ERR_NEGATIVE_LOCKTIME, + SCRIPT_ERR_UNSATISFIED_LOCKTIME, + /* BIP62 */ SCRIPT_ERR_SIG_HASHTYPE, SCRIPT_ERR_SIG_DER, -- cgit v1.2.3 From ffd75adce01a78b3461b3ff05bcc2b530a9ce994 Mon Sep 17 00:00:00 2001 From: Peter Todd Date: Wed, 17 Dec 2014 00:31:44 -0500 Subject: Enable CHECKLOCKTIMEVERIFY as a standard script verify flag Transactions that fail CLTV verification will be rejected from the mempool, making it easy to test the feature. However blocks containing "invalid" CLTV-using transactions will still be accepted; this is *not* the soft-fork required to actually enable CLTV for production use. --- src/script/standard.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src/script') diff --git a/src/script/standard.h b/src/script/standard.h index a8b0acc98..3b401dfe4 100644 --- a/src/script/standard.h +++ b/src/script/standard.h @@ -50,7 +50,8 @@ static const unsigned int STANDARD_SCRIPT_VERIFY_FLAGS = MANDATORY_SCRIPT_VERIFY SCRIPT_VERIFY_MINIMALDATA | SCRIPT_VERIFY_NULLDUMMY | SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_NOPS | - SCRIPT_VERIFY_CLEANSTACK; + SCRIPT_VERIFY_CLEANSTACK | + SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY; /** For convenience, standard but not mandatory verify flags. */ static const unsigned int STANDARD_NOT_MANDATORY_VERIFY_FLAGS = STANDARD_SCRIPT_VERIFY_FLAGS & ~MANDATORY_SCRIPT_VERIFY_FLAGS; -- cgit v1.2.3 From 627b9deff4e8e18fc6d001b6eb9818c13c648820 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jorge=20Tim=C3=B3n?= Date: Wed, 24 Jun 2015 07:25:30 +0200 Subject: Policy: MOVEONLY: Create policy/policy.h with some constants --- src/script/sign.cpp | 3 ++- src/script/standard.h | 17 ----------------- 2 files changed, 2 insertions(+), 18 deletions(-) (limited to 'src/script') diff --git a/src/script/sign.cpp b/src/script/sign.cpp index 4543ca303..8b43183b6 100644 --- a/src/script/sign.cpp +++ b/src/script/sign.cpp @@ -5,9 +5,10 @@ #include "script/sign.h" -#include "primitives/transaction.h" #include "key.h" #include "keystore.h" +#include "policy/policy.h" +#include "primitives/transaction.h" #include "script/standard.h" #include "uint256.h" diff --git a/src/script/standard.h b/src/script/standard.h index 3b401dfe4..c4ad17dd8 100644 --- a/src/script/standard.h +++ b/src/script/standard.h @@ -39,23 +39,6 @@ extern unsigned nMaxDatacarrierBytes; */ static const unsigned int MANDATORY_SCRIPT_VERIFY_FLAGS = SCRIPT_VERIFY_P2SH; -/** - * Standard script verification flags that standard transactions will comply - * with. However scripts violating these flags may still be present in valid - * blocks and we must accept those blocks. - */ -static const unsigned int STANDARD_SCRIPT_VERIFY_FLAGS = MANDATORY_SCRIPT_VERIFY_FLAGS | - SCRIPT_VERIFY_DERSIG | - SCRIPT_VERIFY_STRICTENC | - SCRIPT_VERIFY_MINIMALDATA | - SCRIPT_VERIFY_NULLDUMMY | - SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_NOPS | - SCRIPT_VERIFY_CLEANSTACK | - SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY; - -/** For convenience, standard but not mandatory verify flags. */ -static const unsigned int STANDARD_NOT_MANDATORY_VERIFY_FLAGS = STANDARD_SCRIPT_VERIFY_FLAGS & ~MANDATORY_SCRIPT_VERIFY_FLAGS; - enum txnouttype { TX_NONSTANDARD, -- cgit v1.2.3 From 9238ecb41752e097443d0bc117df35ebd4ac932e Mon Sep 17 00:00:00 2001 From: Luke Dashjr Date: Sat, 11 Oct 2014 22:41:05 +0000 Subject: Policy: MOVEONLY: 3 functions to policy.o: - [script/standard.o] IsStandard - [main.o] IsStandardTx - [main.o] AreInputsStandard Also, don't use namespace std in policy.cpp --- src/script/standard.cpp | 20 -------------------- src/script/standard.h | 1 - 2 files changed, 21 deletions(-) (limited to 'src/script') diff --git a/src/script/standard.cpp b/src/script/standard.cpp index ce50e3aad..66657127a 100644 --- a/src/script/standard.cpp +++ b/src/script/standard.cpp @@ -180,26 +180,6 @@ int ScriptSigArgsExpected(txnouttype t, const std::vector vSolutions; - if (!Solver(scriptPubKey, whichType, vSolutions)) - return false; - - if (whichType == TX_MULTISIG) - { - unsigned char m = vSolutions.front()[0]; - unsigned char n = vSolutions.back()[0]; - // Support up to x-of-3 multisig txns as standard - if (n < 1 || n > 3) - return false; - if (m < 1 || m > n) - return false; - } - - return whichType != TX_NONSTANDARD; -} - bool ExtractDestination(const CScript& scriptPubKey, CTxDestination& addressRet) { vector vSolutions; diff --git a/src/script/standard.h b/src/script/standard.h index c4ad17dd8..46ae5f9f1 100644 --- a/src/script/standard.h +++ b/src/script/standard.h @@ -69,7 +69,6 @@ const char* GetTxnOutputType(txnouttype t); bool Solver(const CScript& scriptPubKey, txnouttype& typeRet, std::vector >& vSolutionsRet); int ScriptSigArgsExpected(txnouttype t, const std::vector >& vSolutions); -bool IsStandard(const CScript& scriptPubKey, txnouttype& whichType); bool ExtractDestination(const CScript& scriptPubKey, CTxDestination& addressRet); bool ExtractDestinations(const CScript& scriptPubKey, txnouttype& typeRet, std::vector& addressRet, int& nRequiredRet); -- cgit v1.2.3 From 5496253966abec287ea61a648cb518d14903f91f Mon Sep 17 00:00:00 2001 From: Jonas Schnelli Date: Wed, 1 Jul 2015 08:32:30 +0200 Subject: add CReserveScript to allow modular script keeping/returning - use one CReserveScript per mining thread --- src/script/script.h | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'src/script') diff --git a/src/script/script.h b/src/script/script.h index c09899aab..e39ca57f4 100644 --- a/src/script/script.h +++ b/src/script/script.h @@ -609,4 +609,13 @@ public: } }; +class CReserveScript +{ +public: + CScript reserveScript; + virtual void KeepScript() {} + CReserveScript() {} + virtual ~CReserveScript() {} +}; + #endif // BITCOIN_SCRIPT_SCRIPT_H -- cgit v1.2.3 From 5098c47b2430ded299c21620527ebd0544fe51e2 Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Thu, 9 Jul 2015 13:56:31 -0400 Subject: Implement accurate memory accounting for mempool --- src/script/script.cpp | 5 +++++ src/script/script.h | 3 +++ 2 files changed, 8 insertions(+) (limited to 'src/script') diff --git a/src/script/script.cpp b/src/script/script.cpp index fd3392473..b1d2ceeb9 100644 --- a/src/script/script.cpp +++ b/src/script/script.cpp @@ -260,3 +260,8 @@ std::string CScript::ToString() const } return str; } + +size_t CScript::DynamicMemoryUsage() const +{ + return memusage::DynamicUsage(*(static_cast*>(this))); +} diff --git a/src/script/script.h b/src/script/script.h index e39ca57f4..aea34d05f 100644 --- a/src/script/script.h +++ b/src/script/script.h @@ -6,6 +6,7 @@ #ifndef BITCOIN_SCRIPT_SCRIPT_H #define BITCOIN_SCRIPT_SCRIPT_H +#include "memusage.h" #include "crypto/common.h" #include @@ -607,6 +608,8 @@ public: // The default std::vector::clear() does not release memory. std::vector().swap(*this); } + + size_t DynamicMemoryUsage() const; }; class CReserveScript -- cgit v1.2.3 From 9e38d0f7451092f6a16853a2b0a37b68a5b3c3fb Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Fri, 17 Jul 2015 13:46:18 -0400 Subject: Separate core memory usage computation in core_memusage.h --- src/script/script.cpp | 5 ----- src/script/script.h | 3 --- 2 files changed, 8 deletions(-) (limited to 'src/script') diff --git a/src/script/script.cpp b/src/script/script.cpp index b1d2ceeb9..fd3392473 100644 --- a/src/script/script.cpp +++ b/src/script/script.cpp @@ -260,8 +260,3 @@ std::string CScript::ToString() const } return str; } - -size_t CScript::DynamicMemoryUsage() const -{ - return memusage::DynamicUsage(*(static_cast*>(this))); -} diff --git a/src/script/script.h b/src/script/script.h index aea34d05f..e39ca57f4 100644 --- a/src/script/script.h +++ b/src/script/script.h @@ -6,7 +6,6 @@ #ifndef BITCOIN_SCRIPT_SCRIPT_H #define BITCOIN_SCRIPT_SCRIPT_H -#include "memusage.h" #include "crypto/common.h" #include @@ -608,8 +607,6 @@ public: // The default std::vector::clear() does not release memory. std::vector().swap(*this); } - - size_t DynamicMemoryUsage() const; }; class CReserveScript -- cgit v1.2.3 From cfc3dd34284357262bcc7eef2714a210891276c0 Mon Sep 17 00:00:00 2001 From: Matt Corallo Date: Wed, 10 Jun 2015 00:03:08 -0700 Subject: Also remove pay-2-pubkey from watch when adding a priv key --- src/script/standard.cpp | 5 +++++ src/script/standard.h | 1 + 2 files changed, 6 insertions(+) (limited to 'src/script') diff --git a/src/script/standard.cpp b/src/script/standard.cpp index 66657127a..1d5aac7b3 100644 --- a/src/script/standard.cpp +++ b/src/script/standard.cpp @@ -286,6 +286,11 @@ CScript GetScriptForDestination(const CTxDestination& dest) return script; } +CScript GetScriptForRawPubKey(const CPubKey& pubKey) +{ + return CScript() << std::vector(pubKey.begin(), pubKey.end()) << OP_CHECKSIG; +} + CScript GetScriptForMultisig(int nRequired, const std::vector& keys) { CScript script; diff --git a/src/script/standard.h b/src/script/standard.h index 46ae5f9f1..9e17dac70 100644 --- a/src/script/standard.h +++ b/src/script/standard.h @@ -73,6 +73,7 @@ bool ExtractDestination(const CScript& scriptPubKey, CTxDestination& addressRet) bool ExtractDestinations(const CScript& scriptPubKey, txnouttype& typeRet, std::vector& addressRet, int& nRequiredRet); CScript GetScriptForDestination(const CTxDestination& dest); +CScript GetScriptForRawPubKey(const CPubKey& pubkey); CScript GetScriptForMultisig(int nRequired, const std::vector& keys); #endif // BITCOIN_SCRIPT_STANDARD_H -- cgit v1.2.3 From af3208bfa6967d6b35aecf0ba35d9d6bf0f8317e Mon Sep 17 00:00:00 2001 From: mruddy Date: Thu, 30 Jul 2015 19:56:00 -0400 Subject: Resolve issue 3166. These changes decode valid SIGHASH types on signatures in assembly (asm) representations of scriptSig scripts. This squashed commit incorporates substantial helpful feedback from jtimon, laanwj, and sipa. --- src/script/interpreter.cpp | 2 +- src/script/interpreter.h | 2 ++ src/script/script.cpp | 33 --------------------------------- src/script/script.h | 1 - 4 files changed, 3 insertions(+), 35 deletions(-) (limited to 'src/script') diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp index 0b78fdf5a..03af78bce 100644 --- a/src/script/interpreter.cpp +++ b/src/script/interpreter.cpp @@ -188,7 +188,7 @@ bool static IsDefinedHashtypeSignature(const valtype &vchSig) { return true; } -bool static CheckSignatureEncoding(const valtype &vchSig, unsigned int flags, ScriptError* serror) { +bool CheckSignatureEncoding(const vector &vchSig, unsigned int flags, ScriptError* serror) { // Empty signature. Not strictly DER encoded, but allowed to provide a // compact way to provide an invalid signature for use with CHECK(MULTI)SIG if (vchSig.size() == 0) { diff --git a/src/script/interpreter.h b/src/script/interpreter.h index 35d572f0a..213e8c765 100644 --- a/src/script/interpreter.h +++ b/src/script/interpreter.h @@ -83,6 +83,8 @@ enum SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY = (1U << 9), }; +bool CheckSignatureEncoding(const std::vector &vchSig, unsigned int flags, ScriptError* serror); + uint256 SignatureHash(const CScript &scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType); class BaseSignatureChecker diff --git a/src/script/script.cpp b/src/script/script.cpp index fd3392473..58dbade0e 100644 --- a/src/script/script.cpp +++ b/src/script/script.cpp @@ -8,16 +8,6 @@ #include "tinyformat.h" #include "utilstrencodings.h" -namespace { -inline std::string ValueString(const std::vector& vch) -{ - if (vch.size() <= 4) - return strprintf("%d", CScriptNum(vch, false).getint()); - else - return HexStr(vch); -} -} // anon namespace - using namespace std; const char* GetOpName(opcodetype opcode) @@ -237,26 +227,3 @@ bool CScript::IsPushOnly() const } return true; } - -std::string CScript::ToString() const -{ - std::string str; - opcodetype opcode; - std::vector vch; - const_iterator pc = begin(); - while (pc < end()) - { - if (!str.empty()) - str += " "; - if (!GetOp(pc, opcode, vch)) - { - str += "[error]"; - return str; - } - if (0 <= opcode && opcode <= OP_PUSHDATA4) - str += ValueString(vch); - else - str += GetOpName(opcode); - } - return str; -} diff --git a/src/script/script.h b/src/script/script.h index e39ca57f4..f0725bbbf 100644 --- a/src/script/script.h +++ b/src/script/script.h @@ -601,7 +601,6 @@ public: return (size() > 0 && *begin() == OP_RETURN); } - std::string ToString() const; void clear() { // The default std::vector::clear() does not release memory. -- cgit v1.2.3 From e846b2a1e96f27257cbb79f7f61bfaf3d873bb97 Mon Sep 17 00:00:00 2001 From: Mark Friedenbach Date: Mon, 10 Aug 2015 18:08:30 -0700 Subject: Correct a possibly intentional pun that is nevertheless hard to read: "two times of nLockTime." What is meant is that there are two kinds, or categories of nLockTime. --- src/script/interpreter.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/script') diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp index 0b78fdf5a..bd5e54b33 100644 --- a/src/script/interpreter.cpp +++ b/src/script/interpreter.cpp @@ -1128,7 +1128,7 @@ bool TransactionSignatureChecker::CheckSig(const vector& vchSigIn bool TransactionSignatureChecker::CheckLockTime(const CScriptNum& nLockTime) const { - // There are two times of nLockTime: lock-by-blockheight + // There are two kinds of nLockTime: lock-by-blockheight // and lock-by-blocktime, distinguished by whether // nLockTime < LOCKTIME_THRESHOLD. // -- cgit v1.2.3 From 6a07eb676a020b0035173facb25f92f1ff6380f7 Mon Sep 17 00:00:00 2001 From: Peter Todd Date: Mon, 13 Oct 2014 10:14:58 -0400 Subject: Make TX_SCRIPTHASH clear vSolutionsRet first Previously unlike other transaction types the TX_SCRIPTHASH would not clear vSolutionsRet, which means that unlike other transaction types if it was called twice in a row you would get the result of the previous invocation as well. --- src/script/standard.cpp | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src/script') diff --git a/src/script/standard.cpp b/src/script/standard.cpp index 1d5aac7b3..59496d795 100644 --- a/src/script/standard.cpp +++ b/src/script/standard.cpp @@ -58,6 +58,8 @@ bool Solver(const CScript& scriptPubKey, txnouttype& typeRet, vector Date: Tue, 4 Nov 2014 12:38:56 -0500 Subject: Add IsPushOnly(const_iterator pc) Allows IsPushOnly() to be applied to just part of the script for OP_RETURN outputs. --- src/script/script.cpp | 8 ++++++-- src/script/script.h | 1 + 2 files changed, 7 insertions(+), 2 deletions(-) (limited to 'src/script') diff --git a/src/script/script.cpp b/src/script/script.cpp index 58dbade0e..0a8ec9fa7 100644 --- a/src/script/script.cpp +++ b/src/script/script.cpp @@ -210,9 +210,8 @@ bool CScript::IsPayToScriptHash() const this->at(22) == OP_EQUAL); } -bool CScript::IsPushOnly() const +bool CScript::IsPushOnly(const_iterator pc) const { - const_iterator pc = begin(); while (pc < end()) { opcodetype opcode; @@ -227,3 +226,8 @@ bool CScript::IsPushOnly() const } return true; } + +bool CScript::IsPushOnly() const +{ + return this->IsPushOnly(begin()); +} diff --git a/src/script/script.h b/src/script/script.h index f0725bbbf..3923a559b 100644 --- a/src/script/script.h +++ b/src/script/script.h @@ -589,6 +589,7 @@ public: bool IsPayToScriptHash() const; /** Called by IsStandardTx and P2SH/BIP62 VerifyScript (which makes it consensus-critical). */ + bool IsPushOnly(const_iterator pc) const; bool IsPushOnly() const; /** -- cgit v1.2.3 From da894ab5da222ad317039eb008ec6443fb9113d9 Mon Sep 17 00:00:00 2001 From: Peter Todd Date: Mon, 13 Oct 2014 10:18:05 -0400 Subject: Accept any sequence of PUSHDATAs in OP_RETURN outputs Previously only one PUSHDATA was allowed, needlessly limiting applications such as matching OP_RETURN contents with bloom filters that operate on a per-PUSHDATA level. Now any combination that passes IsPushOnly() is allowed, so long as the total size of the scriptPubKey is less than 42 bytes. (unchanged modulo non-minimal PUSHDATA encodings) Also, this fixes the odd bug where previously the PUSHDATA could be replaced by any single opcode, even sigops consuming opcodes such as CHECKMULTISIG. (20 sigops!) --- src/script/script.cpp | 2 +- src/script/script.h | 1 - src/script/standard.cpp | 21 ++++++++++----------- src/script/standard.h | 2 +- 4 files changed, 12 insertions(+), 14 deletions(-) (limited to 'src/script') diff --git a/src/script/script.cpp b/src/script/script.cpp index 0a8ec9fa7..9a0c067a3 100644 --- a/src/script/script.cpp +++ b/src/script/script.cpp @@ -144,7 +144,7 @@ const char* GetOpName(opcodetype opcode) case OP_INVALIDOPCODE : return "OP_INVALIDOPCODE"; // Note: - // The template matching params OP_SMALLDATA/etc are defined in opcodetype enum + // 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. diff --git a/src/script/script.h b/src/script/script.h index 3923a559b..cdc9a71bb 100644 --- a/src/script/script.h +++ b/src/script/script.h @@ -167,7 +167,6 @@ enum opcodetype // template matching params - OP_SMALLDATA = 0xf9, OP_SMALLINTEGER = 0xfa, OP_PUBKEYS = 0xfb, OP_PUBKEYHASH = 0xfd, diff --git a/src/script/standard.cpp b/src/script/standard.cpp index 59496d795..bfef8afa1 100644 --- a/src/script/standard.cpp +++ b/src/script/standard.cpp @@ -51,11 +51,6 @@ bool Solver(const CScript& scriptPubKey, txnouttype& typeRet, vector= 1 && scriptPubKey[0] == OP_RETURN && scriptPubKey.IsPushOnly(scriptPubKey.begin()+1)) { + typeRet = TX_NULL_DATA; + return true; + } + // Scan templates const CScript& script1 = scriptPubKey; BOOST_FOREACH(const PAIRTYPE(txnouttype, CScript)& tplate, mTemplates) @@ -142,12 +147,6 @@ bool Solver(const CScript& scriptPubKey, txnouttype& typeRet, vector nMaxDatacarrierBytes) - break; - } else if (opcode1 != opcode2 || vch1 != vch2) { // Others must match exactly diff --git a/src/script/standard.h b/src/script/standard.h index 9e17dac70..ae1bbecca 100644 --- a/src/script/standard.h +++ b/src/script/standard.h @@ -25,7 +25,7 @@ public: CScriptID(const uint160& in) : uint160(in) {} }; -static const unsigned int MAX_OP_RETURN_RELAY = 80; //! bytes +static const unsigned int MAX_OP_RETURN_RELAY = 83; //! bytes (+1 for OP_RETURN, +2 for the pushdata opcodes) extern unsigned nMaxDatacarrierBytes; /** -- cgit v1.2.3 From 287f54fc90c29301faede8d4ac2ea24a91441917 Mon Sep 17 00:00:00 2001 From: Peter Todd Date: Sun, 28 Jun 2015 14:30:50 -0400 Subject: Add CHECKLOCKTIMEVERIFY (BIP65) soft-fork logic Based on the earlier BIP66 soft-fork logic implemented by Pieter Wuille's 5a47811da5158df763aa2fca09ce646ee0c51e7b --- src/script/bitcoinconsensus.h | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'src/script') diff --git a/src/script/bitcoinconsensus.h b/src/script/bitcoinconsensus.h index 032057779..a48ff1e18 100644 --- a/src/script/bitcoinconsensus.h +++ b/src/script/bitcoinconsensus.h @@ -44,9 +44,10 @@ typedef enum bitcoinconsensus_error_t /** Script verification flags */ enum { - bitcoinconsensus_SCRIPT_FLAGS_VERIFY_NONE = 0, - bitcoinconsensus_SCRIPT_FLAGS_VERIFY_P2SH = (1U << 0), // evaluate P2SH (BIP16) subscripts - bitcoinconsensus_SCRIPT_FLAGS_VERIFY_DERSIG = (1U << 2), // enforce strict DER (BIP66) compliance + bitcoinconsensus_SCRIPT_FLAGS_VERIFY_NONE = 0, + bitcoinconsensus_SCRIPT_FLAGS_VERIFY_P2SH = (1U << 0), // evaluate P2SH (BIP16) subscripts + bitcoinconsensus_SCRIPT_FLAGS_VERIFY_DERSIG = (1U << 2), // enforce strict DER (BIP66) compliance + bitcoinconsensus_SCRIPT_FLAGS_VERIFY_CHECKLOCKTIMEVERIFY = (1U << 9), // enable CHECKLOCKTIMEVERIFY (BIP65) }; /// Returns 1 if the input nIn of the serialized transaction pointed to by -- cgit v1.2.3 From b48da5c1894a70f8fa2a50deb2e056c38ed27ecb Mon Sep 17 00:00:00 2001 From: David Hill Date: Tue, 13 Oct 2015 09:56:45 -0400 Subject: script: Remove magic numbers This adds two new constants, MAX_OPS_PER_SCRIPT and MAX_PUBKEYS_PER_MULTISIG. --- src/script/interpreter.cpp | 6 +++--- src/script/script.cpp | 2 +- src/script/script.h | 9 ++++++++- 3 files changed, 12 insertions(+), 5 deletions(-) (limited to 'src/script') diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp index d3aec2602..6a20d497c 100644 --- a/src/script/interpreter.cpp +++ b/src/script/interpreter.cpp @@ -273,7 +273,7 @@ bool EvalScript(vector >& stack, const CScript& script, un return set_error(serror, SCRIPT_ERR_PUSH_SIZE); // Note how OP_RESERVED does not count towards the opcode limit. - if (opcode > OP_16 && ++nOpCount > 201) + if (opcode > OP_16 && ++nOpCount > MAX_OPS_PER_SCRIPT) return set_error(serror, SCRIPT_ERR_OP_COUNT); if (opcode == OP_CAT || @@ -869,10 +869,10 @@ bool EvalScript(vector >& stack, const CScript& script, un return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION); int nKeysCount = CScriptNum(stacktop(-i), fRequireMinimal).getint(); - if (nKeysCount < 0 || nKeysCount > 20) + if (nKeysCount < 0 || nKeysCount > MAX_PUBKEYS_PER_MULTISIG) return set_error(serror, SCRIPT_ERR_PUBKEY_COUNT); nOpCount += nKeysCount; - if (nOpCount > 201) + if (nOpCount > MAX_OPS_PER_SCRIPT) return set_error(serror, SCRIPT_ERR_OP_COUNT); int ikey = ++i; i += nKeysCount; diff --git a/src/script/script.cpp b/src/script/script.cpp index 9a0c067a3..263c89def 100644 --- a/src/script/script.cpp +++ b/src/script/script.cpp @@ -170,7 +170,7 @@ unsigned int CScript::GetSigOpCount(bool fAccurate) const if (fAccurate && lastOpcode >= OP_1 && lastOpcode <= OP_16) n += DecodeOP_N(lastOpcode); else - n += 20; + n += MAX_PUBKEYS_PER_MULTISIG; } lastOpcode = opcode; } diff --git a/src/script/script.h b/src/script/script.h index cdc9a71bb..a38d33a18 100644 --- a/src/script/script.h +++ b/src/script/script.h @@ -17,7 +17,14 @@ #include #include -static const unsigned int MAX_SCRIPT_ELEMENT_SIZE = 520; // bytes +// Maximum number of bytes pushable to the stack +static const unsigned int MAX_SCRIPT_ELEMENT_SIZE = 520; + +// Maximum number of non-push operations per script +static const int MAX_OPS_PER_SCRIPT = 201; + +// Maximum number of public keys per multisig +static const int MAX_PUBKEYS_PER_MULTISIG = 20; // Threshold for nLockTime: below this value it is interpreted as block number, // otherwise as UNIX timestamp. -- cgit v1.2.3 From 830e3f3d027ba5c8121eed0f6a9ce99961352572 Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Fri, 30 Oct 2015 23:14:38 +0100 Subject: Make sigcache faster and more efficient --- src/script/sigcache.cpp | 80 ++++++++++++++++++++++++++++--------------------- src/script/sigcache.h | 4 +++ 2 files changed, 50 insertions(+), 34 deletions(-) (limited to 'src/script') diff --git a/src/script/sigcache.cpp b/src/script/sigcache.cpp index 099b4ad0e..9dc7f0fcd 100644 --- a/src/script/sigcache.cpp +++ b/src/script/sigcache.cpp @@ -5,16 +5,29 @@ #include "sigcache.h" +#include "memusage.h" #include "pubkey.h" #include "random.h" #include "uint256.h" #include "util.h" #include -#include +#include namespace { +/** + * We're hashing a nonce into the entries themselves, so we don't need extra + * blinding in the set hash computation. + */ +class CSignatureCacheHasher +{ +public: + size_t operator()(const uint256& key) const { + return key.GetCheapHash(); + } +}; + /** * Valid signature cache, to avoid doing expensive ECDSA signature checking * twice for every transaction (once when accepted into memory pool, and @@ -23,52 +36,48 @@ namespace { class CSignatureCache { private: - //! sigdata_type is (signature hash, signature, public key): - typedef boost::tuple, CPubKey> sigdata_type; - std::set< sigdata_type> setValid; + //! Entries are SHA256(nonce || signature hash || public key || signature): + uint256 nonce; + typedef boost::unordered_set map_type; + map_type setValid; boost::shared_mutex cs_sigcache; + public: + CSignatureCache() + { + GetRandBytes(nonce.begin(), 32); + } + + void + ComputeEntry(uint256& entry, const uint256 &hash, const std::vector& vchSig, const CPubKey& pubkey) + { + CSHA256().Write(nonce.begin(), 32).Write(hash.begin(), 32).Write(&pubkey[0], pubkey.size()).Write(&vchSig[0], vchSig.size()).Finalize(entry.begin()); + } + bool - Get(const uint256 &hash, const std::vector& vchSig, const CPubKey& pubKey) + Get(const uint256& entry) { boost::shared_lock lock(cs_sigcache); - - sigdata_type k(hash, vchSig, pubKey); - std::set::iterator mi = setValid.find(k); - if (mi != setValid.end()) - return true; - return false; + return setValid.count(entry); } - void Set(const uint256 &hash, const std::vector& vchSig, const CPubKey& pubKey) + void Set(const uint256& entry) { - // DoS prevention: limit cache size to less than 10MB - // (~200 bytes per cache entry times 50,000 entries) - // Since there are a maximum of 20,000 signature operations per block - // 50,000 is a reasonable default. - int64_t nMaxCacheSize = GetArg("-maxsigcachesize", 50000); + size_t nMaxCacheSize = GetArg("-maxsigcachesize", DEFAULT_MAX_SIG_CACHE_SIZE) * ((size_t) 1 << 20); if (nMaxCacheSize <= 0) return; boost::unique_lock lock(cs_sigcache); - - while (static_cast(setValid.size()) > nMaxCacheSize) + while (memusage::DynamicUsage(setValid) > nMaxCacheSize) { - // Evict a random entry. Random because that helps - // foil would-be DoS attackers who might try to pre-generate - // and re-use a set of valid signatures just-slightly-greater - // than our cache size. - uint256 randomHash = GetRandHash(); - std::vector unused; - std::set::iterator it = - setValid.lower_bound(sigdata_type(randomHash, unused, unused)); - if (it == setValid.end()) - it = setValid.begin(); - setValid.erase(*it); + map_type::size_type s = GetRand(setValid.bucket_count()); + map_type::local_iterator it = setValid.begin(s); + if (it != setValid.end(s)) { + setValid.erase(*it); + } } - sigdata_type k(hash, vchSig, pubKey); - setValid.insert(k); + setValid.insert(entry); } }; @@ -78,13 +87,16 @@ bool CachingTransactionSignatureChecker::VerifySignature(const std::vector +// DoS prevention: limit cache size to less than 40MB (over 500000 +// entries on 64-bit systems). +static const unsigned int DEFAULT_MAX_SIG_CACHE_SIZE = 40; + class CPubKey; class CachingTransactionSignatureChecker : public TransactionSignatureChecker -- cgit v1.2.3 From 0b9e9dca4e88e41f7dae4fd9cd8e0f93fabafe01 Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Fri, 30 Oct 2015 23:38:40 +0100 Subject: Evict sigcache entries that are seen in a block --- src/script/sigcache.cpp | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) (limited to 'src/script') diff --git a/src/script/sigcache.cpp b/src/script/sigcache.cpp index 9dc7f0fcd..eee96e7c2 100644 --- a/src/script/sigcache.cpp +++ b/src/script/sigcache.cpp @@ -62,6 +62,12 @@ public: return setValid.count(entry); } + void Erase(const uint256& entry) + { + boost::unique_lock lock(cs_sigcache); + setValid.erase(entry); + } + void Set(const uint256& entry) { size_t nMaxCacheSize = GetArg("-maxsigcachesize", DEFAULT_MAX_SIG_CACHE_SIZE) * ((size_t) 1 << 20); @@ -90,13 +96,18 @@ bool CachingTransactionSignatureChecker::VerifySignature(const std::vector Date: Thu, 29 Oct 2015 07:11:24 +0100 Subject: Prevector type --- src/script/interpreter.cpp | 2 +- src/script/script.cpp | 6 +++--- src/script/script.h | 14 +++++++++----- src/script/sign.cpp | 4 ++-- 4 files changed, 15 insertions(+), 11 deletions(-) (limited to 'src/script') diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp index 6a20d497c..e0853fef4 100644 --- a/src/script/interpreter.cpp +++ b/src/script/interpreter.cpp @@ -1032,7 +1032,7 @@ public: // Serialize the script if (nInput != nIn) // Blank out other inputs' signatures - ::Serialize(s, CScript(), nType, nVersion); + ::Serialize(s, CScriptBase(), nType, nVersion); else SerializeScriptCode(s, nType, nVersion); // Serialize the nSequence diff --git a/src/script/script.cpp b/src/script/script.cpp index 263c89def..9c77ed9fc 100644 --- a/src/script/script.cpp +++ b/src/script/script.cpp @@ -205,9 +205,9 @@ bool CScript::IsPayToScriptHash() const { // Extra-fast test for pay-to-script-hash CScripts: return (this->size() == 23 && - this->at(0) == OP_HASH160 && - this->at(1) == 0x14 && - this->at(22) == OP_EQUAL); + (*this)[0] == OP_HASH160 && + (*this)[1] == 0x14 && + (*this)[22] == OP_EQUAL); } bool CScript::IsPushOnly(const_iterator pc) const diff --git a/src/script/script.h b/src/script/script.h index a38d33a18..3650957fc 100644 --- a/src/script/script.h +++ b/src/script/script.h @@ -7,6 +7,7 @@ #define BITCOIN_SCRIPT_SCRIPT_H #include "crypto/common.h" +#include "prevector.h" #include #include @@ -354,8 +355,10 @@ private: int64_t m_value; }; +typedef prevector<28, unsigned char> CScriptBase; + /** Serialized script, used inside transaction inputs and outputs */ -class CScript : public std::vector +class CScript : public CScriptBase { protected: CScript& push_int64(int64_t n) @@ -376,9 +379,10 @@ protected: } public: CScript() { } - CScript(const CScript& b) : std::vector(b.begin(), b.end()) { } - CScript(const_iterator pbegin, const_iterator pend) : std::vector(pbegin, pend) { } - CScript(const unsigned char* pbegin, const unsigned char* pend) : std::vector(pbegin, pend) { } + CScript(const CScript& b) : CScriptBase(b.begin(), b.end()) { } + CScript(const_iterator pbegin, const_iterator pend) : CScriptBase(pbegin, pend) { } + CScript(std::vector::const_iterator pbegin, std::vector::const_iterator pend) : CScriptBase(pbegin, pend) { } + CScript(const unsigned char* pbegin, const unsigned char* pend) : CScriptBase(pbegin, pend) { } CScript& operator+=(const CScript& b) { @@ -611,7 +615,7 @@ public: void clear() { // The default std::vector::clear() does not release memory. - std::vector().swap(*this); + CScriptBase().swap(*this); } }; diff --git a/src/script/sign.cpp b/src/script/sign.cpp index 8b43183b6..90f557fc6 100644 --- a/src/script/sign.cpp +++ b/src/script/sign.cpp @@ -16,7 +16,7 @@ using namespace std; -typedef vector valtype; +typedef std::vector valtype; TransactionSignatureCreator::TransactionSignatureCreator(const CKeyStore* keystoreIn, const CTransaction* txToIn, unsigned int nInIn, int nHashTypeIn) : BaseSignatureCreator(keystoreIn), txTo(txToIn), nIn(nInIn), nHashType(nHashTypeIn), checker(txTo, nIn) {} @@ -118,7 +118,7 @@ bool ProduceSignature(const BaseSignatureCreator& creator, const CScript& fromPu bool fSolved = SignStep(creator, subscript, scriptSig, subType) && subType != TX_SCRIPTHASH; // Append serialized subscript whether or not it is completely signed: - scriptSig << static_cast(subscript); + scriptSig << valtype(subscript.begin(), subscript.end()); if (!fSolved) return false; } -- cgit v1.2.3 From 6e182686163ce3c15b878bd78c41d8d18db344f1 Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Tue, 28 Jul 2015 20:11:20 +0200 Subject: Switch to libsecp256k1-based validation for ECDSA --- src/script/bitcoinconsensus.cpp | 9 ++++++++- src/script/interpreter.cpp | 13 ++----------- 2 files changed, 10 insertions(+), 12 deletions(-) (limited to 'src/script') diff --git a/src/script/bitcoinconsensus.cpp b/src/script/bitcoinconsensus.cpp index b0d5faaf7..79504f6ad 100644 --- a/src/script/bitcoinconsensus.cpp +++ b/src/script/bitcoinconsensus.cpp @@ -6,6 +6,7 @@ #include "bitcoinconsensus.h" #include "primitives/transaction.h" +#include "pubkey.h" #include "script/interpreter.h" #include "version.h" @@ -60,7 +61,13 @@ inline int set_error(bitcoinconsensus_error* ret, bitcoinconsensus_error serror) return 0; } -} // anon namespace +struct ECCryptoClosure +{ + ECCVerifyHandle handle; +}; + +ECCryptoClosure instance_of_eccryptoclosure; +} int bitcoinconsensus_verify_script(const unsigned char *scriptPubKey, unsigned int scriptPubKeyLen, const unsigned char *txTo , unsigned int txToLen, diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp index 6a20d497c..8dcab832c 100644 --- a/src/script/interpreter.cpp +++ b/src/script/interpreter.cpp @@ -9,7 +9,6 @@ #include "crypto/ripemd160.h" #include "crypto/sha1.h" #include "crypto/sha256.h" -#include "eccryptoverify.h" #include "pubkey.h" #include "script/script.h" #include "uint256.h" @@ -165,16 +164,8 @@ bool static IsLowDERSignature(const valtype &vchSig, ScriptError* serror) { if (!IsValidSignatureEncoding(vchSig)) { return set_error(serror, SCRIPT_ERR_SIG_DER); } - unsigned int nLenR = vchSig[3]; - unsigned int nLenS = vchSig[5+nLenR]; - const unsigned char *S = &vchSig[6+nLenR]; - // If the S value is above the order of the curve divided by two, its - // complement modulo the order could have been used instead, which is - // one byte shorter when encoded correctly. - if (!eccrypto::CheckSignatureElement(S, nLenS, true)) - return set_error(serror, SCRIPT_ERR_SIG_HIGH_S); - - return true; + std::vector vchSigCopy(vchSig.begin(), vchSig.begin() + vchSig.size() - 1); + return CPubKey::CheckLowS(vchSigCopy); } bool static IsDefinedHashtypeSignature(const valtype &vchSig) { -- cgit v1.2.3 From b966aa836a3bc5bfa1314248258308f0026d41bb Mon Sep 17 00:00:00 2001 From: Luke Dashjr Date: Sat, 27 Jun 2015 19:21:41 +0000 Subject: Constrain constant values to a single location in code --- src/script/standard.cpp | 1 + src/script/standard.h | 1 + 2 files changed, 2 insertions(+) (limited to 'src/script') diff --git a/src/script/standard.cpp b/src/script/standard.cpp index bfef8afa1..60cf7ae49 100644 --- a/src/script/standard.cpp +++ b/src/script/standard.cpp @@ -16,6 +16,7 @@ using namespace std; typedef vector valtype; +bool fAcceptDatacarrier = true; unsigned nMaxDatacarrierBytes = MAX_OP_RETURN_RELAY; CScriptID::CScriptID(const CScript& in) : uint160(Hash160(in.begin(), in.end())) {} diff --git a/src/script/standard.h b/src/script/standard.h index ae1bbecca..140306861 100644 --- a/src/script/standard.h +++ b/src/script/standard.h @@ -26,6 +26,7 @@ public: }; static const unsigned int MAX_OP_RETURN_RELAY = 83; //! bytes (+1 for OP_RETURN, +2 for the pushdata opcodes) +extern bool fAcceptDatacarrier; extern unsigned nMaxDatacarrierBytes; /** -- cgit v1.2.3 From faf93f37fe47fe326fcc4955302a66f24eb13b65 Mon Sep 17 00:00:00 2001 From: MarcoFalke Date: Mon, 9 Nov 2015 19:16:38 +0100 Subject: [trivial] Reuse translation and cleanup DEFAULT_* values * DEFAULT_DISABLE_SAFEMODE = false * Use DEFAULT_* constants for extern bools --- src/script/standard.cpp | 2 +- src/script/standard.h | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) (limited to 'src/script') diff --git a/src/script/standard.cpp b/src/script/standard.cpp index 60cf7ae49..4863b9639 100644 --- a/src/script/standard.cpp +++ b/src/script/standard.cpp @@ -16,7 +16,7 @@ using namespace std; typedef vector valtype; -bool fAcceptDatacarrier = true; +bool fAcceptDatacarrier = DEFAULT_ACCEPT_DATACARRIER; unsigned nMaxDatacarrierBytes = MAX_OP_RETURN_RELAY; CScriptID::CScriptID(const CScript& in) : uint160(Hash160(in.begin(), in.end())) {} diff --git a/src/script/standard.h b/src/script/standard.h index 140306861..2b9fbe78d 100644 --- a/src/script/standard.h +++ b/src/script/standard.h @@ -13,6 +13,8 @@ #include +static const bool DEFAULT_ACCEPT_DATACARRIER = true; + class CKeyID; class CScript; -- cgit v1.2.3 From fa24439ff3d8ab5b9efaf66ef4dae6713b88cb35 Mon Sep 17 00:00:00 2001 From: MarcoFalke Date: Sun, 13 Dec 2015 17:58:29 +0100 Subject: Bump copyright headers to 2015 --- src/script/bitcoinconsensus.cpp | 2 +- src/script/bitcoinconsensus.h | 2 +- src/script/interpreter.cpp | 2 +- src/script/interpreter.h | 2 +- src/script/script.cpp | 2 +- src/script/script.h | 2 +- src/script/sigcache.cpp | 2 +- src/script/sigcache.h | 2 +- src/script/sign.cpp | 2 +- src/script/sign.h | 2 +- src/script/standard.cpp | 2 +- src/script/standard.h | 2 +- 12 files changed, 12 insertions(+), 12 deletions(-) (limited to 'src/script') diff --git a/src/script/bitcoinconsensus.cpp b/src/script/bitcoinconsensus.cpp index 79504f6ad..47ad1d080 100644 --- a/src/script/bitcoinconsensus.cpp +++ b/src/script/bitcoinconsensus.cpp @@ -1,5 +1,5 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2014 The Bitcoin Core developers +// Copyright (c) 2009-2015 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/script/bitcoinconsensus.h b/src/script/bitcoinconsensus.h index a48ff1e18..5b8c33c6b 100644 --- a/src/script/bitcoinconsensus.h +++ b/src/script/bitcoinconsensus.h @@ -1,5 +1,5 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2014 The Bitcoin Core developers +// Copyright (c) 2009-2015 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp index 57e0edc4b..a92822326 100644 --- a/src/script/interpreter.cpp +++ b/src/script/interpreter.cpp @@ -1,5 +1,5 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2014 The Bitcoin Core developers +// Copyright (c) 2009-2015 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/script/interpreter.h b/src/script/interpreter.h index 213e8c765..7b34547ff 100644 --- a/src/script/interpreter.h +++ b/src/script/interpreter.h @@ -1,5 +1,5 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2014 The Bitcoin Core developers +// Copyright (c) 2009-2015 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/script/script.cpp b/src/script/script.cpp index 9c77ed9fc..fa1307d61 100644 --- a/src/script/script.cpp +++ b/src/script/script.cpp @@ -1,5 +1,5 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2014 The Bitcoin Core developers +// Copyright (c) 2009-2015 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/script/script.h b/src/script/script.h index 3650957fc..2b95a4af8 100644 --- a/src/script/script.h +++ b/src/script/script.h @@ -1,5 +1,5 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2014 The Bitcoin Core developers +// Copyright (c) 2009-2015 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/script/sigcache.cpp b/src/script/sigcache.cpp index eee96e7c2..bdc0bfdc1 100644 --- a/src/script/sigcache.cpp +++ b/src/script/sigcache.cpp @@ -1,5 +1,5 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2014 The Bitcoin Core developers +// Copyright (c) 2009-2015 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/script/sigcache.h b/src/script/sigcache.h index 226997256..be1df09c2 100644 --- a/src/script/sigcache.h +++ b/src/script/sigcache.h @@ -1,5 +1,5 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2014 The Bitcoin Core developers +// Copyright (c) 2009-2015 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/script/sign.cpp b/src/script/sign.cpp index 90f557fc6..2f4111f78 100644 --- a/src/script/sign.cpp +++ b/src/script/sign.cpp @@ -1,5 +1,5 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2014 The Bitcoin Core developers +// Copyright (c) 2009-2015 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/script/sign.h b/src/script/sign.h index 13f45007d..47a9cde7f 100644 --- a/src/script/sign.h +++ b/src/script/sign.h @@ -1,5 +1,5 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2014 The Bitcoin Core developers +// Copyright (c) 2009-2015 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/script/standard.cpp b/src/script/standard.cpp index 4863b9639..30935768a 100644 --- a/src/script/standard.cpp +++ b/src/script/standard.cpp @@ -1,5 +1,5 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2014 The Bitcoin Core developers +// Copyright (c) 2009-2015 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/script/standard.h b/src/script/standard.h index 2b9fbe78d..6bac6e409 100644 --- a/src/script/standard.h +++ b/src/script/standard.h @@ -1,5 +1,5 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2014 The Bitcoin Core developers +// Copyright (c) 2009-2015 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -- cgit v1.2.3 From 37d271d7cce44885f835292ffe99b54399b014d6 Mon Sep 17 00:00:00 2001 From: mb300sd Date: Mon, 14 Dec 2015 14:21:34 -0500 Subject: Rename OP_NOP2 to OP_CHECKLOCKTIMEVERIFY. --- src/script/script.cpp | 2 +- src/script/script.h | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'src/script') diff --git a/src/script/script.cpp b/src/script/script.cpp index 9c77ed9fc..a7ba57e65 100644 --- a/src/script/script.cpp +++ b/src/script/script.cpp @@ -131,7 +131,7 @@ const char* GetOpName(opcodetype opcode) // expanson case OP_NOP1 : return "OP_NOP1"; - case OP_NOP2 : return "OP_NOP2"; + case OP_CHECKLOCKTIMEVERIFY : return "OP_CHECKLOCKTIMEVERIFY"; case OP_NOP3 : return "OP_NOP3"; case OP_NOP4 : return "OP_NOP4"; case OP_NOP5 : return "OP_NOP5"; diff --git a/src/script/script.h b/src/script/script.h index 3650957fc..7a37b66cc 100644 --- a/src/script/script.h +++ b/src/script/script.h @@ -162,8 +162,8 @@ enum opcodetype // expansion OP_NOP1 = 0xb0, - OP_NOP2 = 0xb1, - OP_CHECKLOCKTIMEVERIFY = OP_NOP2, + OP_CHECKLOCKTIMEVERIFY = 0xb1, + OP_NOP2 = OP_CHECKLOCKTIMEVERIFY, OP_NOP3 = 0xb2, OP_NOP4 = 0xb3, OP_NOP5 = 0xb4, -- cgit v1.2.3 From 5d743099b5fe77ba423110bea4f5dfd854fef3b2 Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Thu, 21 Jan 2016 13:15:19 +0100 Subject: Get rid of inaccurate ScriptSigArgsExpected (cherry picked from commit 52b29dca7670c3f6d2ab918c0fff1d17c4e494ad) --- src/script/standard.cpp | 21 --------------------- src/script/standard.h | 1 - 2 files changed, 22 deletions(-) (limited to 'src/script') diff --git a/src/script/standard.cpp b/src/script/standard.cpp index 30935768a..67b6af327 100644 --- a/src/script/standard.cpp +++ b/src/script/standard.cpp @@ -161,27 +161,6 @@ bool Solver(const CScript& scriptPubKey, txnouttype& typeRet, vector >& vSolutions) -{ - switch (t) - { - case TX_NONSTANDARD: - case TX_NULL_DATA: - return -1; - case TX_PUBKEY: - return 1; - case TX_PUBKEYHASH: - return 2; - case TX_MULTISIG: - if (vSolutions.size() < 1 || vSolutions[0].size() < 1) - return -1; - return vSolutions[0][0] + 1; - case TX_SCRIPTHASH: - return 1; // doesn't include args needed by the script - } - return -1; -} - bool ExtractDestination(const CScript& scriptPubKey, CTxDestination& addressRet) { vector vSolutions; diff --git a/src/script/standard.h b/src/script/standard.h index 6bac6e409..64bf010ec 100644 --- a/src/script/standard.h +++ b/src/script/standard.h @@ -71,7 +71,6 @@ typedef boost::variant CTxDestination; const char* GetTxnOutputType(txnouttype t); bool Solver(const CScript& scriptPubKey, txnouttype& typeRet, std::vector >& vSolutionsRet); -int ScriptSigArgsExpected(txnouttype t, const std::vector >& vSolutions); bool ExtractDestination(const CScript& scriptPubKey, CTxDestination& addressRet); bool ExtractDestinations(const CScript& scriptPubKey, txnouttype& typeRet, std::vector& addressRet, int& nRequiredRet); -- cgit v1.2.3 From 9d95187d5ddee56b6dfb55985008bdf70aed31f2 Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Wed, 10 Feb 2016 14:19:20 +0100 Subject: Correctly report high-S violations --- src/script/interpreter.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'src/script') diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp index a92822326..265131ae0 100644 --- a/src/script/interpreter.cpp +++ b/src/script/interpreter.cpp @@ -165,7 +165,10 @@ bool static IsLowDERSignature(const valtype &vchSig, ScriptError* serror) { return set_error(serror, SCRIPT_ERR_SIG_DER); } std::vector vchSigCopy(vchSig.begin(), vchSig.begin() + vchSig.size() - 1); - return CPubKey::CheckLowS(vchSigCopy); + if (!CPubKey::CheckLowS(vchSigCopy)) { + return set_error(serror, SCRIPT_ERR_SIG_HIGH_S); + } + return true; } bool static IsDefinedHashtypeSignature(const valtype &vchSig) { -- cgit v1.2.3 From c6c2f0fd782ccf607027414012f45c8f48561a30 Mon Sep 17 00:00:00 2001 From: Alex Morcos Date: Mon, 7 Dec 2015 15:44:16 -0500 Subject: Implement SequenceLocks functions SequenceLocks functions are used to evaluate sequence lock times or heights per BIP 68. The majority of this code is copied from maaku in #6312 Further credit: btcdrak, sipa, NicolasDorier --- src/script/interpreter.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/script') diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp index 57e0edc4b..ac753a9d5 100644 --- a/src/script/interpreter.cpp +++ b/src/script/interpreter.cpp @@ -1147,7 +1147,7 @@ bool TransactionSignatureChecker::CheckLockTime(const CScriptNum& nLockTime) con // prevent this condition. Alternatively we could test all // inputs, but testing just this input minimizes the data // required to prove correct CHECKLOCKTIMEVERIFY execution. - if (txTo->vin[nIn].IsFinal()) + if (CTxIn::SEQUENCE_FINAL == txTo->vin[nIn].nSequence) return false; return true; -- cgit v1.2.3 From 53e53a33c939949665f60d5eeb82abbb21f97128 Mon Sep 17 00:00:00 2001 From: Mark Friedenbach Date: Fri, 25 Sep 2015 16:18:51 -0700 Subject: BIP112: Implement CHECKSEQUENCEVERIFY - Replace NOP3 with CHECKSEQUENCEVERIFY (BIP112) CHECKSEQUENCEVERIFY -> - Fails if txin.nSequence < nSequence, allowing funds of a txout to be locked for a number of blocks or a duration of time after its inclusion in a block. - Pull most of CheckLockTime() out into VerifyLockTime(), a local function that will be reused for CheckSequence() - Add bitwise AND operator to CScriptNum - Enable CHECKSEQUENCEVERIFY as a standard script verify flag - Transactions that fail CSV verification will be rejected from the mempool, making it easy to test the feature. However blocks containing "invalid" CSV-using transactions will still be accepted; this is *not* the soft-fork required to actually enable CSV for production use. --- src/script/interpreter.cpp | 89 ++++++++++++++++++++++++++++++++++++++++++---- src/script/interpreter.h | 11 ++++++ src/script/script.h | 12 +++++++ src/script/script_error.h | 2 +- 4 files changed, 106 insertions(+), 8 deletions(-) (limited to 'src/script') diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp index 901f901f0..4e87006f5 100644 --- a/src/script/interpreter.cpp +++ b/src/script/interpreter.cpp @@ -373,7 +373,44 @@ bool EvalScript(vector >& stack, const CScript& script, un break; } - case OP_NOP1: case OP_NOP3: case OP_NOP4: case OP_NOP5: + case OP_CHECKSEQUENCEVERIFY: + { + if (!(flags & SCRIPT_VERIFY_CHECKSEQUENCEVERIFY)) { + // not enabled; treat as a NOP3 + if (flags & SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_NOPS) { + return set_error(serror, SCRIPT_ERR_DISCOURAGE_UPGRADABLE_NOPS); + } + break; + } + + if (stack.size() < 1) + return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION); + + // nSequence, like nLockTime, is a 32-bit unsigned integer + // field. See the comment in CHECKLOCKTIMEVERIFY regarding + // 5-byte numeric operands. + const CScriptNum nSequence(stacktop(-1), fRequireMinimal, 5); + + // In the rare event that the argument may be < 0 due to + // some arithmetic being done first, you can always use + // 0 MAX CHECKSEQUENCEVERIFY. + if (nSequence < 0) + return set_error(serror, SCRIPT_ERR_NEGATIVE_LOCKTIME); + + // To provide for future soft-fork extensibility, if the + // operand has the disabled lock-time flag set, + // CHECKSEQUENCEVERIFY behaves as a NOP. + if ((nSequence & CTxIn::SEQUENCE_LOCKTIME_DISABLE_FLAG) != 0) + break; + + // Compare the specified sequence number with the input. + if (!checker.CheckSequence(nSequence)) + return set_error(serror, SCRIPT_ERR_UNSATISFIED_LOCKTIME); + + break; + } + + case OP_NOP1: case OP_NOP4: case OP_NOP5: case OP_NOP6: case OP_NOP7: case OP_NOP8: case OP_NOP9: case OP_NOP10: { if (flags & SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_NOPS) @@ -1120,27 +1157,33 @@ bool TransactionSignatureChecker::CheckSig(const vector& vchSigIn return true; } -bool TransactionSignatureChecker::CheckLockTime(const CScriptNum& nLockTime) const +static bool VerifyLockTime(int64_t txToLockTime, int64_t nThreshold, const CScriptNum& nLockTime) { // There are two kinds of nLockTime: lock-by-blockheight // and lock-by-blocktime, distinguished by whether - // nLockTime < LOCKTIME_THRESHOLD. + // nLockTime < nThreshold (either LOCKTIME_THRESHOLD or + // CTxIn::SEQUENCE_LOCKTIME_TYPE_FLAG). // // We want to compare apples to apples, so fail the script // unless the type of nLockTime being tested is the same as // the nLockTime in the transaction. if (!( - (txTo->nLockTime < LOCKTIME_THRESHOLD && nLockTime < LOCKTIME_THRESHOLD) || - (txTo->nLockTime >= LOCKTIME_THRESHOLD && nLockTime >= LOCKTIME_THRESHOLD) + (txToLockTime < nThreshold && nLockTime < nThreshold) || + (txToLockTime >= nThreshold && nLockTime >= nThreshold) )) return false; // Now that we know we're comparing apples-to-apples, the // comparison is a simple numeric one. - if (nLockTime > (int64_t)txTo->nLockTime) + if (nLockTime > txToLockTime) return false; - // Finally the nLockTime feature can be disabled and thus + return true; +} + +bool TransactionSignatureChecker::CheckLockTime(const CScriptNum& nLockTime) const +{ + // The nLockTime feature can be disabled and thus // CHECKLOCKTIMEVERIFY bypassed if every txin has been // finalized by setting nSequence to maxint. The // transaction would be allowed into the blockchain, making @@ -1153,6 +1196,38 @@ bool TransactionSignatureChecker::CheckLockTime(const CScriptNum& nLockTime) con if (CTxIn::SEQUENCE_FINAL == txTo->vin[nIn].nSequence) return false; + if (!::VerifyLockTime((int64_t)txTo->nLockTime, LOCKTIME_THRESHOLD, nLockTime)) + return false; + + return true; +} + +bool TransactionSignatureChecker::CheckSequence(const CScriptNum& nSequence) const +{ + // Relative lock times are supported by comparing the passed + // in operand to the sequence number of the input. + const int64_t txToSequence = (int64_t)txTo->vin[nIn].nSequence; + + // Fail if the transaction's version number is not set high + // enough to trigger BIP 68 rules. + if (static_cast(txTo->nVersion) < 2) + return false; + + // Sequence numbers with their most significant bit set are not + // consensus constrained. Testing that the transaction's sequence + // number do not have this bit set prevents using this property + // to get around a CHECKSEQUENCEVERIFY check. + if (txToSequence & CTxIn::SEQUENCE_LOCKTIME_DISABLE_FLAG) + return false; + + // Mask off any bits that do not have consensus-enforced meaning + // before doing the integer comparisons of ::VerifyLockTime. + const uint32_t nLockTimeMask = CTxIn::SEQUENCE_LOCKTIME_TYPE_FLAG + | CTxIn::SEQUENCE_LOCKTIME_MASK; + + if (!::VerifyLockTime(txToSequence & nLockTimeMask, CTxIn::SEQUENCE_LOCKTIME_TYPE_FLAG, nSequence & nLockTimeMask)) + return false; + return true; } diff --git a/src/script/interpreter.h b/src/script/interpreter.h index 7b34547ff..e5cb7290f 100644 --- a/src/script/interpreter.h +++ b/src/script/interpreter.h @@ -81,6 +81,11 @@ enum // // See BIP65 for details. SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY = (1U << 9), + + // support CHECKSEQUENCEVERIFY opcode + // + // See BIP112 for details + SCRIPT_VERIFY_CHECKSEQUENCEVERIFY = (1U << 10), }; bool CheckSignatureEncoding(const std::vector &vchSig, unsigned int flags, ScriptError* serror); @@ -100,6 +105,11 @@ public: return false; } + virtual bool CheckSequence(const CScriptNum& nSequence) const + { + return false; + } + virtual ~BaseSignatureChecker() {} }; @@ -116,6 +126,7 @@ public: TransactionSignatureChecker(const CTransaction* txToIn, unsigned int nInIn) : txTo(txToIn), nIn(nInIn) {} bool CheckSig(const std::vector& scriptSig, const std::vector& vchPubKey, const CScript& scriptCode) const; bool CheckLockTime(const CScriptNum& nLockTime) const; + bool CheckSequence(const CScriptNum& nSequence) const; }; class MutableTransactionSignatureChecker : public TransactionSignatureChecker diff --git a/src/script/script.h b/src/script/script.h index 6551eea30..d2a68a07b 100644 --- a/src/script/script.h +++ b/src/script/script.h @@ -165,6 +165,7 @@ enum opcodetype OP_CHECKLOCKTIMEVERIFY = 0xb1, OP_NOP2 = OP_CHECKLOCKTIMEVERIFY, OP_NOP3 = 0xb2, + OP_CHECKSEQUENCEVERIFY = OP_NOP3, OP_NOP4 = 0xb3, OP_NOP5 = 0xb4, OP_NOP6 = 0xb5, @@ -259,6 +260,11 @@ public: inline CScriptNum& operator+=( const CScriptNum& rhs) { return operator+=(rhs.m_value); } inline CScriptNum& operator-=( const CScriptNum& rhs) { return operator-=(rhs.m_value); } + inline CScriptNum operator&( const int64_t& rhs) const { return CScriptNum(m_value & rhs);} + inline CScriptNum operator&( const CScriptNum& rhs) const { return operator&(rhs.m_value); } + + inline CScriptNum& operator&=( const CScriptNum& rhs) { return operator&=(rhs.m_value); } + inline CScriptNum operator-() const { assert(m_value != std::numeric_limits::min()); @@ -287,6 +293,12 @@ public: return *this; } + inline CScriptNum& operator&=( const int64_t& rhs) + { + m_value &= rhs; + return *this; + } + int getint() const { if (m_value > std::numeric_limits::max()) diff --git a/src/script/script_error.h b/src/script/script_error.h index bb10b8a29..26df33932 100644 --- a/src/script/script_error.h +++ b/src/script/script_error.h @@ -35,7 +35,7 @@ typedef enum ScriptError_t SCRIPT_ERR_INVALID_ALTSTACK_OPERATION, SCRIPT_ERR_UNBALANCED_CONDITIONAL, - /* OP_CHECKLOCKTIMEVERIFY */ + /* CHECKLOCKTIMEVERIFY and CHECKSEQUENCEVERIFY */ SCRIPT_ERR_NEGATIVE_LOCKTIME, SCRIPT_ERR_UNSATISFIED_LOCKTIME, -- cgit v1.2.3 From c3c375226ebf98901849593b8ebfe8e8b69895c2 Mon Sep 17 00:00:00 2001 From: BtcDrak Date: Fri, 12 Feb 2016 20:02:46 +0000 Subject: Separate CheckLockTime() and CheckSequence() logic For the sake of a little repetition, make code more readable. --- src/script/interpreter.cpp | 46 ++++++++++++++++++++++++++-------------------- 1 file changed, 26 insertions(+), 20 deletions(-) (limited to 'src/script') diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp index 4e87006f5..d4fe001d7 100644 --- a/src/script/interpreter.cpp +++ b/src/script/interpreter.cpp @@ -1157,33 +1157,27 @@ bool TransactionSignatureChecker::CheckSig(const vector& vchSigIn return true; } -static bool VerifyLockTime(int64_t txToLockTime, int64_t nThreshold, const CScriptNum& nLockTime) +bool TransactionSignatureChecker::CheckLockTime(const CScriptNum& nLockTime) const { // There are two kinds of nLockTime: lock-by-blockheight // and lock-by-blocktime, distinguished by whether - // nLockTime < nThreshold (either LOCKTIME_THRESHOLD or - // CTxIn::SEQUENCE_LOCKTIME_TYPE_FLAG). + // nLockTime < LOCKTIME_THRESHOLD. // // We want to compare apples to apples, so fail the script // unless the type of nLockTime being tested is the same as // the nLockTime in the transaction. if (!( - (txToLockTime < nThreshold && nLockTime < nThreshold) || - (txToLockTime >= nThreshold && nLockTime >= nThreshold) + (txTo->nLockTime < LOCKTIME_THRESHOLD && nLockTime < LOCKTIME_THRESHOLD) || + (txTo->nLockTime >= LOCKTIME_THRESHOLD && nLockTime >= LOCKTIME_THRESHOLD) )) return false; // Now that we know we're comparing apples-to-apples, the // comparison is a simple numeric one. - if (nLockTime > txToLockTime) + if (nLockTime > (int64_t)txTo->nLockTime) return false; - return true; -} - -bool TransactionSignatureChecker::CheckLockTime(const CScriptNum& nLockTime) const -{ - // The nLockTime feature can be disabled and thus + // Finally the nLockTime feature can be disabled and thus // CHECKLOCKTIMEVERIFY bypassed if every txin has been // finalized by setting nSequence to maxint. The // transaction would be allowed into the blockchain, making @@ -1196,9 +1190,6 @@ bool TransactionSignatureChecker::CheckLockTime(const CScriptNum& nLockTime) con if (CTxIn::SEQUENCE_FINAL == txTo->vin[nIn].nSequence) return false; - if (!::VerifyLockTime((int64_t)txTo->nLockTime, LOCKTIME_THRESHOLD, nLockTime)) - return false; - return true; } @@ -1221,17 +1212,32 @@ bool TransactionSignatureChecker::CheckSequence(const CScriptNum& nSequence) con return false; // Mask off any bits that do not have consensus-enforced meaning - // before doing the integer comparisons of ::VerifyLockTime. - const uint32_t nLockTimeMask = CTxIn::SEQUENCE_LOCKTIME_TYPE_FLAG - | CTxIn::SEQUENCE_LOCKTIME_MASK; + // before doing the integer comparisons + const uint32_t nLockTimeMask = CTxIn::SEQUENCE_LOCKTIME_TYPE_FLAG | CTxIn::SEQUENCE_LOCKTIME_MASK; + const int64_t txToSequenceMasked = txToSequence & nLockTimeMask; + const CScriptNum nSequenceMasked = nSequence & nLockTimeMask; + + // There are two kinds of nSequence: lock-by-blockheight + // and lock-by-blocktime, distinguished by whether + // nSequenceMasked < CTxIn::SEQUENCE_LOCKTIME_TYPE_FLAG. + // + // We want to compare apples to apples, so fail the script + // unless the type of nSequenceMasked being tested is the same as + // the nSequenceMasked in the transaction. + if (!( + (txToSequenceMasked < CTxIn::SEQUENCE_LOCKTIME_TYPE_FLAG && nSequenceMasked < CTxIn::SEQUENCE_LOCKTIME_TYPE_FLAG) || + (txToSequenceMasked >= CTxIn::SEQUENCE_LOCKTIME_TYPE_FLAG && nSequenceMasked >= CTxIn::SEQUENCE_LOCKTIME_TYPE_FLAG) + )) + return false; - if (!::VerifyLockTime(txToSequence & nLockTimeMask, CTxIn::SEQUENCE_LOCKTIME_TYPE_FLAG, nSequence & nLockTimeMask)) + // Now that we know we're comparing apples-to-apples, the + // comparison is a simple numeric one. + if (nSequenceMasked > txToSequenceMasked) return false; return true; } - bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, unsigned int flags, const BaseSignatureChecker& checker, ScriptError* serror) { set_error(serror, SCRIPT_ERR_UNKNOWN_ERROR); -- cgit v1.2.3 From a38107643f3f01cd92a4e6da9e7a9d025770ff37 Mon Sep 17 00:00:00 2001 From: BtcDrak Date: Tue, 16 Feb 2016 09:39:44 +0000 Subject: Code style fix. This if statement is a little obtuse and using braces here improves readability. --- src/script/interpreter.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src/script') diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp index d4fe001d7..149a4f015 100644 --- a/src/script/interpreter.cpp +++ b/src/script/interpreter.cpp @@ -1227,8 +1227,9 @@ bool TransactionSignatureChecker::CheckSequence(const CScriptNum& nSequence) con if (!( (txToSequenceMasked < CTxIn::SEQUENCE_LOCKTIME_TYPE_FLAG && nSequenceMasked < CTxIn::SEQUENCE_LOCKTIME_TYPE_FLAG) || (txToSequenceMasked >= CTxIn::SEQUENCE_LOCKTIME_TYPE_FLAG && nSequenceMasked >= CTxIn::SEQUENCE_LOCKTIME_TYPE_FLAG) - )) + )) { return false; + } // Now that we know we're comparing apples-to-apples, the // comparison is a simple numeric one. -- cgit v1.2.3 From fada0c422c081ba53a324aaf63c0a750cb56498e Mon Sep 17 00:00:00 2001 From: MarcoFalke Date: Sun, 3 Apr 2016 11:49:36 +0200 Subject: [doc] Fix doxygen comments for members --- src/script/interpreter.cpp | 12 ++++++------ src/script/standard.h | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) (limited to 'src/script') diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp index 149a4f015..9c47f7c6c 100644 --- a/src/script/interpreter.cpp +++ b/src/script/interpreter.cpp @@ -1015,12 +1015,12 @@ namespace { */ class CTransactionSignatureSerializer { private: - const CTransaction &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 - const bool fHashSingle; //! whether the hashtype is SIGHASH_SINGLE - const bool fHashNone; //! whether the hashtype is SIGHASH_NONE + const CTransaction& 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 + const bool fHashSingle; //!< whether the hashtype is SIGHASH_SINGLE + const bool fHashNone; //!< whether the hashtype is SIGHASH_NONE public: CTransactionSignatureSerializer(const CTransaction &txToIn, const CScript &scriptCodeIn, unsigned int nInIn, int nHashTypeIn) : diff --git a/src/script/standard.h b/src/script/standard.h index 64bf010ec..f348da8e1 100644 --- a/src/script/standard.h +++ b/src/script/standard.h @@ -27,7 +27,7 @@ public: CScriptID(const uint160& in) : uint160(in) {} }; -static const unsigned int MAX_OP_RETURN_RELAY = 83; //! bytes (+1 for OP_RETURN, +2 for the pushdata opcodes) +static const unsigned int MAX_OP_RETURN_RELAY = 83; //!< bytes (+1 for OP_RETURN, +2 for the pushdata opcodes) extern bool fAcceptDatacarrier; extern unsigned nMaxDatacarrierBytes; -- cgit v1.2.3 From a25a4f5b04c3e045557e9e7e807b2af74ad75128 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Mon, 18 Apr 2016 15:12:46 +0200 Subject: =?UTF-8?q?wallet=5Fismine.h=20=E2=86=92=20script/ismine.h?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Removes conditional dependency of `src/test` on wallet. Makes multisig and P2SH tests complete without wallet built-in. --- src/script/ismine.cpp | 95 +++++++++++++++++++++++++++++++++++++++++++++++++++ src/script/ismine.h | 34 ++++++++++++++++++ 2 files changed, 129 insertions(+) create mode 100644 src/script/ismine.cpp create mode 100644 src/script/ismine.h (limited to 'src/script') diff --git a/src/script/ismine.cpp b/src/script/ismine.cpp new file mode 100644 index 000000000..535c56b57 --- /dev/null +++ b/src/script/ismine.cpp @@ -0,0 +1,95 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2015 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 "ismine.h" + +#include "key.h" +#include "keystore.h" +#include "script/script.h" +#include "script/standard.h" +#include "script/sign.h" + +#include + +using namespace std; + +typedef vector valtype; + +unsigned int HaveKeys(const vector& pubkeys, const CKeyStore& keystore) +{ + unsigned int nResult = 0; + BOOST_FOREACH(const valtype& pubkey, pubkeys) + { + CKeyID keyID = CPubKey(pubkey).GetID(); + if (keystore.HaveKey(keyID)) + ++nResult; + } + return nResult; +} + +isminetype IsMine(const CKeyStore &keystore, const CTxDestination& dest) +{ + CScript script = GetScriptForDestination(dest); + return IsMine(keystore, script); +} + +isminetype IsMine(const CKeyStore &keystore, const CScript& scriptPubKey) +{ + vector vSolutions; + txnouttype whichType; + if (!Solver(scriptPubKey, whichType, vSolutions)) { + if (keystore.HaveWatchOnly(scriptPubKey)) + return ISMINE_WATCH_UNSOLVABLE; + return ISMINE_NO; + } + + CKeyID keyID; + switch (whichType) + { + case TX_NONSTANDARD: + case TX_NULL_DATA: + break; + case TX_PUBKEY: + keyID = CPubKey(vSolutions[0]).GetID(); + if (keystore.HaveKey(keyID)) + return ISMINE_SPENDABLE; + break; + case TX_PUBKEYHASH: + keyID = CKeyID(uint160(vSolutions[0])); + if (keystore.HaveKey(keyID)) + return ISMINE_SPENDABLE; + break; + case TX_SCRIPTHASH: + { + CScriptID scriptID = CScriptID(uint160(vSolutions[0])); + CScript subscript; + if (keystore.GetCScript(scriptID, subscript)) { + isminetype ret = IsMine(keystore, subscript); + if (ret == ISMINE_SPENDABLE) + return ret; + } + break; + } + case TX_MULTISIG: + { + // Only consider transactions "mine" if we own ALL the + // keys involved. Multi-signature transactions that are + // partially owned (somebody else has a key that can spend + // them) enable spend-out-from-under-you attacks, especially + // in shared-wallet situations. + vector keys(vSolutions.begin()+1, vSolutions.begin()+vSolutions.size()-1); + if (HaveKeys(keys, keystore) == keys.size()) + return ISMINE_SPENDABLE; + break; + } + } + + if (keystore.HaveWatchOnly(scriptPubKey)) { + // TODO: This could be optimized some by doing some work after the above solver + CScript scriptSig; + return ProduceSignature(DummySignatureCreator(&keystore), scriptPubKey, scriptSig) ? ISMINE_WATCH_SOLVABLE : ISMINE_WATCH_UNSOLVABLE; + } + return ISMINE_NO; +} diff --git a/src/script/ismine.h b/src/script/ismine.h new file mode 100644 index 000000000..4b7db8802 --- /dev/null +++ b/src/script/ismine.h @@ -0,0 +1,34 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2015 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef BITCOIN_SCRIPT_ISMINE_H +#define BITCOIN_SCRIPT_ISMINE_H + +#include "script/standard.h" + +#include + +class CKeyStore; +class CScript; + +/** IsMine() return codes */ +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_ALL = ISMINE_WATCH_ONLY | ISMINE_SPENDABLE +}; +/** used for bitflags of isminetype */ +typedef uint8_t isminefilter; + +isminetype IsMine(const CKeyStore& keystore, const CScript& scriptPubKey); +isminetype IsMine(const CKeyStore& keystore, const CTxDestination& dest); + +#endif // BITCOIN_SCRIPT_ISMINE_H -- cgit v1.2.3 From ec9ad5f199e4ec6eb05d8ea47016413413fe17e0 Mon Sep 17 00:00:00 2001 From: Patrick Strateman Date: Tue, 19 Apr 2016 13:07:16 -0700 Subject: Replace memcmp with std::equal in CScript::FindAndDelete Function is stl; std::equal just makes more sense. --- src/script/script.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/script') diff --git a/src/script/script.h b/src/script/script.h index d2a68a07b..ef3af21d6 100644 --- a/src/script/script.h +++ b/src/script/script.h @@ -574,7 +574,7 @@ public: opcodetype opcode; do { - while (end() - pc >= (long)b.size() && memcmp(&pc[0], &b[0], b.size()) == 0) + while (end() - pc >= (long)b.size() && std::equal(b.begin(), b.end(), pc)) { pc = erase(pc, pc + b.size()); ++nFound; -- cgit v1.2.3 From c0f660c3a39e3b6d75d9a6bf8a9824c347c321b8 Mon Sep 17 00:00:00 2001 From: Patrick Strateman Date: Tue, 19 Apr 2016 13:13:46 -0700 Subject: Replace c-style cast with c++ style static_cast. --- src/script/script.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/script') diff --git a/src/script/script.h b/src/script/script.h index ef3af21d6..0503b39a7 100644 --- a/src/script/script.h +++ b/src/script/script.h @@ -574,7 +574,7 @@ public: opcodetype opcode; do { - while (end() - pc >= (long)b.size() && std::equal(b.begin(), b.end(), pc)) + while (static_cast(end() - pc) >= b.size() && std::equal(b.begin(), b.end(), pc)) { pc = erase(pc, pc + b.size()); ++nFound; -- cgit v1.2.3 From d1d7775587473410a107e7079616b9ecaae8dd06 Mon Sep 17 00:00:00 2001 From: Patrick Strateman Date: Tue, 19 Apr 2016 13:17:38 -0700 Subject: Improve worst-case behavior of CScript::FindAndDelete Thanks to Sergio Lerner for identifying this issue and suggesting this kind of solution. --- src/script/script.h | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) (limited to 'src/script') diff --git a/src/script/script.h b/src/script/script.h index 0503b39a7..bdbd340bc 100644 --- a/src/script/script.h +++ b/src/script/script.h @@ -570,17 +570,26 @@ public: int nFound = 0; if (b.empty()) return nFound; - iterator pc = begin(); + CScript result; + iterator pc = begin(), pc2 = begin(); opcodetype opcode; do { + result.insert(result.end(), pc2, pc); while (static_cast(end() - pc) >= b.size() && std::equal(b.begin(), b.end(), pc)) { - pc = erase(pc, pc + b.size()); + pc = pc + b.size(); ++nFound; } + pc2 = pc; } while (GetOp(pc, opcode)); + + if (nFound > 0) { + result.insert(result.end(), pc2, end()); + *this = result; + } + return nFound; } int Find(opcodetype op) const -- cgit v1.2.3 From f8e6fb1800fbac87e76cdddc074d8f4af585f050 Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Mon, 25 Apr 2016 12:31:45 +0200 Subject: Introduce constant for maximum CScript length --- src/script/interpreter.cpp | 2 +- src/script/script.h | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) (limited to 'src/script') diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp index 9c47f7c6c..fd4a5674c 100644 --- a/src/script/interpreter.cpp +++ b/src/script/interpreter.cpp @@ -247,7 +247,7 @@ bool EvalScript(vector >& stack, const CScript& script, un vector vfExec; vector altstack; set_error(serror, SCRIPT_ERR_UNKNOWN_ERROR); - if (script.size() > 10000) + if (script.size() > MAX_SCRIPT_SIZE) return set_error(serror, SCRIPT_ERR_SCRIPT_SIZE); int nOpCount = 0; bool fRequireMinimal = (flags & SCRIPT_VERIFY_MINIMALDATA) != 0; diff --git a/src/script/script.h b/src/script/script.h index d2a68a07b..68cde03e3 100644 --- a/src/script/script.h +++ b/src/script/script.h @@ -27,6 +27,9 @@ static const int MAX_OPS_PER_SCRIPT = 201; // Maximum number of public keys per multisig static const int MAX_PUBKEYS_PER_MULTISIG = 20; +// Maximum script length in bytes +static const int MAX_SCRIPT_SIZE = 10000; + // Threshold for nLockTime: below this value it is interpreted as block number, // otherwise as UNIX timestamp. static const unsigned int LOCKTIME_THRESHOLD = 500000000; // Tue Nov 5 00:53:20 1985 UTC -- cgit v1.2.3 From 4f87af6fc7580912726f9bf833c21e6e1b478e1d Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Mon, 25 Apr 2016 12:32:01 +0200 Subject: Treat overly long scriptPubKeys as unspendable --- src/script/script.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/script') diff --git a/src/script/script.h b/src/script/script.h index 68cde03e3..2a338d6f5 100644 --- a/src/script/script.h +++ b/src/script/script.h @@ -624,7 +624,7 @@ public: */ bool IsUnspendable() const { - return (size() > 0 && *begin() == OP_RETURN); + return (size() > 0 && *begin() == OP_RETURN) || (size() > MAX_SCRIPT_SIZE); } void clear() -- cgit v1.2.3 From 7030d9eb47254499bba14f1c00abc6bf493efd91 Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Fri, 6 Nov 2015 01:32:04 +0100 Subject: BIP144: Serialization, hashes, relay (sender side) Contains refactorings by Eric Lombrozo. Contains fixup by Nicolas Dorier. Contains cleanup of CInv::GetCommand by Alex Morcos --- src/script/script.cpp | 12 ++++++++++++ src/script/script.h | 14 ++++++++++++++ 2 files changed, 26 insertions(+) (limited to 'src/script') diff --git a/src/script/script.cpp b/src/script/script.cpp index 9f2809e59..6b1eb52bb 100644 --- a/src/script/script.cpp +++ b/src/script/script.cpp @@ -231,3 +231,15 @@ bool CScript::IsPushOnly() const { return this->IsPushOnly(begin()); } + +std::string CScriptWitness::ToString() const +{ + std::string ret = "CScriptWitness("; + for (unsigned int i = 0; i < stack.size(); i++) { + if (i) { + ret += ", "; + } + ret += HexStr(stack[i]); + } + return ret + ")"; +} diff --git a/src/script/script.h b/src/script/script.h index a2941ce90..0b6d822d8 100644 --- a/src/script/script.h +++ b/src/script/script.h @@ -643,6 +643,20 @@ public: } }; +struct CScriptWitness +{ + // Note that this encodes the data elements being pushed, rather than + // encoding them as a CScript that pushes them. + std::vector > stack; + + // Some compilers complain without a default constructor + CScriptWitness() { } + + bool IsNull() const { return stack.empty(); } + + std::string ToString() const; +}; + class CReserveScript { public: -- cgit v1.2.3 From 449f9b8debcceb61a92043bc7031528a53627c47 Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Sun, 8 Nov 2015 01:16:45 +0100 Subject: BIP141: Witness program --- src/script/bitcoinconsensus.cpp | 2 +- src/script/interpreter.cpp | 111 +++++++++++++++++++++++++++++++++++++++- src/script/interpreter.h | 12 ++++- src/script/script.cpp | 18 +++++++ src/script/script.h | 1 + src/script/script_error.cpp | 14 +++++ src/script/script_error.h | 9 ++++ src/script/sign.cpp | 2 +- 8 files changed, 163 insertions(+), 6 deletions(-) (limited to 'src/script') diff --git a/src/script/bitcoinconsensus.cpp b/src/script/bitcoinconsensus.cpp index 47ad1d080..5c9e7c0a5 100644 --- a/src/script/bitcoinconsensus.cpp +++ b/src/script/bitcoinconsensus.cpp @@ -85,7 +85,7 @@ int bitcoinconsensus_verify_script(const unsigned char *scriptPubKey, unsigned i // Regardless of the verification result, the tx did not error. set_error(err, bitcoinconsensus_ERR_OK); - return VerifyScript(tx.vin[nIn].scriptSig, CScript(scriptPubKey, scriptPubKey + scriptPubKeyLen), flags, TransactionSignatureChecker(&tx, nIn), NULL); + return VerifyScript(tx.vin[nIn].scriptSig, CScript(scriptPubKey, scriptPubKey + scriptPubKeyLen), nIn < tx.wit.vtxinwit.size() ? &tx.wit.vtxinwit[nIn].scriptWitness : NULL, flags, TransactionSignatureChecker(&tx, nIn), NULL); } catch (const std::exception&) { return set_error(err, bitcoinconsensus_ERR_TX_DESERIALIZE); // Error deserializing } diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp index fd4a5674c..be649fca2 100644 --- a/src/script/interpreter.cpp +++ b/src/script/interpreter.cpp @@ -1239,8 +1239,67 @@ bool TransactionSignatureChecker::CheckSequence(const CScriptNum& nSequence) con return true; } -bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, unsigned int flags, const BaseSignatureChecker& checker, ScriptError* serror) +static bool VerifyWitnessProgram(const CScriptWitness& witness, int witversion, const std::vector& program, unsigned int flags, const BaseSignatureChecker& checker, ScriptError* serror) { + vector > stack; + CScript scriptPubKey; + + if (witversion == 0) { + if (program.size() == 32) { + // Version 0 segregated witness program: SHA256(CScript) inside the program, CScript + inputs in witness + if (witness.stack.size() == 0) { + return set_error(serror, SCRIPT_ERR_WITNESS_PROGRAM_WITNESS_EMPTY); + } + scriptPubKey = CScript(witness.stack.back().begin(), witness.stack.back().end()); + stack = std::vector >(witness.stack.begin(), witness.stack.end() - 1); + uint256 hashScriptPubKey; + CSHA256().Write(&scriptPubKey[0], scriptPubKey.size()).Finalize(hashScriptPubKey.begin()); + if (memcmp(hashScriptPubKey.begin(), &program[0], 32)) { + return set_error(serror, SCRIPT_ERR_WITNESS_PROGRAM_MISMATCH); + } + } else if (program.size() == 20) { + // Special case for pay-to-pubkeyhash; signature + pubkey in witness + if (witness.stack.size() != 2) { + return set_error(serror, SCRIPT_ERR_WITNESS_PROGRAM_MISMATCH); // 2 items in witness + } + scriptPubKey << OP_DUP << OP_HASH160 << program << OP_EQUALVERIFY << OP_CHECKSIG; + stack = witness.stack; + } else { + return set_error(serror, SCRIPT_ERR_WITNESS_PROGRAM_WRONG_LENGTH); + } + } else if (flags & SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM) { + return set_error(serror, SCRIPT_ERR_DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM); + } else { + // Higher version witness scripts return true for future softfork compatibility + return set_success(serror); + } + + // Disallow stack item size > MAX_SCRIPT_ELEMENT_SIZE in witness stack + for (unsigned int i = 0; i < stack.size(); i++) { + if (stack.at(i).size() > MAX_SCRIPT_ELEMENT_SIZE) + return set_error(serror, SCRIPT_ERR_PUSH_SIZE); + } + + if (!EvalScript(stack, scriptPubKey, flags, checker, serror)) { + return false; + } + + // Scripts inside witness implicitly require cleanstack behaviour + if (stack.size() != 1) + return set_error(serror, SCRIPT_ERR_EVAL_FALSE); + if (!CastToBool(stack.back())) + return set_error(serror, SCRIPT_ERR_EVAL_FALSE); + return true; +} + +bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const CScriptWitness* witness, unsigned int flags, const BaseSignatureChecker& checker, ScriptError* serror) +{ + static const CScriptWitness emptyWitness; + if (witness == NULL) { + witness = &emptyWitness; + } + bool hadWitness = false; + set_error(serror, SCRIPT_ERR_UNKNOWN_ERROR); if ((flags & SCRIPT_VERIFY_SIGPUSHONLY) != 0 && !scriptSig.IsPushOnly()) { @@ -1261,6 +1320,25 @@ bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, unsigne if (CastToBool(stack.back()) == false) return set_error(serror, SCRIPT_ERR_EVAL_FALSE); + // Bare witness programs + int witnessversion; + std::vector witnessprogram; + if (flags & SCRIPT_VERIFY_WITNESS) { + if (scriptPubKey.IsWitnessProgram(witnessversion, witnessprogram)) { + hadWitness = true; + if (scriptSig.size() != 0) { + // The scriptSig must be _exactly_ CScript(), otherwise we reintroduce malleability. + return set_error(serror, SCRIPT_ERR_WITNESS_MALLEATED); + } + if (!VerifyWitnessProgram(*witness, witnessversion, witnessprogram, flags, checker, serror)) { + return false; + } + // Bypass the cleanstack check at the end. The actual stack is obviously not clean + // for witness programs. + stack.resize(1); + } + } + // Additional validation for spend-to-script-hash transactions: if ((flags & SCRIPT_VERIFY_P2SH) && scriptPubKey.IsPayToScriptHash()) { @@ -1287,19 +1365,48 @@ bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, unsigne return set_error(serror, SCRIPT_ERR_EVAL_FALSE); if (!CastToBool(stack.back())) return set_error(serror, SCRIPT_ERR_EVAL_FALSE); + + // P2SH witness program + if (flags & SCRIPT_VERIFY_WITNESS) { + if (pubKey2.IsWitnessProgram(witnessversion, witnessprogram)) { + hadWitness = true; + if (scriptSig != CScript() << std::vector(pubKey2.begin(), pubKey2.end())) { + // The scriptSig must be _exactly_ a single push of the redeemScript. Otherwise we + // reintroduce malleability. + return set_error(serror, SCRIPT_ERR_WITNESS_MALLEATED_P2SH); + } + if (!VerifyWitnessProgram(*witness, witnessversion, witnessprogram, flags, checker, serror)) { + return false; + } + // Bypass the cleanstack check at the end. The actual stack is obviously not clean + // for witness programs. + stack.resize(1); + } + } } // The CLEANSTACK check is only performed after potential P2SH evaluation, // as the non-P2SH evaluation of a P2SH script will obviously not result in - // a clean stack (the P2SH inputs remain). + // a clean stack (the P2SH inputs remain). The same holds for witness evaluation. if ((flags & SCRIPT_VERIFY_CLEANSTACK) != 0) { // Disallow CLEANSTACK without P2SH, as otherwise a switch CLEANSTACK->P2SH+CLEANSTACK // would be possible, which is not a softfork (and P2SH should be one). assert((flags & SCRIPT_VERIFY_P2SH) != 0); + assert((flags & SCRIPT_VERIFY_WITNESS) != 0); if (stack.size() != 1) { return set_error(serror, SCRIPT_ERR_CLEANSTACK); } } + if (flags & SCRIPT_VERIFY_WITNESS) { + // We can't check for correct unexpected witness data if P2SH was off, so require + // that WITNESS implies P2SH. Otherwise, going from WITNESS->P2SH+WITNESS would be + // possible, which is not a softfork. + assert((flags & SCRIPT_VERIFY_P2SH) != 0); + if (!hadWitness && !witness->IsNull()) { + return set_error(serror, SCRIPT_ERR_WITNESS_UNEXPECTED); + } + } + return set_success(serror); } diff --git a/src/script/interpreter.h b/src/script/interpreter.h index e5cb7290f..d1459ed62 100644 --- a/src/script/interpreter.h +++ b/src/script/interpreter.h @@ -74,7 +74,7 @@ enum // "At least one stack element must remain, and when interpreted as a boolean, it must be true" to // "Exactly one stack element must remain, and when interpreted as a boolean, it must be true". // (softfork safe, BIP62 rule 6) - // Note: CLEANSTACK should never be used without P2SH. + // Note: CLEANSTACK should never be used without P2SH or WITNESS. SCRIPT_VERIFY_CLEANSTACK = (1U << 8), // Verify CHECKLOCKTIMEVERIFY @@ -86,6 +86,14 @@ enum // // See BIP112 for details SCRIPT_VERIFY_CHECKSEQUENCEVERIFY = (1U << 10), + + // Support segregated witness + // + SCRIPT_VERIFY_WITNESS = (1U << 11), + + // Making v1-v16 witness program non-standard + // + SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM = (1U << 12), }; bool CheckSignatureEncoding(const std::vector &vchSig, unsigned int flags, ScriptError* serror); @@ -139,6 +147,6 @@ public: }; bool EvalScript(std::vector >& stack, const CScript& script, unsigned int flags, const BaseSignatureChecker& checker, ScriptError* error = NULL); -bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, unsigned int flags, const BaseSignatureChecker& checker, ScriptError* error = NULL); +bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const CScriptWitness* witness, unsigned int flags, const BaseSignatureChecker& checker, ScriptError* serror = NULL); #endif // BITCOIN_SCRIPT_INTERPRETER_H diff --git a/src/script/script.cpp b/src/script/script.cpp index 6b1eb52bb..73f5a61bf 100644 --- a/src/script/script.cpp +++ b/src/script/script.cpp @@ -210,6 +210,24 @@ bool CScript::IsPayToScriptHash() const (*this)[22] == OP_EQUAL); } +// A witness program is any valid CScript that consists of a 1-byte push opcode +// followed by a data push between 2 and 40 bytes. +bool CScript::IsWitnessProgram(int& version, std::vector& program) const +{ + if (this->size() < 4 || this->size() > 42) { + return false; + } + if ((*this)[0] != OP_0 && ((*this)[0] < OP_1 || (*this)[0] > OP_16)) { + return false; + } + if ((size_t)((*this)[1] + 2) == this->size()) { + version = DecodeOP_N((opcodetype)(*this)[0]); + program = std::vector(this->begin() + 2, this->end()); + return true; + } + return false; +} + bool CScript::IsPushOnly(const_iterator pc) const { while (pc < end()) diff --git a/src/script/script.h b/src/script/script.h index 0b6d822d8..b9b5be901 100644 --- a/src/script/script.h +++ b/src/script/script.h @@ -621,6 +621,7 @@ public: unsigned int GetSigOpCount(const CScript& scriptSig) const; bool IsPayToScriptHash() const; + bool IsWitnessProgram(int& version, std::vector& program) const; /** Called by IsStandardTx and P2SH/BIP62 VerifyScript (which makes it consensus-critical). */ bool IsPushOnly(const_iterator pc) const; diff --git a/src/script/script_error.cpp b/src/script/script_error.cpp index f1aa1fb40..cef807edc 100644 --- a/src/script/script_error.cpp +++ b/src/script/script_error.cpp @@ -65,8 +65,22 @@ const char* ScriptErrorString(const ScriptError serror) return "Dummy CHECKMULTISIG argument must be zero"; case SCRIPT_ERR_DISCOURAGE_UPGRADABLE_NOPS: return "NOPx reserved for soft-fork upgrades"; + case SCRIPT_ERR_DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM: + return "Witness version reserved for soft-fork upgrades"; case SCRIPT_ERR_PUBKEYTYPE: return "Public key is neither compressed or uncompressed"; + case SCRIPT_ERR_WITNESS_PROGRAM_WRONG_LENGTH: + return "Witness program has incorrect length"; + case SCRIPT_ERR_WITNESS_PROGRAM_WITNESS_EMPTY: + return "Witness program was passed an empty witness"; + case SCRIPT_ERR_WITNESS_PROGRAM_MISMATCH: + return "Witness program hash mismatch"; + case SCRIPT_ERR_WITNESS_MALLEATED: + return "Witness requires empty scriptSig"; + case SCRIPT_ERR_WITNESS_MALLEATED_P2SH: + return "Witness requires only-redeemscript scriptSig"; + case SCRIPT_ERR_WITNESS_UNEXPECTED: + return "Witness provided for non-witness script"; 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 26df33932..09dc6945a 100644 --- a/src/script/script_error.h +++ b/src/script/script_error.h @@ -51,6 +51,15 @@ typedef enum ScriptError_t /* softfork safeness */ SCRIPT_ERR_DISCOURAGE_UPGRADABLE_NOPS, + SCRIPT_ERR_DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM, + + /* segregated witness */ + SCRIPT_ERR_WITNESS_PROGRAM_WRONG_LENGTH, + SCRIPT_ERR_WITNESS_PROGRAM_WITNESS_EMPTY, + SCRIPT_ERR_WITNESS_PROGRAM_MISMATCH, + SCRIPT_ERR_WITNESS_MALLEATED, + SCRIPT_ERR_WITNESS_MALLEATED_P2SH, + SCRIPT_ERR_WITNESS_UNEXPECTED, SCRIPT_ERR_ERROR_COUNT } ScriptError; diff --git a/src/script/sign.cpp b/src/script/sign.cpp index 2f4111f78..37b702de1 100644 --- a/src/script/sign.cpp +++ b/src/script/sign.cpp @@ -123,7 +123,7 @@ bool ProduceSignature(const BaseSignatureCreator& creator, const CScript& fromPu } // Test solution - return VerifyScript(scriptSig, fromPubKey, STANDARD_SCRIPT_VERIFY_FLAGS, creator.Checker()); + return VerifyScript(scriptSig, fromPubKey, NULL, STANDARD_SCRIPT_VERIFY_FLAGS, creator.Checker()); } bool SignSignature(const CKeyStore &keystore, const CScript& fromPubKey, CMutableTransaction& txTo, unsigned int nIn, int nHashType) -- cgit v1.2.3 From 0ef1dd3e11dd573b6e443852ef0c72e34093ac68 Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Thu, 31 Mar 2016 14:51:29 +0200 Subject: Refactor script validation to observe amounts This is a preparation for BIP143 support. --- src/script/bitcoinconsensus.cpp | 3 ++- src/script/bitcoinconsensus.h | 2 ++ src/script/interpreter.h | 4 ++-- src/script/sigcache.h | 2 +- src/script/sign.cpp | 5 +++-- 5 files changed, 10 insertions(+), 6 deletions(-) (limited to 'src/script') diff --git a/src/script/bitcoinconsensus.cpp b/src/script/bitcoinconsensus.cpp index 5c9e7c0a5..26e7a85b7 100644 --- a/src/script/bitcoinconsensus.cpp +++ b/src/script/bitcoinconsensus.cpp @@ -85,7 +85,8 @@ int bitcoinconsensus_verify_script(const unsigned char *scriptPubKey, unsigned i // Regardless of the verification result, the tx did not error. set_error(err, bitcoinconsensus_ERR_OK); - return VerifyScript(tx.vin[nIn].scriptSig, CScript(scriptPubKey, scriptPubKey + scriptPubKeyLen), nIn < tx.wit.vtxinwit.size() ? &tx.wit.vtxinwit[nIn].scriptWitness : NULL, flags, TransactionSignatureChecker(&tx, nIn), NULL); + CAmount am(0); + return VerifyScript(tx.vin[nIn].scriptSig, CScript(scriptPubKey, scriptPubKey + scriptPubKeyLen), nIn < tx.wit.vtxinwit.size() ? &tx.wit.vtxinwit[nIn].scriptWitness : NULL, flags, TransactionSignatureChecker(&tx, nIn, am), NULL); } catch (const std::exception&) { return set_error(err, bitcoinconsensus_ERR_TX_DESERIALIZE); // Error deserializing } diff --git a/src/script/bitcoinconsensus.h b/src/script/bitcoinconsensus.h index 5b8c33c6b..cd3e2f773 100644 --- a/src/script/bitcoinconsensus.h +++ b/src/script/bitcoinconsensus.h @@ -6,6 +6,8 @@ #ifndef BITCOIN_BITCOINCONSENSUS_H #define BITCOIN_BITCOINCONSENSUS_H +#include + #if defined(BUILD_BITCOIN_INTERNAL) && defined(HAVE_CONFIG_H) #include "config/bitcoin-config.h" #if defined(_WIN32) diff --git a/src/script/interpreter.h b/src/script/interpreter.h index d1459ed62..6f13e75d7 100644 --- a/src/script/interpreter.h +++ b/src/script/interpreter.h @@ -131,7 +131,7 @@ protected: virtual bool VerifySignature(const std::vector& vchSig, const CPubKey& vchPubKey, const uint256& sighash) const; public: - TransactionSignatureChecker(const CTransaction* txToIn, unsigned int nInIn) : txTo(txToIn), nIn(nInIn) {} + TransactionSignatureChecker(const CTransaction* txToIn, unsigned int nInIn, const CAmount& amount) : txTo(txToIn), nIn(nInIn) {} bool CheckSig(const std::vector& scriptSig, const std::vector& vchPubKey, const CScript& scriptCode) const; bool CheckLockTime(const CScriptNum& nLockTime) const; bool CheckSequence(const CScriptNum& nSequence) const; @@ -143,7 +143,7 @@ private: const CTransaction txTo; public: - MutableTransactionSignatureChecker(const CMutableTransaction* txToIn, unsigned int nInIn) : TransactionSignatureChecker(&txTo, nInIn), txTo(*txToIn) {} + MutableTransactionSignatureChecker(const CMutableTransaction* txToIn, unsigned int nInIn, const CAmount& amount) : TransactionSignatureChecker(&txTo, nInIn, amount), txTo(*txToIn) {} }; bool EvalScript(std::vector >& stack, const CScript& script, unsigned int flags, const BaseSignatureChecker& checker, ScriptError* error = NULL); diff --git a/src/script/sigcache.h b/src/script/sigcache.h index be1df09c2..050bf8cc4 100644 --- a/src/script/sigcache.h +++ b/src/script/sigcache.h @@ -22,7 +22,7 @@ private: bool store; public: - CachingTransactionSignatureChecker(const CTransaction* txToIn, unsigned int nInIn, bool storeIn=true) : TransactionSignatureChecker(txToIn, nInIn), store(storeIn) {} + CachingTransactionSignatureChecker(const CTransaction* txToIn, unsigned int nInIn, const CAmount& amount, bool storeIn) : TransactionSignatureChecker(txToIn, nInIn, amount), store(storeIn) {} bool VerifySignature(const std::vector& vchSig, const CPubKey& vchPubKey, const uint256& sighash) const; }; diff --git a/src/script/sign.cpp b/src/script/sign.cpp index 37b702de1..62d874eed 100644 --- a/src/script/sign.cpp +++ b/src/script/sign.cpp @@ -18,7 +18,8 @@ using namespace std; typedef std::vector valtype; -TransactionSignatureCreator::TransactionSignatureCreator(const CKeyStore* keystoreIn, const CTransaction* txToIn, unsigned int nInIn, int nHashTypeIn) : BaseSignatureCreator(keystoreIn), txTo(txToIn), nIn(nInIn), nHashType(nHashTypeIn), checker(txTo, nIn) {} +static const CAmount amountZero = 0; +TransactionSignatureCreator::TransactionSignatureCreator(const CKeyStore* keystoreIn, const CTransaction* txToIn, unsigned int nInIn, int nHashTypeIn) : BaseSignatureCreator(keystoreIn), txTo(txToIn), nIn(nInIn), nHashType(nHashTypeIn), checker(txTo, nIn, amountZero) {} bool TransactionSignatureCreator::CreateSig(std::vector& vchSig, const CKeyID& address, const CScript& scriptCode) const { @@ -258,7 +259,7 @@ static CScript CombineSignatures(const CScript& scriptPubKey, const BaseSignatur CScript CombineSignatures(const CScript& scriptPubKey, const CTransaction& txTo, unsigned int nIn, const CScript& scriptSig1, const CScript& scriptSig2) { - TransactionSignatureChecker checker(&txTo, nIn); + TransactionSignatureChecker checker(&txTo, nIn, amountZero); return CombineSignatures(scriptPubKey, checker, scriptSig1, scriptSig2); } -- cgit v1.2.3 From 3dd410294d42f251e4808ef1dfcfcd64817edbac Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Sun, 27 Dec 2015 19:49:08 +0100 Subject: BIP143: Verification logic Includes simplifications by Eric Lombrozo. --- src/script/interpreter.cpp | 84 +++++++++++++++++++++++++++++++++++++++------- src/script/interpreter.h | 17 +++++++--- src/script/sign.cpp | 17 +++++----- src/script/sign.h | 5 +-- 4 files changed, 95 insertions(+), 28 deletions(-) (limited to 'src/script') diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp index be649fca2..4deebd050 100644 --- a/src/script/interpreter.cpp +++ b/src/script/interpreter.cpp @@ -229,7 +229,7 @@ bool static CheckMinimalPush(const valtype& data, opcodetype opcode) { return true; } -bool EvalScript(vector >& stack, const CScript& script, unsigned int flags, const BaseSignatureChecker& checker, ScriptError* serror) +bool EvalScript(vector >& stack, const CScript& script, unsigned int flags, const BaseSignatureChecker& checker, SigVersion sigversion, ScriptError* serror) { static const CScriptNum bnZero(0); static const CScriptNum bnOne(1); @@ -869,13 +869,15 @@ bool EvalScript(vector >& stack, const CScript& script, un CScript scriptCode(pbegincodehash, pend); // Drop the signature, since there's no way for a signature to sign itself - scriptCode.FindAndDelete(CScript(vchSig)); + if (sigversion == SIGVERSION_BASE) { + scriptCode.FindAndDelete(CScript(vchSig)); + } if (!CheckSignatureEncoding(vchSig, flags, serror) || !CheckPubKeyEncoding(vchPubKey, flags, serror)) { //serror is set return false; } - bool fSuccess = checker.CheckSig(vchSig, vchPubKey, scriptCode); + bool fSuccess = checker.CheckSig(vchSig, vchPubKey, scriptCode, sigversion); popstack(stack); popstack(stack); @@ -925,7 +927,9 @@ bool EvalScript(vector >& stack, const CScript& script, un for (int k = 0; k < nSigsCount; k++) { valtype& vchSig = stacktop(-isig-k); - scriptCode.FindAndDelete(CScript(vchSig)); + if (sigversion == SIGVERSION_BASE) { + scriptCode.FindAndDelete(CScript(vchSig)); + } } bool fSuccess = true; @@ -943,7 +947,7 @@ bool EvalScript(vector >& stack, const CScript& script, un } // Check signature - bool fOk = checker.CheckSig(vchSig, vchPubKey, scriptCode); + bool fOk = checker.CheckSig(vchSig, vchPubKey, scriptCode, sigversion); if (fOk) { isig++; @@ -1106,8 +1110,64 @@ public: } // anon namespace -uint256 SignatureHash(const CScript& scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType) +uint256 SignatureHash(const CScript& scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType, const CAmount& amount, SigVersion sigversion) { + if (sigversion == SIGVERSION_WITNESS_V0) { + uint256 hashPrevouts; + uint256 hashSequence; + uint256 hashOutputs; + + if (!(nHashType & SIGHASH_ANYONECANPAY)) { + CHashWriter ss(SER_GETHASH, 0); + for (unsigned int n = 0; n < txTo.vin.size(); n++) { + ss << txTo.vin[n].prevout; + } + hashPrevouts = ss.GetHash(); // TODO: cache this value for all signatures in a transaction + } + + if (!(nHashType & SIGHASH_ANYONECANPAY) && (nHashType & 0x1f) != SIGHASH_SINGLE && (nHashType & 0x1f) != SIGHASH_NONE) { + CHashWriter ss(SER_GETHASH, 0); + for (unsigned int n = 0; n < txTo.vin.size(); n++) { + ss << txTo.vin[n].nSequence; + } + hashSequence = ss.GetHash(); // TODO: cache this value for all signatures in a transaction + } + + if ((nHashType & 0x1f) != SIGHASH_SINGLE && (nHashType & 0x1f) != SIGHASH_NONE) { + CHashWriter ss(SER_GETHASH, 0); + for (unsigned int n = 0; n < txTo.vout.size(); n++) { + ss << txTo.vout[n]; + } + hashOutputs = ss.GetHash(); // TODO: cache this value for all signatures in a transaction + } else if ((nHashType & 0x1f) == SIGHASH_SINGLE && nIn < txTo.vout.size()) { + CHashWriter ss(SER_GETHASH, 0); + ss << txTo.vout[nIn]; + hashOutputs = ss.GetHash(); + } + + CHashWriter ss(SER_GETHASH, 0); + // Version + ss << txTo.nVersion; + // Input prevouts/nSequence (none/all, depending on flags) + ss << hashPrevouts; + ss << hashSequence; + // The input being signed (replacing the scriptSig with scriptCode + amount) + // The prevout may already be contained in hashPrevout, and the nSequence + // may already be contain in hashSequence. + ss << txTo.vin[nIn].prevout; + ss << static_cast(scriptCode); + ss << amount; + ss << txTo.vin[nIn].nSequence; + // Outputs (none/one/all, depending on flags) + ss << hashOutputs; + // Locktime + ss << txTo.nLockTime; + // Sighash type + ss << nHashType; + + return ss.GetHash(); + } + static const uint256 one(uint256S("0000000000000000000000000000000000000000000000000000000000000001")); if (nIn >= txTo.vin.size()) { // nIn out of range @@ -1136,7 +1196,7 @@ bool TransactionSignatureChecker::VerifySignature(const std::vector& vchSigIn, const vector& vchPubKey, const CScript& scriptCode) const +bool TransactionSignatureChecker::CheckSig(const vector& vchSigIn, const vector& vchPubKey, const CScript& scriptCode, SigVersion sigversion) const { CPubKey pubkey(vchPubKey); if (!pubkey.IsValid()) @@ -1149,7 +1209,7 @@ bool TransactionSignatureChecker::CheckSig(const vector& vchSigIn int nHashType = vchSig.back(); vchSig.pop_back(); - uint256 sighash = SignatureHash(scriptCode, *txTo, nIn, nHashType); + uint256 sighash = SignatureHash(scriptCode, *txTo, nIn, nHashType, amount, sigversion); if (!VerifySignature(vchSig, pubkey, sighash)) return false; @@ -1280,7 +1340,7 @@ static bool VerifyWitnessProgram(const CScriptWitness& witness, int witversion, return set_error(serror, SCRIPT_ERR_PUSH_SIZE); } - if (!EvalScript(stack, scriptPubKey, flags, checker, serror)) { + if (!EvalScript(stack, scriptPubKey, flags, checker, SIGVERSION_WITNESS_V0, serror)) { return false; } @@ -1307,12 +1367,12 @@ bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const C } vector > stack, stackCopy; - if (!EvalScript(stack, scriptSig, flags, checker, serror)) + if (!EvalScript(stack, scriptSig, flags, checker, SIGVERSION_BASE, serror)) // serror is set return false; if (flags & SCRIPT_VERIFY_P2SH) stackCopy = stack; - if (!EvalScript(stack, scriptPubKey, flags, checker, serror)) + if (!EvalScript(stack, scriptPubKey, flags, checker, SIGVERSION_BASE, serror)) // serror is set return false; if (stack.empty()) @@ -1358,7 +1418,7 @@ bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const C CScript pubKey2(pubKeySerialized.begin(), pubKeySerialized.end()); popstack(stack); - if (!EvalScript(stack, pubKey2, flags, checker, serror)) + if (!EvalScript(stack, pubKey2, flags, checker, SIGVERSION_BASE, serror)) // serror is set return false; if (stack.empty()) diff --git a/src/script/interpreter.h b/src/script/interpreter.h index 6f13e75d7..864372942 100644 --- a/src/script/interpreter.h +++ b/src/script/interpreter.h @@ -98,12 +98,18 @@ enum bool CheckSignatureEncoding(const std::vector &vchSig, unsigned int flags, ScriptError* serror); -uint256 SignatureHash(const CScript &scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType); +enum SigVersion +{ + SIGVERSION_BASE = 0, + SIGVERSION_WITNESS_V0 = 1, +}; + +uint256 SignatureHash(const CScript &scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType, const CAmount& amount, SigVersion sigversion); class BaseSignatureChecker { public: - virtual bool CheckSig(const std::vector& scriptSig, const std::vector& vchPubKey, const CScript& scriptCode) const + virtual bool CheckSig(const std::vector& scriptSig, const std::vector& vchPubKey, const CScript& scriptCode, SigVersion sigversion) const { return false; } @@ -126,13 +132,14 @@ class TransactionSignatureChecker : public BaseSignatureChecker private: const CTransaction* txTo; unsigned int nIn; + const CAmount amount; protected: virtual bool VerifySignature(const std::vector& vchSig, const CPubKey& vchPubKey, const uint256& sighash) const; public: - TransactionSignatureChecker(const CTransaction* txToIn, unsigned int nInIn, const CAmount& amount) : txTo(txToIn), nIn(nInIn) {} - bool CheckSig(const std::vector& scriptSig, const std::vector& vchPubKey, const CScript& scriptCode) const; + TransactionSignatureChecker(const CTransaction* txToIn, unsigned int nInIn, const CAmount& amountIn) : txTo(txToIn), nIn(nInIn), amount(amountIn) {} + bool CheckSig(const std::vector& scriptSig, const std::vector& vchPubKey, const CScript& scriptCode, SigVersion sigversion) const; bool CheckLockTime(const CScriptNum& nLockTime) const; bool CheckSequence(const CScriptNum& nSequence) const; }; @@ -146,7 +153,7 @@ public: MutableTransactionSignatureChecker(const CMutableTransaction* txToIn, unsigned int nInIn, const CAmount& amount) : TransactionSignatureChecker(&txTo, nInIn, amount), txTo(*txToIn) {} }; -bool EvalScript(std::vector >& stack, const CScript& script, unsigned int flags, const BaseSignatureChecker& checker, ScriptError* error = NULL); +bool EvalScript(std::vector >& stack, const CScript& script, unsigned int flags, const BaseSignatureChecker& checker, SigVersion sigversion, ScriptError* error = NULL); bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const CScriptWitness* witness, unsigned int flags, const BaseSignatureChecker& checker, ScriptError* serror = NULL); #endif // BITCOIN_SCRIPT_INTERPRETER_H diff --git a/src/script/sign.cpp b/src/script/sign.cpp index 62d874eed..83bb0cfa2 100644 --- a/src/script/sign.cpp +++ b/src/script/sign.cpp @@ -18,8 +18,7 @@ using namespace std; typedef std::vector valtype; -static const CAmount amountZero = 0; -TransactionSignatureCreator::TransactionSignatureCreator(const CKeyStore* keystoreIn, const CTransaction* txToIn, unsigned int nInIn, int nHashTypeIn) : BaseSignatureCreator(keystoreIn), txTo(txToIn), nIn(nInIn), nHashType(nHashTypeIn), checker(txTo, nIn, amountZero) {} +TransactionSignatureCreator::TransactionSignatureCreator(const CKeyStore* keystoreIn, const CTransaction* txToIn, unsigned int nInIn, const CAmount& amountIn, int nHashTypeIn) : BaseSignatureCreator(keystoreIn), txTo(txToIn), nIn(nInIn), nHashType(nHashTypeIn), amount(amountIn), checker(txTo, nIn, amountIn) {} bool TransactionSignatureCreator::CreateSig(std::vector& vchSig, const CKeyID& address, const CScript& scriptCode) const { @@ -27,7 +26,7 @@ bool TransactionSignatureCreator::CreateSig(std::vector& vchSig, if (!keystore->GetKey(address, key)) return false; - uint256 hash = SignatureHash(scriptCode, *txTo, nIn, nHashType); + uint256 hash = SignatureHash(scriptCode, *txTo, nIn, nHashType, amount, SIGVERSION_BASE); if (!key.Sign(hash, vchSig)) return false; vchSig.push_back((unsigned char)nHashType); @@ -186,7 +185,7 @@ static CScript CombineMultisig(const CScript& scriptPubKey, const BaseSignatureC if (sigs.count(pubkey)) continue; // Already got a sig for this pubkey - if (checker.CheckSig(sig, pubkey, scriptPubKey)) + if (checker.CheckSig(sig, pubkey, scriptPubKey, SIGVERSION_BASE)) { sigs[pubkey] = sig; break; @@ -256,10 +255,10 @@ static CScript CombineSignatures(const CScript& scriptPubKey, const BaseSignatur return CScript(); } -CScript CombineSignatures(const CScript& scriptPubKey, const CTransaction& txTo, unsigned int nIn, +CScript CombineSignatures(const CScript& scriptPubKey, const CTransaction& txTo, unsigned int nIn, const CAmount& amount, const CScript& scriptSig1, const CScript& scriptSig2) { - TransactionSignatureChecker checker(&txTo, nIn, amountZero); + TransactionSignatureChecker checker(&txTo, nIn, amount); return CombineSignatures(scriptPubKey, checker, scriptSig1, scriptSig2); } @@ -271,9 +270,9 @@ CScript CombineSignatures(const CScript& scriptPubKey, const BaseSignatureChecke Solver(scriptPubKey, txType, vSolutions); vector stack1; - EvalScript(stack1, scriptSig1, SCRIPT_VERIFY_STRICTENC, BaseSignatureChecker()); + EvalScript(stack1, scriptSig1, SCRIPT_VERIFY_STRICTENC, BaseSignatureChecker(), SIGVERSION_BASE); vector stack2; - EvalScript(stack2, scriptSig2, SCRIPT_VERIFY_STRICTENC, BaseSignatureChecker()); + EvalScript(stack2, scriptSig2, SCRIPT_VERIFY_STRICTENC, BaseSignatureChecker(), SIGVERSION_BASE); return CombineSignatures(scriptPubKey, checker, txType, vSolutions, stack1, stack2); } @@ -285,7 +284,7 @@ class DummySignatureChecker : public BaseSignatureChecker public: DummySignatureChecker() {} - bool CheckSig(const std::vector& scriptSig, const std::vector& vchPubKey, const CScript& scriptCode) const + bool CheckSig(const std::vector& scriptSig, const std::vector& vchPubKey, const CScript& scriptCode, SigVersion sigversion) const { return true; } diff --git a/src/script/sign.h b/src/script/sign.h index 47a9cde7f..f54511f7a 100644 --- a/src/script/sign.h +++ b/src/script/sign.h @@ -35,10 +35,11 @@ class TransactionSignatureCreator : public BaseSignatureCreator { const CTransaction* txTo; unsigned int nIn; int nHashType; + CAmount amount; const TransactionSignatureChecker checker; public: - TransactionSignatureCreator(const CKeyStore* keystoreIn, const CTransaction* txToIn, unsigned int nInIn, int nHashTypeIn=SIGHASH_ALL); + TransactionSignatureCreator(const CKeyStore* keystoreIn, const CTransaction* txToIn, unsigned int nInIn, const CAmount& amountIn, int nHashTypeIn=SIGHASH_ALL); const BaseSignatureChecker& Checker() const { return checker; } bool CreateSig(std::vector& vchSig, const CKeyID& keyid, const CScript& scriptCode) const; }; @@ -62,6 +63,6 @@ bool SignSignature(const CKeyStore& keystore, const CTransaction& txFrom, CMutab CScript CombineSignatures(const CScript& scriptPubKey, const BaseSignatureChecker& checker, const CScript& scriptSig1, const CScript& scriptSig2); /** Combine two script signatures on transactions. */ -CScript CombineSignatures(const CScript& scriptPubKey, const CTransaction& txTo, unsigned int nIn, const CScript& scriptSig1, const CScript& scriptSig2); +CScript CombineSignatures(const CScript& scriptPubKey, const CTransaction& txTo, unsigned int nIn, const CAmount& amount, const CScript& scriptSig1, const CScript& scriptSig2); #endif // BITCOIN_SCRIPT_SIGN_H -- cgit v1.2.3 From 2b1f6f9ccf36f1e0a2c9d99154e1642f796d7c2b Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Sun, 3 Jan 2016 18:54:50 +0100 Subject: BIP141: Other consensus critical limits, and BIP145 Includes changes by Suhas Daftuar, Luke-jr, and mruddy. --- src/script/interpreter.cpp | 47 ++++++++++++++++++++++++++++++++++++++++++++++ src/script/interpreter.h | 2 ++ 2 files changed, 49 insertions(+) (limited to 'src/script') diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp index 4deebd050..bc027e9f0 100644 --- a/src/script/interpreter.cpp +++ b/src/script/interpreter.cpp @@ -1470,3 +1470,50 @@ bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const C return set_success(serror); } + +size_t static WitnessSigOps(int witversion, const std::vector& witprogram, const CScriptWitness& witness, int flags) +{ + if (witversion == 0) { + if (witprogram.size() == 20) + return 1; + + if (witprogram.size() == 32 && witness.stack.size() > 0) { + CScript subscript(witness.stack.back().begin(), witness.stack.back().end()); + return subscript.GetSigOpCount(true); + } + } + + // Future flags may be implemented here. + return 0; +} + +size_t CountWitnessSigOps(const CScript& scriptSig, const CScript& scriptPubKey, const CScriptWitness* witness, unsigned int flags) +{ + static const CScriptWitness witnessEmpty; + + if ((flags & SCRIPT_VERIFY_WITNESS) == 0) { + return 0; + } + assert((flags & SCRIPT_VERIFY_P2SH) != 0); + + int witnessversion; + std::vector witnessprogram; + if (scriptPubKey.IsWitnessProgram(witnessversion, witnessprogram)) { + return WitnessSigOps(witnessversion, witnessprogram, witness ? *witness : witnessEmpty, flags); + } + + if (scriptPubKey.IsPayToScriptHash() && scriptSig.IsPushOnly()) { + CScript::const_iterator pc = scriptSig.begin(); + vector data; + while (pc < scriptSig.end()) { + opcodetype opcode; + scriptSig.GetOp(pc, opcode, data); + } + CScript subscript(data.begin(), data.end()); + if (subscript.IsWitnessProgram(witnessversion, witnessprogram)) { + return WitnessSigOps(witnessversion, witnessprogram, witness ? *witness : witnessEmpty, flags); + } + } + + return 0; +} diff --git a/src/script/interpreter.h b/src/script/interpreter.h index 864372942..bd2f21166 100644 --- a/src/script/interpreter.h +++ b/src/script/interpreter.h @@ -156,4 +156,6 @@ public: bool EvalScript(std::vector >& stack, const CScript& script, unsigned int flags, const BaseSignatureChecker& checker, SigVersion sigversion, ScriptError* error = NULL); bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const CScriptWitness* witness, unsigned int flags, const BaseSignatureChecker& checker, ScriptError* serror = NULL); +size_t CountWitnessSigOps(const CScript& scriptSig, const CScript& scriptPubKey, const CScriptWitness* witness, unsigned int flags); + #endif // BITCOIN_SCRIPT_INTERPRETER_H -- cgit v1.2.3 From b7dbeb24ebff16198b2925d906c06771e167bd9e Mon Sep 17 00:00:00 2001 From: Thomas Kerin Date: Sun, 24 Jan 2016 16:29:39 +0000 Subject: [libconsensus] Script verification API with amounts script_tests: always test bitcoinconsensus_verify_script_with_amount if VERIFY_WITNESS isn't set Rename internal method + make it static trim bitcoinconsensus_ prefix Add SERIALIZE_TRANSACTION_WITNESS flag --- src/script/bitcoinconsensus.cpp | 30 +++++++++++++++++++++++++----- src/script/bitcoinconsensus.h | 8 +++++++- 2 files changed, 32 insertions(+), 6 deletions(-) (limited to 'src/script') diff --git a/src/script/bitcoinconsensus.cpp b/src/script/bitcoinconsensus.cpp index 26e7a85b7..62fd9031f 100644 --- a/src/script/bitcoinconsensus.cpp +++ b/src/script/bitcoinconsensus.cpp @@ -69,7 +69,7 @@ struct ECCryptoClosure ECCryptoClosure instance_of_eccryptoclosure; } -int bitcoinconsensus_verify_script(const unsigned char *scriptPubKey, unsigned int scriptPubKeyLen, +static int verify_script(const unsigned char *scriptPubKey, unsigned int scriptPubKeyLen, CAmount amount, const unsigned char *txTo , unsigned int txToLen, unsigned int nIn, unsigned int flags, bitcoinconsensus_error* err) { @@ -82,16 +82,36 @@ int bitcoinconsensus_verify_script(const unsigned char *scriptPubKey, unsigned i if (tx.GetSerializeSize(SER_NETWORK, PROTOCOL_VERSION) != txToLen) return set_error(err, bitcoinconsensus_ERR_TX_SIZE_MISMATCH); - // Regardless of the verification result, the tx did not error. - set_error(err, bitcoinconsensus_ERR_OK); + // Regardless of the verification result, the tx did not error. + set_error(err, bitcoinconsensus_ERR_OK); - CAmount am(0); - return VerifyScript(tx.vin[nIn].scriptSig, CScript(scriptPubKey, scriptPubKey + scriptPubKeyLen), nIn < tx.wit.vtxinwit.size() ? &tx.wit.vtxinwit[nIn].scriptWitness : NULL, flags, TransactionSignatureChecker(&tx, nIn, am), NULL); + return VerifyScript(tx.vin[nIn].scriptSig, CScript(scriptPubKey, scriptPubKey + scriptPubKeyLen), nIn < tx.wit.vtxinwit.size() ? &tx.wit.vtxinwit[nIn].scriptWitness : NULL, flags, TransactionSignatureChecker(&tx, nIn, amount), NULL); } catch (const std::exception&) { return set_error(err, bitcoinconsensus_ERR_TX_DESERIALIZE); // Error deserializing } } +int bitcoinconsensus_verify_script_with_amount(const unsigned char *scriptPubKey, unsigned int scriptPubKeyLen, int64_t amount, + const unsigned char *txTo , unsigned int txToLen, + unsigned int nIn, unsigned int flags, bitcoinconsensus_error* err) +{ + CAmount am(amount); + return ::verify_script(scriptPubKey, scriptPubKeyLen, am, txTo, txToLen, nIn, flags, err); +} + + +int bitcoinconsensus_verify_script(const unsigned char *scriptPubKey, unsigned int scriptPubKeyLen, + const unsigned char *txTo , unsigned int txToLen, + unsigned int nIn, unsigned int flags, bitcoinconsensus_error* err) +{ + if (flags & bitcoinconsensus_SCRIPT_FLAGS_VERIFY_WITNESS) { + return set_error(err, bitcoinconsensus_ERR_AMOUNT_REQUIRED); + } + + CAmount am(0); + return ::verify_script(scriptPubKey, scriptPubKeyLen, am, txTo, txToLen, nIn, flags, err); +} + unsigned int bitcoinconsensus_version() { // Just use the API version for now diff --git a/src/script/bitcoinconsensus.h b/src/script/bitcoinconsensus.h index cd3e2f773..6f868d0d6 100644 --- a/src/script/bitcoinconsensus.h +++ b/src/script/bitcoinconsensus.h @@ -33,7 +33,7 @@ extern "C" { #endif -#define BITCOINCONSENSUS_API_VER 0 +#define BITCOINCONSENSUS_API_VER 1 typedef enum bitcoinconsensus_error_t { @@ -41,6 +41,7 @@ typedef enum bitcoinconsensus_error_t bitcoinconsensus_ERR_TX_INDEX, bitcoinconsensus_ERR_TX_SIZE_MISMATCH, bitcoinconsensus_ERR_TX_DESERIALIZE, + bitcoinconsensus_ERR_AMOUNT_REQUIRED, } bitcoinconsensus_error; /** Script verification flags */ @@ -50,6 +51,7 @@ enum bitcoinconsensus_SCRIPT_FLAGS_VERIFY_P2SH = (1U << 0), // evaluate P2SH (BIP16) subscripts bitcoinconsensus_SCRIPT_FLAGS_VERIFY_DERSIG = (1U << 2), // enforce strict DER (BIP66) compliance bitcoinconsensus_SCRIPT_FLAGS_VERIFY_CHECKLOCKTIMEVERIFY = (1U << 9), // enable CHECKLOCKTIMEVERIFY (BIP65) + bitcoinconsensus_SCRIPT_FLAGS_VERIFY_WITNESS = (1U << 11), // enable WITNESS (BIP141) }; /// Returns 1 if the input nIn of the serialized transaction pointed to by @@ -57,6 +59,10 @@ enum /// the additional constraints specified by flags. /// If not NULL, err will contain an error/success code for the operation EXPORT_SYMBOL int bitcoinconsensus_verify_script(const unsigned char *scriptPubKey, unsigned int scriptPubKeyLen, + const unsigned char *txTo , unsigned int txToLen, + unsigned int nIn, unsigned int flags, bitcoinconsensus_error* err); + +EXPORT_SYMBOL int bitcoinconsensus_verify_script_with_amount(const unsigned char *scriptPubKey, unsigned int scriptPubKeyLen, int64_t amount, const unsigned char *txTo , unsigned int txToLen, unsigned int nIn, unsigned int flags, bitcoinconsensus_error* err); -- cgit v1.2.3 From 605e8473a7ddca13b24a4020c7bd630aa5d374e2 Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Thu, 31 Mar 2016 14:54:58 +0200 Subject: BIP143: Signing logic --- src/script/ismine.cpp | 19 +++- src/script/sign.cpp | 279 ++++++++++++++++++++++++++++++++++-------------- src/script/sign.h | 34 ++++-- src/script/standard.cpp | 41 +++++++ src/script/standard.h | 3 + 5 files changed, 282 insertions(+), 94 deletions(-) (limited to 'src/script') diff --git a/src/script/ismine.cpp b/src/script/ismine.cpp index 535c56b57..0bf180341 100644 --- a/src/script/ismine.cpp +++ b/src/script/ismine.cpp @@ -57,6 +57,7 @@ isminetype IsMine(const CKeyStore &keystore, const CScript& scriptPubKey) return ISMINE_SPENDABLE; break; case TX_PUBKEYHASH: + case TX_WITNESS_V0_KEYHASH: keyID = CKeyID(uint160(vSolutions[0])); if (keystore.HaveKey(keyID)) return ISMINE_SPENDABLE; @@ -72,6 +73,20 @@ isminetype IsMine(const CKeyStore &keystore, const CScript& scriptPubKey) } break; } + case TX_WITNESS_V0_SCRIPTHASH: + { + uint160 hash; + CRIPEMD160().Write(&vSolutions[0][0], vSolutions[0].size()).Finalize(hash.begin()); + CScriptID scriptID = CScriptID(hash); + CScript subscript; + if (keystore.GetCScript(scriptID, subscript)) { + isminetype ret = IsMine(keystore, subscript); + if (ret == ISMINE_SPENDABLE) + return ret; + } + break; + } + case TX_MULTISIG: { // Only consider transactions "mine" if we own ALL the @@ -88,8 +103,8 @@ isminetype IsMine(const CKeyStore &keystore, const CScript& scriptPubKey) if (keystore.HaveWatchOnly(scriptPubKey)) { // TODO: This could be optimized some by doing some work after the above solver - CScript scriptSig; - return ProduceSignature(DummySignatureCreator(&keystore), scriptPubKey, scriptSig) ? ISMINE_WATCH_SOLVABLE : ISMINE_WATCH_UNSOLVABLE; + SignatureData sigs; + return ProduceSignature(DummySignatureCreator(&keystore), scriptPubKey, sigs) ? ISMINE_WATCH_SOLVABLE : ISMINE_WATCH_UNSOLVABLE; } return ISMINE_NO; } diff --git a/src/script/sign.cpp b/src/script/sign.cpp index 83bb0cfa2..87f38d9c7 100644 --- a/src/script/sign.cpp +++ b/src/script/sign.cpp @@ -20,29 +20,29 @@ typedef std::vector valtype; TransactionSignatureCreator::TransactionSignatureCreator(const CKeyStore* keystoreIn, const CTransaction* txToIn, unsigned int nInIn, const CAmount& amountIn, int nHashTypeIn) : BaseSignatureCreator(keystoreIn), txTo(txToIn), nIn(nInIn), nHashType(nHashTypeIn), amount(amountIn), checker(txTo, nIn, amountIn) {} -bool TransactionSignatureCreator::CreateSig(std::vector& vchSig, const CKeyID& address, const CScript& scriptCode) const +bool TransactionSignatureCreator::CreateSig(std::vector& vchSig, const CKeyID& address, const CScript& scriptCode, SigVersion sigversion) const { CKey key; if (!keystore->GetKey(address, key)) return false; - uint256 hash = SignatureHash(scriptCode, *txTo, nIn, nHashType, amount, SIGVERSION_BASE); + uint256 hash = SignatureHash(scriptCode, *txTo, nIn, nHashType, amount, sigversion); if (!key.Sign(hash, vchSig)) return false; vchSig.push_back((unsigned char)nHashType); return true; } -static bool Sign1(const CKeyID& address, const BaseSignatureCreator& creator, const CScript& scriptCode, CScript& scriptSigRet) +static bool Sign1(const CKeyID& address, const BaseSignatureCreator& creator, const CScript& scriptCode, std::vector& ret, SigVersion sigversion) { vector vchSig; - if (!creator.CreateSig(vchSig, address, scriptCode)) + if (!creator.CreateSig(vchSig, address, scriptCode, sigversion)) return false; - scriptSigRet << vchSig; + ret.push_back(vchSig); return true; } -static bool SignN(const vector& multisigdata, const BaseSignatureCreator& creator, const CScript& scriptCode, CScript& scriptSigRet) +static bool SignN(const vector& multisigdata, const BaseSignatureCreator& creator, const CScript& scriptCode, std::vector& ret, SigVersion sigversion) { int nSigned = 0; int nRequired = multisigdata.front()[0]; @@ -50,7 +50,7 @@ static bool SignN(const vector& multisigdata, const BaseSignatureCreato { const valtype& pubkey = multisigdata[i]; CKeyID keyID = CPubKey(pubkey).GetID(); - if (Sign1(keyID, creator, scriptCode, scriptSigRet)) + if (Sign1(keyID, creator, scriptCode, ret, sigversion)) ++nSigned; } return nSigned==nRequired; @@ -63,9 +63,11 @@ static bool SignN(const vector& multisigdata, const BaseSignatureCreato * Returns false if scriptPubKey could not be completely satisfied. */ static bool SignStep(const BaseSignatureCreator& creator, const CScript& scriptPubKey, - CScript& scriptSigRet, txnouttype& whichTypeRet) + std::vector& ret, txnouttype& whichTypeRet, SigVersion sigversion) { - scriptSigRet.clear(); + CScript scriptRet; + uint160 h160; + ret.clear(); vector vSolutions; if (!Solver(scriptPubKey, whichTypeRet, vSolutions)) @@ -79,62 +81,142 @@ static bool SignStep(const BaseSignatureCreator& creator, const CScript& scriptP return false; case TX_PUBKEY: keyID = CPubKey(vSolutions[0]).GetID(); - return Sign1(keyID, creator, scriptPubKey, scriptSigRet); + return Sign1(keyID, creator, scriptPubKey, ret, sigversion); case TX_PUBKEYHASH: keyID = CKeyID(uint160(vSolutions[0])); - if (!Sign1(keyID, creator, scriptPubKey, scriptSigRet)) + if (!Sign1(keyID, creator, scriptPubKey, ret, sigversion)) return false; else { CPubKey vch; creator.KeyStore().GetPubKey(keyID, vch); - scriptSigRet << ToByteVector(vch); + ret.push_back(ToByteVector(vch)); } return true; case TX_SCRIPTHASH: - return creator.KeyStore().GetCScript(uint160(vSolutions[0]), scriptSigRet); + if (creator.KeyStore().GetCScript(uint160(vSolutions[0]), scriptRet)) { + ret.push_back(std::vector(scriptRet.begin(), scriptRet.end())); + return true; + } + return false; case TX_MULTISIG: - scriptSigRet << OP_0; // workaround CHECKMULTISIG bug - return (SignN(vSolutions, creator, scriptPubKey, scriptSigRet)); + ret.push_back(valtype()); // workaround CHECKMULTISIG bug + return (SignN(vSolutions, creator, scriptPubKey, ret, sigversion)); + + 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 (creator.KeyStore().GetCScript(h160, scriptRet)) { + ret.push_back(std::vector(scriptRet.begin(), scriptRet.end())); + return true; + } + return false; + + default: + return false; } - return false; } -bool ProduceSignature(const BaseSignatureCreator& creator, const CScript& fromPubKey, CScript& scriptSig) +static CScript PushAll(const vector& values) { + CScript result; + BOOST_FOREACH(const valtype& v, values) { + if (v.size() == 0) { + result << OP_0; + } else if (v.size() == 1 && v[0] >= 1 && v[0] <= 16) { + result << CScript::EncodeOP_N(v[0]); + } else { + result << v; + } + } + return result; +} + +bool ProduceSignature(const BaseSignatureCreator& creator, const CScript& fromPubKey, SignatureData& sigdata) +{ + CScript script = fromPubKey; + bool solved = true; + std::vector result; txnouttype whichType; - if (!SignStep(creator, fromPubKey, scriptSig, whichType)) - return false; + solved = SignStep(creator, script, result, whichType, SIGVERSION_BASE); + bool P2SH = false; + CScript subscript; + sigdata.scriptWitness.stack.clear(); - if (whichType == TX_SCRIPTHASH) + if (solved && whichType == TX_SCRIPTHASH) { - // Solver returns the subscript that need to be evaluated; + // Solver returns the subscript that needs to be evaluated; // the final scriptSig is the signatures from that // and then the serialized subscript: - CScript subscript = scriptSig; + script = subscript = CScript(result[0].begin(), result[0].end()); + solved = solved && SignStep(creator, script, result, whichType, SIGVERSION_BASE) && whichType != TX_SCRIPTHASH; + P2SH = true; + } + if (solved && whichType == TX_WITNESS_V0_KEYHASH) + { + CScript witnessscript; + witnessscript << OP_DUP << OP_HASH160 << ToByteVector(result[0]) << OP_EQUALVERIFY << OP_CHECKSIG; txnouttype subType; - bool fSolved = - SignStep(creator, subscript, scriptSig, subType) && subType != TX_SCRIPTHASH; - // Append serialized subscript whether or not it is completely signed: - scriptSig << valtype(subscript.begin(), subscript.end()); - if (!fSolved) return false; + solved = solved && SignStep(creator, witnessscript, result, subType, SIGVERSION_WITNESS_V0); + sigdata.scriptWitness.stack = result; + result.clear(); + } + else if (solved && whichType == TX_WITNESS_V0_SCRIPTHASH) + { + CScript witnessscript(result[0].begin(), result[0].end()); + txnouttype subType; + solved = solved && SignStep(creator, witnessscript, result, subType, SIGVERSION_WITNESS_V0) && subType != TX_SCRIPTHASH && subType != TX_WITNESS_V0_SCRIPTHASH && subType != TX_WITNESS_V0_KEYHASH; + result.push_back(std::vector(witnessscript.begin(), witnessscript.end())); + sigdata.scriptWitness.stack = result; + result.clear(); } + if (P2SH) { + result.push_back(std::vector(subscript.begin(), subscript.end())); + } + sigdata.scriptSig = PushAll(result); + // Test solution - return VerifyScript(scriptSig, fromPubKey, NULL, STANDARD_SCRIPT_VERIFY_FLAGS, creator.Checker()); + 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; + if (tx.wit.vtxinwit.size() > nIn) { + data.scriptWitness = tx.wit.vtxinwit[nIn].scriptWitness; + } + return data; +} + +void UpdateTransaction(CMutableTransaction& tx, unsigned int nIn, const SignatureData& data) +{ + assert(tx.vin.size() > nIn); + tx.vin[nIn].scriptSig = data.scriptSig; + if (!data.scriptWitness.IsNull() || tx.wit.vtxinwit.size() > nIn) { + tx.wit.vtxinwit.resize(tx.vin.size()); + tx.wit.vtxinwit[nIn].scriptWitness = data.scriptWitness; + } } -bool SignSignature(const CKeyStore &keystore, const CScript& fromPubKey, CMutableTransaction& txTo, unsigned int nIn, int nHashType) +bool SignSignature(const CKeyStore &keystore, const CScript& fromPubKey, CMutableTransaction& txTo, unsigned int nIn, const CAmount& amount, int nHashType) { assert(nIn < txTo.vin.size()); - CTxIn& txin = txTo.vin[nIn]; CTransaction txToConst(txTo); - TransactionSignatureCreator creator(&keystore, &txToConst, nIn, nHashType); + TransactionSignatureCreator creator(&keystore, &txToConst, nIn, amount, nHashType); - return ProduceSignature(creator, fromPubKey, txin.scriptSig); + SignatureData sigdata; + bool ret = ProduceSignature(creator, fromPubKey, sigdata); + UpdateTransaction(txTo, nIn, sigdata); + return ret; } bool SignSignature(const CKeyStore &keystore, const CTransaction& txFrom, CMutableTransaction& txTo, unsigned int nIn, int nHashType) @@ -144,20 +226,12 @@ bool SignSignature(const CKeyStore &keystore, const CTransaction& txFrom, CMutab assert(txin.prevout.n < txFrom.vout.size()); const CTxOut& txout = txFrom.vout[txin.prevout.n]; - return SignSignature(keystore, txout.scriptPubKey, txTo, nIn, nHashType); -} - -static CScript PushAll(const vector& values) -{ - CScript result; - BOOST_FOREACH(const valtype& v, values) - result << v; - return result; + return SignSignature(keystore, txout.scriptPubKey, txTo, nIn, txout.nValue, nHashType); } -static CScript CombineMultisig(const CScript& scriptPubKey, const BaseSignatureChecker& checker, +static vector CombineMultisig(const CScript& scriptPubKey, const BaseSignatureChecker& checker, const vector& vSolutions, - const vector& sigs1, const vector& sigs2) + const vector& sigs1, const vector& sigs2, SigVersion sigversion) { // Combine all the signatures we've got: set allsigs; @@ -185,7 +259,7 @@ static CScript CombineMultisig(const CScript& scriptPubKey, const BaseSignatureC if (sigs.count(pubkey)) continue; // Already got a sig for this pubkey - if (checker.CheckSig(sig, pubkey, scriptPubKey, SIGVERSION_BASE)) + if (checker.CheckSig(sig, pubkey, scriptPubKey, sigversion)) { sigs[pubkey] = sig; break; @@ -194,87 +268,126 @@ static CScript CombineMultisig(const CScript& scriptPubKey, const BaseSignatureC } // Now build a merged CScript: unsigned int nSigsHave = 0; - CScript result; result << OP_0; // pop-one-too-many workaround + std::vector 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 << sigs[vSolutions[i+1]]; + result.push_back(sigs[vSolutions[i+1]]); ++nSigsHave; } } // Fill any missing with OP_0: for (unsigned int i = nSigsHave; i < nSigsRequired; i++) - result << OP_0; + result.push_back(valtype()); return result; } -static CScript CombineSignatures(const CScript& scriptPubKey, const BaseSignatureChecker& checker, +namespace +{ +struct Stacks +{ + std::vector script; + std::vector witness; + + Stacks() {} + explicit Stacks(const std::vector& scriptSigStack_) : script(scriptSigStack_), witness() {} + explicit Stacks(const SignatureData& data) : witness(data.scriptWitness.stack) { + EvalScript(script, data.scriptSig, SCRIPT_VERIFY_STRICTENC, BaseSignatureChecker(), SIGVERSION_BASE); + } + + SignatureData Output() const { + SignatureData result; + result.scriptSig = PushAll(script); + result.scriptWitness.stack = witness; + return result; + } +}; +} + +static Stacks CombineSignatures(const CScript& scriptPubKey, const BaseSignatureChecker& checker, const txnouttype txType, const vector& vSolutions, - vector& sigs1, vector& sigs2) + Stacks sigs1, Stacks sigs2, SigVersion sigversion) { switch (txType) { case TX_NONSTANDARD: case TX_NULL_DATA: // Don't know anything about this, assume bigger one is correct: - if (sigs1.size() >= sigs2.size()) - return PushAll(sigs1); - return PushAll(sigs2); + 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.empty() || sigs1[0].empty()) - return PushAll(sigs2); - return PushAll(sigs1); + 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.empty() || sigs1.back().empty()) - return PushAll(sigs2); - else if (sigs2.empty() || sigs2.back().empty()) - return PushAll(sigs1); + 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.back(); + valtype spk = sigs1.script.back(); CScript pubKey2(spk.begin(), spk.end()); txnouttype txType2; vector > vSolutions2; Solver(pubKey2, txType2, vSolutions2); - sigs1.pop_back(); - sigs2.pop_back(); - CScript result = CombineSignatures(pubKey2, checker, txType2, vSolutions2, sigs1, sigs2); - result << spk; + 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 CombineMultisig(scriptPubKey, checker, vSolutions, sigs1, sigs2); + 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; + vector 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; + } + default: + return Stacks(); } - - return CScript(); } -CScript CombineSignatures(const CScript& scriptPubKey, const CTransaction& txTo, unsigned int nIn, const CAmount& amount, - const CScript& scriptSig1, const CScript& scriptSig2) -{ - TransactionSignatureChecker checker(&txTo, nIn, amount); - return CombineSignatures(scriptPubKey, checker, scriptSig1, scriptSig2); -} - -CScript CombineSignatures(const CScript& scriptPubKey, const BaseSignatureChecker& checker, - const CScript& scriptSig1, const CScript& scriptSig2) +SignatureData CombineSignatures(const CScript& scriptPubKey, const BaseSignatureChecker& checker, + const SignatureData& scriptSig1, const SignatureData& scriptSig2) { txnouttype txType; vector > vSolutions; Solver(scriptPubKey, txType, vSolutions); - vector stack1; - EvalScript(stack1, scriptSig1, SCRIPT_VERIFY_STRICTENC, BaseSignatureChecker(), SIGVERSION_BASE); - vector stack2; - EvalScript(stack2, scriptSig2, SCRIPT_VERIFY_STRICTENC, BaseSignatureChecker(), SIGVERSION_BASE); - - return CombineSignatures(scriptPubKey, checker, txType, vSolutions, stack1, stack2); + return CombineSignatures(scriptPubKey, checker, txType, vSolutions, Stacks(scriptSig1), Stacks(scriptSig2), SIGVERSION_BASE).Output(); } namespace { @@ -297,7 +410,7 @@ const BaseSignatureChecker& DummySignatureCreator::Checker() const return dummyChecker; } -bool DummySignatureCreator::CreateSig(std::vector& vchSig, const CKeyID& keyid, const CScript& scriptCode) const +bool DummySignatureCreator::CreateSig(std::vector& vchSig, const CKeyID& keyid, const CScript& scriptCode, SigVersion sigversion) const { // Create a dummy signature that is a valid DER-encoding vchSig.assign(72, '\000'); diff --git a/src/script/sign.h b/src/script/sign.h index f54511f7a..6404b4523 100644 --- a/src/script/sign.h +++ b/src/script/sign.h @@ -27,7 +27,7 @@ public: virtual const BaseSignatureChecker& Checker() const =0; /** Create a singular (non-script) signature. */ - virtual bool CreateSig(std::vector& vchSig, const CKeyID& keyid, const CScript& scriptCode) const =0; + virtual bool CreateSig(std::vector& vchSig, const CKeyID& keyid, const CScript& scriptCode, SigVersion sigversion) const =0; }; /** A signature creator for transactions. */ @@ -41,7 +41,14 @@ class TransactionSignatureCreator : public BaseSignatureCreator { public: TransactionSignatureCreator(const CKeyStore* keystoreIn, const CTransaction* txToIn, unsigned int nInIn, const CAmount& amountIn, int nHashTypeIn=SIGHASH_ALL); const BaseSignatureChecker& Checker() const { return checker; } - bool CreateSig(std::vector& vchSig, const CKeyID& keyid, const CScript& scriptCode) const; + bool CreateSig(std::vector& vchSig, const CKeyID& keyid, const CScript& scriptCode, SigVersion sigversion) const; +}; + +class MutableTransactionSignatureCreator : public TransactionSignatureCreator { + CTransaction tx; + +public: + MutableTransactionSignatureCreator(const CKeyStore* keystoreIn, const CMutableTransaction* txToIn, unsigned int nInIn, const CAmount& amount, int nHashTypeIn) : TransactionSignatureCreator(keystoreIn, &tx, nInIn, amount, nHashTypeIn), tx(*txToIn) {} }; /** A signature creator that just produces 72-byte empty signatyres. */ @@ -49,20 +56,29 @@ class DummySignatureCreator : public BaseSignatureCreator { public: DummySignatureCreator(const CKeyStore* keystoreIn) : BaseSignatureCreator(keystoreIn) {} const BaseSignatureChecker& Checker() const; - bool CreateSig(std::vector& vchSig, const CKeyID& keyid, const CScript& scriptCode) const; + bool CreateSig(std::vector& vchSig, const CKeyID& keyid, const CScript& scriptCode, SigVersion sigversion) const; +}; + +struct SignatureData { + CScript scriptSig; + CScriptWitness scriptWitness; + + SignatureData() {} + explicit SignatureData(const CScript& script) : scriptSig(script) {} }; /** Produce a script signature using a generic signature creator. */ -bool ProduceSignature(const BaseSignatureCreator& creator, const CScript& scriptPubKey, CScript& scriptSig); +bool ProduceSignature(const BaseSignatureCreator& creator, const CScript& scriptPubKey, SignatureData& sigdata); /** Produce a script signature for a transaction. */ -bool SignSignature(const CKeyStore& keystore, const CScript& fromPubKey, CMutableTransaction& txTo, unsigned int nIn, int nHashType=SIGHASH_ALL); -bool SignSignature(const CKeyStore& keystore, const CTransaction& txFrom, CMutableTransaction& txTo, unsigned int nIn, int nHashType=SIGHASH_ALL); +bool SignSignature(const CKeyStore &keystore, const CScript& fromPubKey, CMutableTransaction& txTo, unsigned int nIn, const CAmount& amount, int nHashType); +bool SignSignature(const CKeyStore& keystore, 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. */ -CScript CombineSignatures(const CScript& scriptPubKey, const BaseSignatureChecker& checker, const CScript& scriptSig1, const CScript& scriptSig2); +SignatureData CombineSignatures(const CScript& scriptPubKey, const BaseSignatureChecker& checker, const SignatureData& scriptSig1, const SignatureData& scriptSig2); -/** Combine two script signatures on transactions. */ -CScript CombineSignatures(const CScript& scriptPubKey, const CTransaction& txTo, unsigned int nIn, const CAmount& amount, const CScript& scriptSig1, const CScript& 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); #endif // BITCOIN_SCRIPT_SIGN_H diff --git a/src/script/standard.cpp b/src/script/standard.cpp index 67b6af327..bb178f49f 100644 --- a/src/script/standard.cpp +++ b/src/script/standard.cpp @@ -31,6 +31,8 @@ const char* GetTxnOutputType(txnouttype t) case TX_SCRIPTHASH: return "scripthash"; case TX_MULTISIG: return "multisig"; case TX_NULL_DATA: return "nulldata"; + case TX_WITNESS_V0_KEYHASH: return "witness_v0_keyhash"; + case TX_WITNESS_V0_SCRIPTHASH: return "witness_v0_scripthash"; } return NULL; } @@ -66,6 +68,22 @@ bool Solver(const CScript& scriptPubKey, txnouttype& typeRet, vector witnessprogram; + if (scriptPubKey.IsWitnessProgram(witnessversion, witnessprogram)) { + if (witnessversion == 0 && witnessprogram.size() == 20) { + typeRet = TX_WITNESS_V0_KEYHASH; + vSolutionsRet.push_back(witnessprogram); + return true; + } + if (witnessversion == 0 && witnessprogram.size() == 32) { + typeRet = TX_WITNESS_V0_SCRIPTHASH; + vSolutionsRet.push_back(witnessprogram); + return true; + } + return false; + } + // Provably prunable, data-carrying output // // So long as script passes the IsUnspendable() test and all but the first @@ -282,3 +300,26 @@ CScript GetScriptForMultisig(int nRequired, const std::vector& keys) script << CScript::EncodeOP_N(keys.size()) << OP_CHECKMULTISIG; return script; } + +CScript GetScriptForWitness(const CScript& redeemscript) +{ + CScript ret; + + txnouttype typ; + std::vector > vSolutions; + if (Solver(redeemscript, typ, vSolutions)) { + if (typ == TX_PUBKEY) { + unsigned char h160[20]; + CHash160().Write(&vSolutions[0][0], vSolutions[0].size()).Finalize(h160); + ret << OP_0 << std::vector(&h160[0], &h160[20]); + return ret; + } else if (typ == TX_PUBKEYHASH) { + ret << OP_0 << vSolutions[0]; + return ret; + } + } + uint256 hash; + CSHA256().Write(&redeemscript[0], redeemscript.size()).Finalize(hash.begin()); + ret << OP_0 << ToByteVector(hash); + return ret; +} diff --git a/src/script/standard.h b/src/script/standard.h index f348da8e1..72aaea0b7 100644 --- a/src/script/standard.h +++ b/src/script/standard.h @@ -51,6 +51,8 @@ enum txnouttype TX_SCRIPTHASH, TX_MULTISIG, TX_NULL_DATA, + TX_WITNESS_V0_SCRIPTHASH, + TX_WITNESS_V0_KEYHASH, }; class CNoDestination { @@ -77,5 +79,6 @@ bool ExtractDestinations(const CScript& scriptPubKey, txnouttype& typeRet, std:: CScript GetScriptForDestination(const CTxDestination& dest); CScript GetScriptForRawPubKey(const CPubKey& pubkey); CScript GetScriptForMultisig(int nRequired, const std::vector& keys); +CScript GetScriptForWitness(const CScript& redeemscript); #endif // BITCOIN_SCRIPT_STANDARD_H -- cgit v1.2.3 From 745eb678ef5d52c74edcd9c322ebd1f3232a9310 Mon Sep 17 00:00:00 2001 From: NicolasDorier Date: Sun, 10 Apr 2016 15:59:23 +0900 Subject: [RPC] signrawtransaction can sign P2WSH --- src/script/script.cpp | 8 ++++++++ src/script/script.h | 1 + 2 files changed, 9 insertions(+) (limited to 'src/script') diff --git a/src/script/script.cpp b/src/script/script.cpp index 73f5a61bf..da551c23e 100644 --- a/src/script/script.cpp +++ b/src/script/script.cpp @@ -210,6 +210,14 @@ bool CScript::IsPayToScriptHash() const (*this)[22] == OP_EQUAL); } +bool CScript::IsPayToWitnessScriptHash() const +{ + // Extra-fast test for pay-to-witness-script-hash CScripts: + return (this->size() == 34 && + (*this)[0] == OP_0 && + (*this)[1] == 0x20); +} + // A witness program is any valid CScript that consists of a 1-byte push opcode // followed by a data push between 2 and 40 bytes. bool CScript::IsWitnessProgram(int& version, std::vector& program) const diff --git a/src/script/script.h b/src/script/script.h index b9b5be901..71af3754b 100644 --- a/src/script/script.h +++ b/src/script/script.h @@ -621,6 +621,7 @@ public: unsigned int GetSigOpCount(const CScript& scriptSig) const; bool IsPayToScriptHash() const; + bool IsPayToWitnessScriptHash() const; bool IsWitnessProgram(int& version, std::vector& program) const; /** Called by IsStandardTx and P2SH/BIP62 VerifyScript (which makes it consensus-critical). */ -- cgit v1.2.3 From 14d01309bed59afb08651f2b701ff90371b15b20 Mon Sep 17 00:00:00 2001 From: BtcDrak Date: Tue, 16 Feb 2016 18:29:53 +0000 Subject: Rename OP_NOP3 to OP_CHECKSEQUENCEVERIFY --- src/script/script.cpp | 2 +- src/script/script.h | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'src/script') diff --git a/src/script/script.cpp b/src/script/script.cpp index da551c23e..ddf677556 100644 --- a/src/script/script.cpp +++ b/src/script/script.cpp @@ -132,7 +132,7 @@ const char* GetOpName(opcodetype opcode) // expanson case OP_NOP1 : return "OP_NOP1"; case OP_CHECKLOCKTIMEVERIFY : return "OP_CHECKLOCKTIMEVERIFY"; - case OP_NOP3 : return "OP_NOP3"; + case OP_CHECKSEQUENCEVERIFY : return "OP_CHECKSEQUENCEVERIFY"; case OP_NOP4 : return "OP_NOP4"; case OP_NOP5 : return "OP_NOP5"; case OP_NOP6 : return "OP_NOP6"; diff --git a/src/script/script.h b/src/script/script.h index 71af3754b..278774d32 100644 --- a/src/script/script.h +++ b/src/script/script.h @@ -167,8 +167,8 @@ enum opcodetype OP_NOP1 = 0xb0, OP_CHECKLOCKTIMEVERIFY = 0xb1, OP_NOP2 = OP_CHECKLOCKTIMEVERIFY, - OP_NOP3 = 0xb2, - OP_CHECKSEQUENCEVERIFY = OP_NOP3, + OP_CHECKSEQUENCEVERIFY = 0xb2, + OP_NOP3 = OP_CHECKSEQUENCEVERIFY, OP_NOP4 = 0xb3, OP_NOP5 = 0xb4, OP_NOP6 = 0xb5, -- cgit v1.2.3 From d12b732ac287a1ed7543481b79801c9afc333b7f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jorge=20Tim=C3=B3n?= Date: Wed, 27 Jul 2016 22:42:13 +0200 Subject: libconsensus: Expose a flag for BIP112 We added the segwit one, but we forgot CHECKSEQUENCEVERIFY --- src/script/bitcoinconsensus.h | 1 + 1 file changed, 1 insertion(+) (limited to 'src/script') diff --git a/src/script/bitcoinconsensus.h b/src/script/bitcoinconsensus.h index 6f868d0d6..f73a8e30b 100644 --- a/src/script/bitcoinconsensus.h +++ b/src/script/bitcoinconsensus.h @@ -51,6 +51,7 @@ enum bitcoinconsensus_SCRIPT_FLAGS_VERIFY_P2SH = (1U << 0), // evaluate P2SH (BIP16) subscripts bitcoinconsensus_SCRIPT_FLAGS_VERIFY_DERSIG = (1U << 2), // enforce strict DER (BIP66) compliance bitcoinconsensus_SCRIPT_FLAGS_VERIFY_CHECKLOCKTIMEVERIFY = (1U << 9), // enable CHECKLOCKTIMEVERIFY (BIP65) + bitcoinconsensus_SCRIPT_FLAGS_VERIFY_CHECKSEQUENCEVERIFY = (1U << 10), // enable CHECKSEQUENCEVERIFY (BIP112) bitcoinconsensus_SCRIPT_FLAGS_VERIFY_WITNESS = (1U << 11), // enable WITNESS (BIP141) }; -- cgit v1.2.3 From d2c5d044d00ec805957ab246a7863d83ca075805 Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Tue, 16 Aug 2016 15:35:45 +0200 Subject: Precompute sighashes Original version by Nicolas Dorier. Precomputing version by Pieter Wuille. --- src/script/bitcoinconsensus.cpp | 4 +-- src/script/interpreter.cpp | 54 ++++++++++++++++++++++++++++------------- src/script/interpreter.h | 13 ++++++++-- src/script/sigcache.h | 2 +- 4 files changed, 51 insertions(+), 22 deletions(-) (limited to 'src/script') diff --git a/src/script/bitcoinconsensus.cpp b/src/script/bitcoinconsensus.cpp index 62fd9031f..01a8babb6 100644 --- a/src/script/bitcoinconsensus.cpp +++ b/src/script/bitcoinconsensus.cpp @@ -84,8 +84,8 @@ static int verify_script(const unsigned char *scriptPubKey, unsigned int scriptP // Regardless of the verification result, the tx did not error. set_error(err, bitcoinconsensus_ERR_OK); - - return VerifyScript(tx.vin[nIn].scriptSig, CScript(scriptPubKey, scriptPubKey + scriptPubKeyLen), nIn < tx.wit.vtxinwit.size() ? &tx.wit.vtxinwit[nIn].scriptWitness : NULL, flags, TransactionSignatureChecker(&tx, nIn, amount), NULL); + CachedHashes cachedHashes(tx); + return VerifyScript(tx.vin[nIn].scriptSig, CScript(scriptPubKey, scriptPubKey + scriptPubKeyLen), nIn < tx.wit.vtxinwit.size() ? &tx.wit.vtxinwit[nIn].scriptWitness : NULL, flags, TransactionSignatureChecker(&tx, nIn, amount, cachedHashes), NULL); } catch (const std::exception&) { return set_error(err, bitcoinconsensus_ERR_TX_DESERIALIZE); // Error deserializing } diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp index bc027e9f0..459feaa0f 100644 --- a/src/script/interpreter.cpp +++ b/src/script/interpreter.cpp @@ -1108,9 +1108,40 @@ public: } }; +uint256 GetPrevoutHash(const CTransaction& txTo) { + CHashWriter ss(SER_GETHASH, 0); + for (unsigned int n = 0; n < txTo.vin.size(); n++) { + ss << txTo.vin[n].prevout; + } + return ss.GetHash(); +} + +uint256 GetSequenceHash(const CTransaction& txTo) { + CHashWriter ss(SER_GETHASH, 0); + for (unsigned int n = 0; n < txTo.vin.size(); n++) { + ss << txTo.vin[n].nSequence; + } + return ss.GetHash(); +} + +uint256 GetOutputsHash(const CTransaction& txTo) { + CHashWriter ss(SER_GETHASH, 0); + for (unsigned int n = 0; n < txTo.vout.size(); n++) { + ss << txTo.vout[n]; + } + return ss.GetHash(); +} + } // anon namespace -uint256 SignatureHash(const CScript& scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType, const CAmount& amount, SigVersion sigversion) +CachedHashes::CachedHashes(const CTransaction& txTo) +{ + hashPrevouts = GetPrevoutHash(txTo); + hashSequence = GetSequenceHash(txTo); + hashOutputs = GetOutputsHash(txTo); +} + +uint256 SignatureHash(const CScript& scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType, const CAmount& amount, SigVersion sigversion, const CachedHashes* cache) { if (sigversion == SIGVERSION_WITNESS_V0) { uint256 hashPrevouts; @@ -1118,27 +1149,16 @@ uint256 SignatureHash(const CScript& scriptCode, const CTransaction& txTo, unsig uint256 hashOutputs; if (!(nHashType & SIGHASH_ANYONECANPAY)) { - CHashWriter ss(SER_GETHASH, 0); - for (unsigned int n = 0; n < txTo.vin.size(); n++) { - ss << txTo.vin[n].prevout; - } - hashPrevouts = ss.GetHash(); // TODO: cache this value for all signatures in a transaction + hashPrevouts = cache ? cache->hashPrevouts : GetPrevoutHash(txTo); } if (!(nHashType & SIGHASH_ANYONECANPAY) && (nHashType & 0x1f) != SIGHASH_SINGLE && (nHashType & 0x1f) != SIGHASH_NONE) { - CHashWriter ss(SER_GETHASH, 0); - for (unsigned int n = 0; n < txTo.vin.size(); n++) { - ss << txTo.vin[n].nSequence; - } - hashSequence = ss.GetHash(); // TODO: cache this value for all signatures in a transaction + hashSequence = cache ? cache->hashSequence : GetSequenceHash(txTo); } + if ((nHashType & 0x1f) != SIGHASH_SINGLE && (nHashType & 0x1f) != SIGHASH_NONE) { - CHashWriter ss(SER_GETHASH, 0); - for (unsigned int n = 0; n < txTo.vout.size(); n++) { - ss << txTo.vout[n]; - } - hashOutputs = ss.GetHash(); // TODO: cache this value for all signatures in a transaction + hashOutputs = cache ? cache->hashOutputs : GetOutputsHash(txTo); } else if ((nHashType & 0x1f) == SIGHASH_SINGLE && nIn < txTo.vout.size()) { CHashWriter ss(SER_GETHASH, 0); ss << txTo.vout[nIn]; @@ -1209,7 +1229,7 @@ bool TransactionSignatureChecker::CheckSig(const vector& vchSigIn int nHashType = vchSig.back(); vchSig.pop_back(); - uint256 sighash = SignatureHash(scriptCode, *txTo, nIn, nHashType, amount, sigversion); + uint256 sighash = SignatureHash(scriptCode, *txTo, nIn, nHashType, amount, sigversion, this->cachedHashes); if (!VerifySignature(vchSig, pubkey, sighash)) return false; diff --git a/src/script/interpreter.h b/src/script/interpreter.h index bd2f21166..b4da113ee 100644 --- a/src/script/interpreter.h +++ b/src/script/interpreter.h @@ -98,13 +98,20 @@ enum bool CheckSignatureEncoding(const std::vector &vchSig, unsigned int flags, ScriptError* serror); +struct CachedHashes +{ + uint256 hashPrevouts, hashSequence, hashOutputs; + + CachedHashes(const CTransaction& tx); +}; + enum SigVersion { SIGVERSION_BASE = 0, SIGVERSION_WITNESS_V0 = 1, }; -uint256 SignatureHash(const CScript &scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType, const CAmount& amount, SigVersion sigversion); +uint256 SignatureHash(const CScript &scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType, const CAmount& amount, SigVersion sigversion, const CachedHashes* cache = NULL); class BaseSignatureChecker { @@ -133,12 +140,14 @@ private: const CTransaction* txTo; unsigned int nIn; const CAmount amount; + const CachedHashes* cachedHashes; protected: virtual bool VerifySignature(const std::vector& 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) {} + TransactionSignatureChecker(const CTransaction* txToIn, unsigned int nInIn, const CAmount& amountIn) : txTo(txToIn), nIn(nInIn), amount(amountIn), cachedHashes(NULL) {} + TransactionSignatureChecker(const CTransaction* txToIn, unsigned int nInIn, const CAmount& amountIn, const CachedHashes& cachedHashesIn) : txTo(txToIn), nIn(nInIn), amount(amountIn), cachedHashes(&cachedHashesIn) {} bool CheckSig(const std::vector& scriptSig, const std::vector& vchPubKey, const CScript& scriptCode, SigVersion sigversion) const; bool CheckLockTime(const CScriptNum& nLockTime) const; bool CheckSequence(const CScriptNum& nSequence) const; diff --git a/src/script/sigcache.h b/src/script/sigcache.h index 050bf8cc4..551f8b225 100644 --- a/src/script/sigcache.h +++ b/src/script/sigcache.h @@ -22,7 +22,7 @@ private: bool store; public: - CachingTransactionSignatureChecker(const CTransaction* txToIn, unsigned int nInIn, const CAmount& amount, bool storeIn) : TransactionSignatureChecker(txToIn, nInIn, amount), store(storeIn) {} + CachingTransactionSignatureChecker(const CTransaction* txToIn, unsigned int nInIn, const CAmount& amount, bool storeIn, CachedHashes& cachedHashesIn) : TransactionSignatureChecker(txToIn, nInIn, amount, cachedHashesIn), store(storeIn) {} bool VerifySignature(const std::vector& vchSig, const CPubKey& vchPubKey, const uint256& sighash) const; }; -- cgit v1.2.3 From fa27c0a2c4545a579bf339e816c3fa785252b7dc Mon Sep 17 00:00:00 2001 From: MarcoFalke Date: Wed, 29 Jun 2016 11:48:51 +0200 Subject: [doc] Fix typos in comments, doxygen: Fix comment syntax --- src/script/sign.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/script') diff --git a/src/script/sign.h b/src/script/sign.h index 6404b4523..f9aa6fca2 100644 --- a/src/script/sign.h +++ b/src/script/sign.h @@ -51,7 +51,7 @@ public: MutableTransactionSignatureCreator(const CKeyStore* keystoreIn, const CMutableTransaction* txToIn, unsigned int nInIn, const CAmount& amount, int nHashTypeIn) : TransactionSignatureCreator(keystoreIn, &tx, nInIn, amount, nHashTypeIn), tx(*txToIn) {} }; -/** A signature creator that just produces 72-byte empty signatyres. */ +/** A signature creator that just produces 72-byte empty signatures. */ class DummySignatureCreator : public BaseSignatureCreator { public: DummySignatureCreator(const CKeyStore* keystoreIn) : BaseSignatureCreator(keystoreIn) {} -- cgit v1.2.3 From 35fe0393f216aa6020fc929272118eade5628636 Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Fri, 26 Aug 2016 18:38:20 +0200 Subject: Rename to PrecomputedTransactionData --- src/script/bitcoinconsensus.cpp | 4 ++-- src/script/interpreter.cpp | 6 +++--- src/script/interpreter.h | 12 ++++++------ src/script/sigcache.h | 2 +- 4 files changed, 12 insertions(+), 12 deletions(-) (limited to 'src/script') diff --git a/src/script/bitcoinconsensus.cpp b/src/script/bitcoinconsensus.cpp index 01a8babb6..b629f4278 100644 --- a/src/script/bitcoinconsensus.cpp +++ b/src/script/bitcoinconsensus.cpp @@ -84,8 +84,8 @@ static int verify_script(const unsigned char *scriptPubKey, unsigned int scriptP // Regardless of the verification result, the tx did not error. set_error(err, bitcoinconsensus_ERR_OK); - CachedHashes cachedHashes(tx); - return VerifyScript(tx.vin[nIn].scriptSig, CScript(scriptPubKey, scriptPubKey + scriptPubKeyLen), nIn < tx.wit.vtxinwit.size() ? &tx.wit.vtxinwit[nIn].scriptWitness : NULL, flags, TransactionSignatureChecker(&tx, nIn, amount, cachedHashes), NULL); + PrecomputedTransactionData txdata(tx); + return VerifyScript(tx.vin[nIn].scriptSig, CScript(scriptPubKey, scriptPubKey + scriptPubKeyLen), nIn < tx.wit.vtxinwit.size() ? &tx.wit.vtxinwit[nIn].scriptWitness : NULL, flags, TransactionSignatureChecker(&tx, nIn, amount, txdata), NULL); } catch (const std::exception&) { return set_error(err, bitcoinconsensus_ERR_TX_DESERIALIZE); // Error deserializing } diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp index 459feaa0f..47ea261e3 100644 --- a/src/script/interpreter.cpp +++ b/src/script/interpreter.cpp @@ -1134,14 +1134,14 @@ uint256 GetOutputsHash(const CTransaction& txTo) { } // anon namespace -CachedHashes::CachedHashes(const CTransaction& txTo) +PrecomputedTransactionData::PrecomputedTransactionData(const CTransaction& txTo) { hashPrevouts = GetPrevoutHash(txTo); hashSequence = GetSequenceHash(txTo); hashOutputs = GetOutputsHash(txTo); } -uint256 SignatureHash(const CScript& scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType, const CAmount& amount, SigVersion sigversion, const CachedHashes* cache) +uint256 SignatureHash(const CScript& scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType, const CAmount& amount, SigVersion sigversion, const PrecomputedTransactionData* cache) { if (sigversion == SIGVERSION_WITNESS_V0) { uint256 hashPrevouts; @@ -1229,7 +1229,7 @@ bool TransactionSignatureChecker::CheckSig(const vector& vchSigIn int nHashType = vchSig.back(); vchSig.pop_back(); - uint256 sighash = SignatureHash(scriptCode, *txTo, nIn, nHashType, amount, sigversion, this->cachedHashes); + uint256 sighash = SignatureHash(scriptCode, *txTo, nIn, nHashType, amount, sigversion, this->txdata); if (!VerifySignature(vchSig, pubkey, sighash)) return false; diff --git a/src/script/interpreter.h b/src/script/interpreter.h index b4da113ee..e5d7865cd 100644 --- a/src/script/interpreter.h +++ b/src/script/interpreter.h @@ -98,11 +98,11 @@ enum bool CheckSignatureEncoding(const std::vector &vchSig, unsigned int flags, ScriptError* serror); -struct CachedHashes +struct PrecomputedTransactionData { uint256 hashPrevouts, hashSequence, hashOutputs; - CachedHashes(const CTransaction& tx); + PrecomputedTransactionData(const CTransaction& tx); }; enum SigVersion @@ -111,7 +111,7 @@ enum SigVersion SIGVERSION_WITNESS_V0 = 1, }; -uint256 SignatureHash(const CScript &scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType, const CAmount& amount, SigVersion sigversion, const CachedHashes* cache = NULL); +uint256 SignatureHash(const CScript &scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType, const CAmount& amount, SigVersion sigversion, const PrecomputedTransactionData* cache = NULL); class BaseSignatureChecker { @@ -140,14 +140,14 @@ private: const CTransaction* txTo; unsigned int nIn; const CAmount amount; - const CachedHashes* cachedHashes; + const PrecomputedTransactionData* txdata; protected: virtual bool VerifySignature(const std::vector& 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), cachedHashes(NULL) {} - TransactionSignatureChecker(const CTransaction* txToIn, unsigned int nInIn, const CAmount& amountIn, const CachedHashes& cachedHashesIn) : txTo(txToIn), nIn(nInIn), amount(amountIn), cachedHashes(&cachedHashesIn) {} + TransactionSignatureChecker(const CTransaction* txToIn, unsigned int nInIn, const CAmount& amountIn) : txTo(txToIn), nIn(nInIn), amount(amountIn), txdata(NULL) {} + TransactionSignatureChecker(const CTransaction* txToIn, unsigned int nInIn, const CAmount& amountIn, const PrecomputedTransactionData& txdataIn) : txTo(txToIn), nIn(nInIn), amount(amountIn), txdata(&txdataIn) {} bool CheckSig(const std::vector& scriptSig, const std::vector& vchPubKey, const CScript& scriptCode, SigVersion sigversion) const; bool CheckLockTime(const CScriptNum& nLockTime) const; bool CheckSequence(const CScriptNum& nSequence) const; diff --git a/src/script/sigcache.h b/src/script/sigcache.h index 551f8b225..44551ec2b 100644 --- a/src/script/sigcache.h +++ b/src/script/sigcache.h @@ -22,7 +22,7 @@ private: bool store; public: - CachingTransactionSignatureChecker(const CTransaction* txToIn, unsigned int nInIn, const CAmount& amount, bool storeIn, CachedHashes& cachedHashesIn) : TransactionSignatureChecker(txToIn, nInIn, amount, cachedHashesIn), store(storeIn) {} + CachingTransactionSignatureChecker(const CTransaction* txToIn, unsigned int nInIn, const CAmount& amount, bool storeIn, PrecomputedTransactionData& txdataIn) : TransactionSignatureChecker(txToIn, nInIn, amount, txdataIn), store(storeIn) {} bool VerifySignature(const std::vector& vchSig, const CPubKey& vchPubKey, const uint256& sighash) const; }; -- cgit v1.2.3 From c72c5b1e3bd42e84465677e94aa83316ff3d9a14 Mon Sep 17 00:00:00 2001 From: Johnson Lau Date: Fri, 23 Sep 2016 13:06:45 +0800 Subject: Make non-minimal OP_IF/NOTIF argument non-standard for P2WSH --- src/script/interpreter.cpp | 6 ++++++ src/script/interpreter.h | 4 ++++ src/script/script_error.cpp | 2 ++ src/script/script_error.h | 3 ++- 4 files changed, 14 insertions(+), 1 deletion(-) (limited to 'src/script') diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp index 47ea261e3..fd356fed0 100644 --- a/src/script/interpreter.cpp +++ b/src/script/interpreter.cpp @@ -428,6 +428,12 @@ bool EvalScript(vector >& stack, const CScript& script, un if (stack.size() < 1) return set_error(serror, SCRIPT_ERR_UNBALANCED_CONDITIONAL); valtype& vch = stacktop(-1); + if (sigversion == SIGVERSION_WITNESS_V0 && (flags & SCRIPT_VERIFY_MINIMALIF)) { + if (vch.size() > 1) + return set_error(serror, SCRIPT_ERR_MINIMALIF); + if (vch.size() == 1 && vch[0] != 1) + return set_error(serror, SCRIPT_ERR_MINIMALIF); + } fValue = CastToBool(vch); if (opcode == OP_NOTIF) fValue = !fValue; diff --git a/src/script/interpreter.h b/src/script/interpreter.h index e5d7865cd..2ce4b23e5 100644 --- a/src/script/interpreter.h +++ b/src/script/interpreter.h @@ -94,6 +94,10 @@ enum // Making v1-v16 witness program non-standard // SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM = (1U << 12), + + // Segwit script only: Require the argument of OP_IF/NOTIF to be exactly 0x01 or empty vector + // + SCRIPT_VERIFY_MINIMALIF = (1U << 13), }; bool CheckSignatureEncoding(const std::vector &vchSig, unsigned int flags, ScriptError* serror); diff --git a/src/script/script_error.cpp b/src/script/script_error.cpp index cef807edc..9969c232f 100644 --- a/src/script/script_error.cpp +++ b/src/script/script_error.cpp @@ -63,6 +63,8 @@ const char* ScriptErrorString(const ScriptError serror) return "Non-canonical signature: S value is unnecessarily high"; case SCRIPT_ERR_SIG_NULLDUMMY: return "Dummy CHECKMULTISIG argument must be zero"; + case SCRIPT_ERR_MINIMALIF: + return "OP_IF/NOTIF argument must be minimal"; case SCRIPT_ERR_DISCOURAGE_UPGRADABLE_NOPS: return "NOPx reserved for soft-fork upgrades"; case SCRIPT_ERR_DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM: diff --git a/src/script/script_error.h b/src/script/script_error.h index 09dc6945a..6d34d3792 100644 --- a/src/script/script_error.h +++ b/src/script/script_error.h @@ -39,7 +39,7 @@ typedef enum ScriptError_t SCRIPT_ERR_NEGATIVE_LOCKTIME, SCRIPT_ERR_UNSATISFIED_LOCKTIME, - /* BIP62 */ + /* Malleability */ SCRIPT_ERR_SIG_HASHTYPE, SCRIPT_ERR_SIG_DER, SCRIPT_ERR_MINIMALDATA, @@ -48,6 +48,7 @@ typedef enum ScriptError_t SCRIPT_ERR_SIG_NULLDUMMY, SCRIPT_ERR_PUBKEYTYPE, SCRIPT_ERR_CLEANSTACK, + SCRIPT_ERR_MINIMALIF, /* softfork safeness */ SCRIPT_ERR_DISCOURAGE_UPGRADABLE_NOPS, -- cgit v1.2.3 From e41bd449ab2b8d01260795383af2c40b659d8587 Mon Sep 17 00:00:00 2001 From: Johnson Lau Date: Thu, 22 Sep 2016 15:06:54 +0800 Subject: Add policy: null signature for failed CHECK(MULTI)SIG --- src/script/interpreter.cpp | 14 +++++++++++++- src/script/interpreter.h | 4 ++++ src/script/script_error.cpp | 2 ++ src/script/script_error.h | 1 + 4 files changed, 20 insertions(+), 1 deletion(-) (limited to 'src/script') diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp index fd356fed0..41756ea71 100644 --- a/src/script/interpreter.cpp +++ b/src/script/interpreter.cpp @@ -885,6 +885,9 @@ bool EvalScript(vector >& stack, const CScript& script, un } bool fSuccess = checker.CheckSig(vchSig, vchPubKey, scriptCode, sigversion); + if (!fSuccess && (flags & SCRIPT_VERIFY_NULLFAIL) && vchSig.size()) + return set_error(serror, SCRIPT_ERR_SIG_NULLFAIL); + popstack(stack); popstack(stack); stack.push_back(fSuccess ? vchTrue : vchFalse); @@ -914,6 +917,9 @@ bool EvalScript(vector >& stack, const CScript& script, un if (nOpCount > MAX_OPS_PER_SCRIPT) return set_error(serror, SCRIPT_ERR_OP_COUNT); int ikey = ++i; + // ikey2 is the position of last non-signature item in the stack. Top stack item = 1. + // With SCRIPT_VERIFY_NULLFAIL, this is used for cleanup if operation fails. + int ikey2 = nKeysCount + 2; i += nKeysCount; if ((int)stack.size() < i) return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION); @@ -970,8 +976,14 @@ bool EvalScript(vector >& stack, const CScript& script, un } // Clean up stack of actual arguments - while (i-- > 1) + while (i-- > 1) { + // If the operation failed, we require that all signatures must be empty vector + if (!fSuccess && (flags & SCRIPT_VERIFY_NULLFAIL) && !ikey2 && stacktop(-1).size()) + return set_error(serror, SCRIPT_ERR_SIG_NULLFAIL); + if (ikey2 > 0) + ikey2--; popstack(stack); + } // A bug causes CHECKMULTISIG to consume one extra argument // whose contents were not checked in any way. diff --git a/src/script/interpreter.h b/src/script/interpreter.h index 2ce4b23e5..0adc9482f 100644 --- a/src/script/interpreter.h +++ b/src/script/interpreter.h @@ -98,6 +98,10 @@ enum // Segwit script only: Require the argument of OP_IF/NOTIF to be exactly 0x01 or empty vector // SCRIPT_VERIFY_MINIMALIF = (1U << 13), + + // Signature(s) must be empty vector if an CHECK(MULTI)SIG operation failed + // + SCRIPT_VERIFY_NULLFAIL = (1U << 14), }; bool CheckSignatureEncoding(const std::vector &vchSig, unsigned int flags, ScriptError* serror); diff --git a/src/script/script_error.cpp b/src/script/script_error.cpp index 9969c232f..e27b715c2 100644 --- a/src/script/script_error.cpp +++ b/src/script/script_error.cpp @@ -65,6 +65,8 @@ const char* ScriptErrorString(const ScriptError serror) return "Dummy CHECKMULTISIG argument must be zero"; case SCRIPT_ERR_MINIMALIF: return "OP_IF/NOTIF argument must be minimal"; + case SCRIPT_ERR_SIG_NULLFAIL: + return "Signature must be zero for failed CHECK(MULTI)SIG operation"; case SCRIPT_ERR_DISCOURAGE_UPGRADABLE_NOPS: return "NOPx reserved for soft-fork upgrades"; case SCRIPT_ERR_DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM: diff --git a/src/script/script_error.h b/src/script/script_error.h index 6d34d3792..bccfdb99e 100644 --- a/src/script/script_error.h +++ b/src/script/script_error.h @@ -49,6 +49,7 @@ typedef enum ScriptError_t SCRIPT_ERR_PUBKEYTYPE, SCRIPT_ERR_CLEANSTACK, SCRIPT_ERR_MINIMALIF, + SCRIPT_ERR_SIG_NULLFAIL, /* softfork safeness */ SCRIPT_ERR_DISCOURAGE_UPGRADABLE_NOPS, -- cgit v1.2.3 From 2fa0063c26c80c719a1c0d30e548e338689ac917 Mon Sep 17 00:00:00 2001 From: Johnson Lau Date: Fri, 30 Sep 2016 23:08:29 +0800 Subject: Add NULLDUMMY verify flag in bitcoinconsensus.h --- src/script/bitcoinconsensus.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src/script') diff --git a/src/script/bitcoinconsensus.h b/src/script/bitcoinconsensus.h index f73a8e30b..1d2d5c23e 100644 --- a/src/script/bitcoinconsensus.h +++ b/src/script/bitcoinconsensus.h @@ -1,5 +1,5 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2015 The Bitcoin Core developers +// Copyright (c) 2009-2016 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -50,6 +50,7 @@ enum bitcoinconsensus_SCRIPT_FLAGS_VERIFY_NONE = 0, bitcoinconsensus_SCRIPT_FLAGS_VERIFY_P2SH = (1U << 0), // evaluate P2SH (BIP16) subscripts bitcoinconsensus_SCRIPT_FLAGS_VERIFY_DERSIG = (1U << 2), // enforce strict DER (BIP66) compliance + bitcoinconsensus_SCRIPT_FLAGS_VERIFY_NULLDUMMY = (1U << 4), // enforce NULLDUMMY (BIP147) bitcoinconsensus_SCRIPT_FLAGS_VERIFY_CHECKLOCKTIMEVERIFY = (1U << 9), // enable CHECKLOCKTIMEVERIFY (BIP65) bitcoinconsensus_SCRIPT_FLAGS_VERIFY_CHECKSEQUENCEVERIFY = (1U << 10), // enable CHECKSEQUENCEVERIFY (BIP112) bitcoinconsensus_SCRIPT_FLAGS_VERIFY_WITNESS = (1U << 11), // enable WITNESS (BIP141) -- cgit v1.2.3 From acf853df910339412bafb1743f42af1774f5b910 Mon Sep 17 00:00:00 2001 From: Johnson Lau Date: Sat, 15 Oct 2016 23:45:07 +0800 Subject: Add script tests for FindAndDelete in pre-segwit and segwit scripts --- src/script/interpreter.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/script') diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp index 41756ea71..e0f2f8919 100644 --- a/src/script/interpreter.cpp +++ b/src/script/interpreter.cpp @@ -874,7 +874,7 @@ bool EvalScript(vector >& stack, const CScript& script, un // Subset of script starting at the most recent codeseparator CScript scriptCode(pbegincodehash, pend); - // Drop the signature, since there's no way for a signature to sign itself + // Drop the signature in pre-segwit scripts but not segwit scripts if (sigversion == SIGVERSION_BASE) { scriptCode.FindAndDelete(CScript(vchSig)); } @@ -935,7 +935,7 @@ bool EvalScript(vector >& stack, const CScript& script, un // Subset of script starting at the most recent codeseparator CScript scriptCode(pbegincodehash, pend); - // Drop the signatures, since there's no way for a signature to sign itself + // Drop the signature in pre-segwit scripts but not segwit scripts for (int k = 0; k < nSigsCount; k++) { valtype& vchSig = stacktop(-isig-k); -- cgit v1.2.3 From 4c0c25a604cec39675187507df423cff5d707ab5 Mon Sep 17 00:00:00 2001 From: Johnson Lau Date: Sun, 2 Oct 2016 01:19:33 +0800 Subject: Require compressed keys in segwit as policy and disable signing with uncompressed keys for segwit scripts --- src/script/interpreter.cpp | 28 ++++++++++++++++++++++------ src/script/interpreter.h | 6 +++++- src/script/script_error.cpp | 2 ++ src/script/script_error.h | 1 + src/script/sign.cpp | 6 +++++- 5 files changed, 35 insertions(+), 8 deletions(-) (limited to 'src/script') diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp index 41756ea71..836cf9ee3 100644 --- a/src/script/interpreter.cpp +++ b/src/script/interpreter.cpp @@ -79,8 +79,20 @@ bool static IsCompressedOrUncompressedPubKey(const valtype &vchPubKey) { return false; } } else { - // Non-canonical public key: neither compressed nor uncompressed - return false; + // Non-canonical public key: neither compressed nor uncompressed + return false; + } + return true; +} + +bool static IsCompressedPubKey(const valtype &vchPubKey) { + if (vchPubKey.size() != 33) { + // Non-canonical public key: invalid length for compressed key + return false; + } + if (vchPubKey[0] != 0x02 && vchPubKey[0] != 0x03) { + // Non-canonical public key: invalid prefix for compressed key + return false; } return true; } @@ -199,10 +211,14 @@ bool CheckSignatureEncoding(const vector &vchSig, unsigned int fl return true; } -bool static CheckPubKeyEncoding(const valtype &vchSig, unsigned int flags, ScriptError* serror) { - if ((flags & SCRIPT_VERIFY_STRICTENC) != 0 && !IsCompressedOrUncompressedPubKey(vchSig)) { +bool static CheckPubKeyEncoding(const valtype &vchPubKey, unsigned int flags, const SigVersion &sigversion, ScriptError* serror) { + if ((flags & SCRIPT_VERIFY_STRICTENC) != 0 && !IsCompressedOrUncompressedPubKey(vchPubKey)) { return set_error(serror, SCRIPT_ERR_PUBKEYTYPE); } + // Only compressed keys are accepted in segwit + if ((flags & SCRIPT_VERIFY_WITNESS_PUBKEYTYPE) != 0 && sigversion == SIGVERSION_WITNESS_V0 && !IsCompressedPubKey(vchPubKey)) { + return set_error(serror, SCRIPT_ERR_WITNESS_PUBKEYTYPE); + } return true; } @@ -879,7 +895,7 @@ bool EvalScript(vector >& stack, const CScript& script, un scriptCode.FindAndDelete(CScript(vchSig)); } - if (!CheckSignatureEncoding(vchSig, flags, serror) || !CheckPubKeyEncoding(vchPubKey, flags, serror)) { + if (!CheckSignatureEncoding(vchSig, flags, serror) || !CheckPubKeyEncoding(vchPubKey, flags, sigversion, serror)) { //serror is set return false; } @@ -953,7 +969,7 @@ bool EvalScript(vector >& stack, const CScript& script, un // Note how this makes the exact order of pubkey/signature evaluation // distinguishable by CHECKMULTISIG NOT if the STRICTENC flag is set. // See the script_(in)valid tests for details. - if (!CheckSignatureEncoding(vchSig, flags, serror) || !CheckPubKeyEncoding(vchPubKey, flags, serror)) { + if (!CheckSignatureEncoding(vchSig, flags, serror) || !CheckPubKeyEncoding(vchPubKey, flags, sigversion, serror)) { // serror is set return false; } diff --git a/src/script/interpreter.h b/src/script/interpreter.h index 0adc9482f..79894c530 100644 --- a/src/script/interpreter.h +++ b/src/script/interpreter.h @@ -1,5 +1,5 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2015 The Bitcoin Core developers +// Copyright (c) 2009-2016 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -102,6 +102,10 @@ enum // Signature(s) must be empty vector if an CHECK(MULTI)SIG operation failed // SCRIPT_VERIFY_NULLFAIL = (1U << 14), + + // Public keys in segregated witness scripts must be compressed + // + SCRIPT_VERIFY_WITNESS_PUBKEYTYPE = (1U << 15), }; bool CheckSignatureEncoding(const std::vector &vchSig, unsigned int flags, ScriptError* serror); diff --git a/src/script/script_error.cpp b/src/script/script_error.cpp index e27b715c2..2c5359fe8 100644 --- a/src/script/script_error.cpp +++ b/src/script/script_error.cpp @@ -85,6 +85,8 @@ const char* ScriptErrorString(const ScriptError serror) return "Witness requires only-redeemscript scriptSig"; case SCRIPT_ERR_WITNESS_UNEXPECTED: return "Witness provided for non-witness script"; + case SCRIPT_ERR_WITNESS_PUBKEYTYPE: + return "Using non-compressed keys in segwit"; 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 bccfdb99e..430836991 100644 --- a/src/script/script_error.h +++ b/src/script/script_error.h @@ -62,6 +62,7 @@ typedef enum ScriptError_t SCRIPT_ERR_WITNESS_MALLEATED, SCRIPT_ERR_WITNESS_MALLEATED_P2SH, SCRIPT_ERR_WITNESS_UNEXPECTED, + SCRIPT_ERR_WITNESS_PUBKEYTYPE, SCRIPT_ERR_ERROR_COUNT } ScriptError; diff --git a/src/script/sign.cpp b/src/script/sign.cpp index 87f38d9c7..f552ad5bb 100644 --- a/src/script/sign.cpp +++ b/src/script/sign.cpp @@ -1,5 +1,5 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2015 The Bitcoin Core developers +// Copyright (c) 2009-2016 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -26,6 +26,10 @@ bool TransactionSignatureCreator::CreateSig(std::vector& vchSig, if (!keystore->GetKey(address, key)) return false; + // Signing with uncompressed keys is disabled in witness scripts + if (sigversion == SIGVERSION_WITNESS_V0 && !key.IsCompressed()) + return false; + uint256 hash = SignatureHash(scriptCode, *txTo, nIn, nHashType, amount, sigversion); if (!key.Sign(hash, vchSig)) return false; -- cgit v1.2.3 From 248f3a76a825a332e5495c5947ad283a9e5e938f Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Mon, 17 Oct 2016 00:32:36 +0800 Subject: Fix ismine and addwitnessaddress: no uncompressed keys in segwit --- src/script/ismine.cpp | 62 ++++++++++++++++++++++++++++++++++++++++++++------- src/script/ismine.h | 11 +++++++-- 2 files changed, 63 insertions(+), 10 deletions(-) (limited to 'src/script') diff --git a/src/script/ismine.cpp b/src/script/ismine.cpp index 0bf180341..7467d23b2 100644 --- a/src/script/ismine.cpp +++ b/src/script/ismine.cpp @@ -29,13 +29,25 @@ unsigned int HaveKeys(const vector& pubkeys, const CKeyStore& keystore) return nResult; } -isminetype IsMine(const CKeyStore &keystore, const CTxDestination& dest) +isminetype IsMine(const CKeyStore& keystore, const CScript& scriptPubKey, SigVersion sigversion) +{ + bool isInvalid = false; + return IsMine(keystore, scriptPubKey, isInvalid, sigversion); +} + +isminetype IsMine(const CKeyStore& keystore, const CTxDestination& dest, SigVersion sigversion) +{ + bool isInvalid = false; + return IsMine(keystore, dest, isInvalid, sigversion); +} + +isminetype IsMine(const CKeyStore &keystore, const CTxDestination& dest, bool& isInvalid, SigVersion sigversion) { CScript script = GetScriptForDestination(dest); - return IsMine(keystore, script); + return IsMine(keystore, script, isInvalid, sigversion); } -isminetype IsMine(const CKeyStore &keystore, const CScript& scriptPubKey) +isminetype IsMine(const CKeyStore &keystore, const CScript& scriptPubKey, bool& isInvalid, SigVersion sigversion) { vector vSolutions; txnouttype whichType; @@ -53,12 +65,35 @@ isminetype IsMine(const CKeyStore &keystore, const CScript& scriptPubKey) break; case TX_PUBKEY: keyID = CPubKey(vSolutions[0]).GetID(); + if (sigversion != SIGVERSION_BASE && vSolutions[0].size() != 33) { + isInvalid = true; + return ISMINE_NO; + } if (keystore.HaveKey(keyID)) return ISMINE_SPENDABLE; break; - case TX_PUBKEYHASH: case TX_WITNESS_V0_KEYHASH: + { + if (!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 = ::IsMine(keystore, GetScriptForDestination(CKeyID(uint160(vSolutions[0]))), isInvalid, SIGVERSION_WITNESS_V0); + if (ret == ISMINE_SPENDABLE || ret == ISMINE_WATCH_SOLVABLE || (ret == ISMINE_NO && isInvalid)) + return ret; + break; + } + case TX_PUBKEYHASH: keyID = CKeyID(uint160(vSolutions[0])); + if (sigversion != SIGVERSION_BASE) { + CPubKey pubkey; + if (keystore.GetPubKey(keyID, pubkey) && !pubkey.IsCompressed()) { + isInvalid = true; + return ISMINE_NO; + } + } if (keystore.HaveKey(keyID)) return ISMINE_SPENDABLE; break; @@ -67,21 +102,24 @@ isminetype IsMine(const CKeyStore &keystore, const CScript& scriptPubKey) CScriptID scriptID = CScriptID(uint160(vSolutions[0])); CScript subscript; if (keystore.GetCScript(scriptID, subscript)) { - isminetype ret = IsMine(keystore, subscript); - if (ret == ISMINE_SPENDABLE) + isminetype ret = IsMine(keystore, subscript, isInvalid); + if (ret == ISMINE_SPENDABLE || ret == ISMINE_WATCH_SOLVABLE || (ret == ISMINE_NO && isInvalid)) return ret; } break; } case TX_WITNESS_V0_SCRIPTHASH: { + if (!keystore.HaveCScript(CScriptID(CScript() << OP_0 << vSolutions[0]))) { + break; + } uint160 hash; CRIPEMD160().Write(&vSolutions[0][0], vSolutions[0].size()).Finalize(hash.begin()); CScriptID scriptID = CScriptID(hash); CScript subscript; if (keystore.GetCScript(scriptID, subscript)) { - isminetype ret = IsMine(keystore, subscript); - if (ret == ISMINE_SPENDABLE) + isminetype ret = IsMine(keystore, subscript, isInvalid, SIGVERSION_WITNESS_V0); + if (ret == ISMINE_SPENDABLE || ret == ISMINE_WATCH_SOLVABLE || (ret == ISMINE_NO && isInvalid)) return ret; } break; @@ -95,6 +133,14 @@ isminetype IsMine(const CKeyStore &keystore, const CScript& scriptPubKey) // them) enable spend-out-from-under-you attacks, especially // in shared-wallet situations. vector keys(vSolutions.begin()+1, vSolutions.begin()+vSolutions.size()-1); + if (sigversion != SIGVERSION_BASE) { + for (size_t i = 0; i < keys.size(); i++) { + if (keys[i].size() != 33) { + isInvalid = true; + return ISMINE_NO; + } + } + } if (HaveKeys(keys, keystore) == keys.size()) return ISMINE_SPENDABLE; break; diff --git a/src/script/ismine.h b/src/script/ismine.h index 4b7db8802..ec7a620e3 100644 --- a/src/script/ismine.h +++ b/src/script/ismine.h @@ -28,7 +28,14 @@ enum isminetype /** used for bitflags of isminetype */ typedef uint8_t isminefilter; -isminetype IsMine(const CKeyStore& keystore, const CScript& scriptPubKey); -isminetype IsMine(const CKeyStore& keystore, const CTxDestination& dest); +/* isInvalid becomes true when the script is found invalid by consensus or policy. This will terminate the recursion + * and return a 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, SigVersion = SIGVERSION_BASE); +isminetype IsMine(const CKeyStore& keystore, const CScript& scriptPubKey, SigVersion = SIGVERSION_BASE); +isminetype IsMine(const CKeyStore& keystore, const CTxDestination& dest, bool& isInvalid, SigVersion = SIGVERSION_BASE); +isminetype IsMine(const CKeyStore& keystore, const CTxDestination& dest, SigVersion = SIGVERSION_BASE); #endif // BITCOIN_SCRIPT_ISMINE_H -- cgit v1.2.3 From 5ca8ef299a08aae91d5061750533694b58d810b2 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Thu, 20 Oct 2016 08:30:03 +0200 Subject: libconsensus: Add input validation of flags Makes it an error to use flags that have not been defined on the libconsensus API. There has been some confusion as to what pass to libconsensus, and (combined with mention in the release notes) this should clear it up. Using undocumented flags is a risk because their meaning, and what combinations are allowed, changes from release to release. E.g. it is no longer possible to pass (CLEANSTACK | P2SH) without running into an assertion after the segwit changes. --- src/script/bitcoinconsensus.cpp | 9 +++++++++ src/script/bitcoinconsensus.h | 4 ++++ 2 files changed, 13 insertions(+) (limited to 'src/script') diff --git a/src/script/bitcoinconsensus.cpp b/src/script/bitcoinconsensus.cpp index b629f4278..1d0ca0c5a 100644 --- a/src/script/bitcoinconsensus.cpp +++ b/src/script/bitcoinconsensus.cpp @@ -69,10 +69,19 @@ struct ECCryptoClosure ECCryptoClosure instance_of_eccryptoclosure; } +/** Check that all specified flags are part of the libconsensus interface. */ +static bool verify_flags(unsigned int flags) +{ + return (flags & ~(bitcoinconsensus_SCRIPT_FLAGS_VERIFY_ALL)) == 0; +} + static int verify_script(const unsigned char *scriptPubKey, unsigned int scriptPubKeyLen, CAmount amount, const unsigned char *txTo , unsigned int txToLen, unsigned int nIn, unsigned int flags, bitcoinconsensus_error* err) { + if (!verify_flags(flags)) { + return bitcoinconsensus_ERR_INVALID_FLAGS; + } try { TxInputStream stream(SER_NETWORK, PROTOCOL_VERSION, txTo, txToLen); CTransaction tx; diff --git a/src/script/bitcoinconsensus.h b/src/script/bitcoinconsensus.h index 1d2d5c23e..1bef4fe9e 100644 --- a/src/script/bitcoinconsensus.h +++ b/src/script/bitcoinconsensus.h @@ -42,6 +42,7 @@ typedef enum bitcoinconsensus_error_t bitcoinconsensus_ERR_TX_SIZE_MISMATCH, bitcoinconsensus_ERR_TX_DESERIALIZE, bitcoinconsensus_ERR_AMOUNT_REQUIRED, + bitcoinconsensus_ERR_INVALID_FLAGS, } bitcoinconsensus_error; /** Script verification flags */ @@ -54,6 +55,9 @@ enum bitcoinconsensus_SCRIPT_FLAGS_VERIFY_CHECKLOCKTIMEVERIFY = (1U << 9), // enable CHECKLOCKTIMEVERIFY (BIP65) bitcoinconsensus_SCRIPT_FLAGS_VERIFY_CHECKSEQUENCEVERIFY = (1U << 10), // enable CHECKSEQUENCEVERIFY (BIP112) bitcoinconsensus_SCRIPT_FLAGS_VERIFY_WITNESS = (1U << 11), // enable WITNESS (BIP141) + bitcoinconsensus_SCRIPT_FLAGS_VERIFY_ALL = bitcoinconsensus_SCRIPT_FLAGS_VERIFY_P2SH | bitcoinconsensus_SCRIPT_FLAGS_VERIFY_DERSIG | + bitcoinconsensus_SCRIPT_FLAGS_VERIFY_NULLDUMMY | bitcoinconsensus_SCRIPT_FLAGS_VERIFY_CHECKLOCKTIMEVERIFY | + bitcoinconsensus_SCRIPT_FLAGS_VERIFY_CHECKSEQUENCEVERIFY | bitcoinconsensus_SCRIPT_FLAGS_VERIFY_WITNESS }; /// Returns 1 if the input nIn of the serialized transaction pointed to by -- cgit v1.2.3 From c2c5d42f36f4440aaddc4a64974a52fdb07af08b Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Fri, 28 Oct 2016 21:55:12 -0700 Subject: Make streams' read and write return void The stream implementations had two cascading layers (the upper one with operator<< and operator>>, and a lower one with read and write). The lower layer's functions are never cascaded (nor should they, as they should only be used from the higher layer), so make them return void instead. --- src/script/bitcoinconsensus.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'src/script') diff --git a/src/script/bitcoinconsensus.cpp b/src/script/bitcoinconsensus.cpp index 1d0ca0c5a..4e2358d9a 100644 --- a/src/script/bitcoinconsensus.cpp +++ b/src/script/bitcoinconsensus.cpp @@ -23,7 +23,7 @@ public: m_remaining(txToLen) {} - TxInputStream& read(char* pch, size_t nSize) + void read(char* pch, size_t nSize) { if (nSize > m_remaining) throw std::ios_base::failure(std::string(__func__) + ": end of data"); @@ -37,7 +37,6 @@ public: memcpy(pch, m_data, nSize); m_remaining -= nSize; m_data += nSize; - return *this; } template -- cgit v1.2.3 From 657e05ab2e87ff725723fe8a375fc3f8aad02126 Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Fri, 28 Oct 2016 16:51:33 -0700 Subject: Make GetSerializeSize a wrapper on top of CSizeComputer Given that in default GetSerializeSize implementations created by ADD_SERIALIZE_METHODS we're already using CSizeComputer(), get rid of the specialized GetSerializeSize methods everywhere, and just use CSizeComputer. This removes a lot of code which isn't actually used anywhere. For CCompactSize and CVarInt this actually removes a more efficient size computing algorithm, which is brought back in a later commit. --- src/script/bitcoinconsensus.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/script') diff --git a/src/script/bitcoinconsensus.cpp b/src/script/bitcoinconsensus.cpp index 4e2358d9a..ec573c318 100644 --- a/src/script/bitcoinconsensus.cpp +++ b/src/script/bitcoinconsensus.cpp @@ -87,7 +87,7 @@ static int verify_script(const unsigned char *scriptPubKey, unsigned int scriptP stream >> tx; if (nIn >= tx.vin.size()) return set_error(err, bitcoinconsensus_ERR_TX_INDEX); - if (tx.GetSerializeSize(SER_NETWORK, PROTOCOL_VERSION) != txToLen) + if (GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION) != txToLen) return set_error(err, bitcoinconsensus_ERR_TX_SIZE_MISMATCH); // Regardless of the verification result, the tx did not error. -- cgit v1.2.3 From 528472111b4965b1a99c4bcf08ac5ec93d87f10f Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Fri, 28 Oct 2016 16:29:17 -0700 Subject: Get rid of nType and nVersion Remove the nType and nVersion as parameters to all serialization methods and functions. There is only one place where it's read and has an impact (in CAddress), and even there it does not impact any of the recursively invoked serializers. Instead, the few places that need nType or nVersion are changed to read it directly from the stream object, through GetType() and GetVersion() methods which are added to all stream classes. --- src/script/bitcoinconsensus.cpp | 4 +++- src/script/interpreter.cpp | 30 +++++++++++++++--------------- 2 files changed, 18 insertions(+), 16 deletions(-) (limited to 'src/script') diff --git a/src/script/bitcoinconsensus.cpp b/src/script/bitcoinconsensus.cpp index ec573c318..069ac55bf 100644 --- a/src/script/bitcoinconsensus.cpp +++ b/src/script/bitcoinconsensus.cpp @@ -42,10 +42,12 @@ public: template TxInputStream& operator>>(T& obj) { - ::Unserialize(*this, obj, m_type, m_version); + ::Unserialize(*this, obj); return *this; } + int GetVersion() const { return m_version; } + int GetType() const { return m_type; } private: const int m_type; const int m_version; diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp index 0e17ddc13..a6403f936 100644 --- a/src/script/interpreter.cpp +++ b/src/script/interpreter.cpp @@ -1069,7 +1069,7 @@ public: /** Serialize the passed scriptCode, skipping OP_CODESEPARATORs */ template - void SerializeScriptCode(S &s, int nType, int nVersion) const { + void SerializeScriptCode(S &s) const { CScript::const_iterator it = scriptCode.begin(); CScript::const_iterator itBegin = it; opcodetype opcode; @@ -1092,53 +1092,53 @@ public: /** Serialize an input of txTo */ template - void SerializeInput(S &s, unsigned int nInput, int nType, int nVersion) const { + void SerializeInput(S &s, unsigned int nInput) const { // In case of SIGHASH_ANYONECANPAY, only the input being signed is serialized if (fAnyoneCanPay) nInput = nIn; // Serialize the prevout - ::Serialize(s, txTo.vin[nInput].prevout, nType, nVersion); + ::Serialize(s, txTo.vin[nInput].prevout); // Serialize the script if (nInput != nIn) // Blank out other inputs' signatures - ::Serialize(s, CScriptBase(), nType, nVersion); + ::Serialize(s, CScriptBase()); else - SerializeScriptCode(s, nType, nVersion); + SerializeScriptCode(s); // Serialize the nSequence if (nInput != nIn && (fHashSingle || fHashNone)) // let the others update at will - ::Serialize(s, (int)0, nType, nVersion); + ::Serialize(s, (int)0); else - ::Serialize(s, txTo.vin[nInput].nSequence, nType, nVersion); + ::Serialize(s, txTo.vin[nInput].nSequence); } /** Serialize an output of txTo */ template - void SerializeOutput(S &s, unsigned int nOutput, int nType, int nVersion) const { + void SerializeOutput(S &s, unsigned int nOutput) const { if (fHashSingle && nOutput != nIn) // Do not lock-in the txout payee at other indices as txin - ::Serialize(s, CTxOut(), nType, nVersion); + ::Serialize(s, CTxOut()); else - ::Serialize(s, txTo.vout[nOutput], nType, nVersion); + ::Serialize(s, txTo.vout[nOutput]); } /** Serialize txTo */ template - void Serialize(S &s, int nType, int nVersion) const { + void Serialize(S &s) const { // Serialize nVersion - ::Serialize(s, txTo.nVersion, nType, nVersion); + ::Serialize(s, txTo.nVersion); // Serialize vin unsigned int nInputs = fAnyoneCanPay ? 1 : txTo.vin.size(); ::WriteCompactSize(s, nInputs); for (unsigned int nInput = 0; nInput < nInputs; nInput++) - SerializeInput(s, nInput, nType, nVersion); + SerializeInput(s, nInput); // Serialize vout unsigned int nOutputs = fHashNone ? 0 : (fHashSingle ? nIn+1 : txTo.vout.size()); ::WriteCompactSize(s, nOutputs); for (unsigned int nOutput = 0; nOutput < nOutputs; nOutput++) - SerializeOutput(s, nOutput, nType, nVersion); + SerializeOutput(s, nOutput); // Serialize nLockTime - ::Serialize(s, txTo.nLockTime, nType, nVersion); + ::Serialize(s, txTo.nLockTime); } }; -- cgit v1.2.3 From 81e3228fcb33e8ed32d8b9fbe917444ba080073a Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Fri, 11 Nov 2016 16:23:17 -0800 Subject: Make CTransaction actually immutable --- src/script/bitcoinconsensus.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'src/script') diff --git a/src/script/bitcoinconsensus.cpp b/src/script/bitcoinconsensus.cpp index 069ac55bf..036d6ca7b 100644 --- a/src/script/bitcoinconsensus.cpp +++ b/src/script/bitcoinconsensus.cpp @@ -85,8 +85,7 @@ static int verify_script(const unsigned char *scriptPubKey, unsigned int scriptP } try { TxInputStream stream(SER_NETWORK, PROTOCOL_VERSION, txTo, txToLen); - CTransaction tx; - stream >> tx; + CTransaction tx(deserialize, stream); if (nIn >= tx.vin.size()) return set_error(err, bitcoinconsensus_ERR_TX_INDEX); if (GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION) != txToLen) -- cgit v1.2.3 From f6fb7acda4aefd01b8ef6cd77063bfc0c4f4ab36 Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Thu, 4 Aug 2016 02:49:16 +0200 Subject: Move CTxInWitness inside CTxIn --- src/script/bitcoinconsensus.cpp | 3 ++- src/script/script.h | 2 ++ src/script/sign.cpp | 9 ++------- 3 files changed, 6 insertions(+), 8 deletions(-) (limited to 'src/script') diff --git a/src/script/bitcoinconsensus.cpp b/src/script/bitcoinconsensus.cpp index 036d6ca7b..be7fa5d3b 100644 --- a/src/script/bitcoinconsensus.cpp +++ b/src/script/bitcoinconsensus.cpp @@ -93,8 +93,9 @@ static int verify_script(const unsigned char *scriptPubKey, unsigned int scriptP // Regardless of the verification result, the tx did not error. set_error(err, bitcoinconsensus_ERR_OK); + PrecomputedTransactionData txdata(tx); - return VerifyScript(tx.vin[nIn].scriptSig, CScript(scriptPubKey, scriptPubKey + scriptPubKeyLen), nIn < tx.wit.vtxinwit.size() ? &tx.wit.vtxinwit[nIn].scriptWitness : NULL, flags, TransactionSignatureChecker(&tx, nIn, amount, txdata), NULL); + return VerifyScript(tx.vin[nIn].scriptSig, CScript(scriptPubKey, scriptPubKey + scriptPubKeyLen), &tx.vin[nIn].scriptWitness, flags, TransactionSignatureChecker(&tx, nIn, amount, txdata), NULL); } catch (const std::exception&) { return set_error(err, bitcoinconsensus_ERR_TX_DESERIALIZE); // Error deserializing } diff --git a/src/script/script.h b/src/script/script.h index 278774d32..76419c149 100644 --- a/src/script/script.h +++ b/src/script/script.h @@ -656,6 +656,8 @@ struct CScriptWitness bool IsNull() const { return stack.empty(); } + void SetNull() { stack.clear(); stack.shrink_to_fit(); } + std::string ToString() const; }; diff --git a/src/script/sign.cpp b/src/script/sign.cpp index f552ad5bb..b008df259 100644 --- a/src/script/sign.cpp +++ b/src/script/sign.cpp @@ -194,9 +194,7 @@ SignatureData DataFromTransaction(const CMutableTransaction& tx, unsigned int nI SignatureData data; assert(tx.vin.size() > nIn); data.scriptSig = tx.vin[nIn].scriptSig; - if (tx.wit.vtxinwit.size() > nIn) { - data.scriptWitness = tx.wit.vtxinwit[nIn].scriptWitness; - } + data.scriptWitness = tx.vin[nIn].scriptWitness; return data; } @@ -204,10 +202,7 @@ void UpdateTransaction(CMutableTransaction& tx, unsigned int nIn, const Signatur { assert(tx.vin.size() > nIn); tx.vin[nIn].scriptSig = data.scriptSig; - if (!data.scriptWitness.IsNull() || tx.wit.vtxinwit.size() > nIn) { - tx.wit.vtxinwit.resize(tx.vin.size()); - tx.wit.vtxinwit[nIn].scriptWitness = data.scriptWitness; - } + tx.vin[nIn].scriptWitness = data.scriptWitness; } bool SignSignature(const CKeyStore &keystore, const CScript& fromPubKey, CMutableTransaction& txTo, unsigned int nIn, const CAmount& amount, int nHashType) -- cgit v1.2.3 From 8c1dbc5e9ddbafb77e60e8c4e6eb275a3a76ac12 Mon Sep 17 00:00:00 2001 From: Karl-Johan Alm Date: Fri, 9 Dec 2016 12:01:37 +0900 Subject: Refactor: Removed begin/end_ptr functions. --- src/script/interpreter.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'src/script') diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp index a6403f936..1410d0b73 100644 --- a/src/script/interpreter.cpp +++ b/src/script/interpreter.cpp @@ -856,15 +856,15 @@ bool EvalScript(vector >& stack, const CScript& script, un valtype& vch = stacktop(-1); valtype vchHash((opcode == OP_RIPEMD160 || opcode == OP_SHA1 || opcode == OP_HASH160) ? 20 : 32); if (opcode == OP_RIPEMD160) - CRIPEMD160().Write(begin_ptr(vch), vch.size()).Finalize(begin_ptr(vchHash)); + CRIPEMD160().Write(vch.data(), vch.size()).Finalize(vchHash.data()); else if (opcode == OP_SHA1) - CSHA1().Write(begin_ptr(vch), vch.size()).Finalize(begin_ptr(vchHash)); + CSHA1().Write(vch.data(), vch.size()).Finalize(vchHash.data()); else if (opcode == OP_SHA256) - CSHA256().Write(begin_ptr(vch), vch.size()).Finalize(begin_ptr(vchHash)); + CSHA256().Write(vch.data(), vch.size()).Finalize(vchHash.data()); else if (opcode == OP_HASH160) - CHash160().Write(begin_ptr(vch), vch.size()).Finalize(begin_ptr(vchHash)); + CHash160().Write(vch.data(), vch.size()).Finalize(vchHash.data()); else if (opcode == OP_HASH256) - CHash256().Write(begin_ptr(vch), vch.size()).Finalize(begin_ptr(vchHash)); + CHash256().Write(vch.data(), vch.size()).Finalize(vchHash.data()); popstack(stack); stack.push_back(vchHash); } -- cgit v1.2.3 From c9e69fbf3915fe1187b4c2e77be5ae6b16121194 Mon Sep 17 00:00:00 2001 From: Jeremy Rubin Date: Wed, 5 Oct 2016 16:58:47 -0400 Subject: Add CuckooCache implementation and replace the sigcache map_type with it SQUASHME: Change cuckoocache to only work for powers of two, to avoid mod operator SQUASHME: Update Documentation and simplify logarithm logic SQUASHME: OSX Build Errors SQUASHME: minor Feedback from sipa + bluematt SQUASHME: DOCONLY: Clarify a few comments. --- src/script/sigcache.cpp | 77 +++++++++++++++++++++++++------------------------ src/script/sigcache.h | 9 ++++-- 2 files changed, 45 insertions(+), 41 deletions(-) (limited to 'src/script') diff --git a/src/script/sigcache.cpp b/src/script/sigcache.cpp index bdc0bfdc1..b78d7b607 100644 --- a/src/script/sigcache.cpp +++ b/src/script/sigcache.cpp @@ -11,20 +11,29 @@ #include "uint256.h" #include "util.h" +#include "cuckoocache.h" #include -#include namespace { /** * We're hashing a nonce into the entries themselves, so we don't need extra * blinding in the set hash computation. + * + * This may exhibit platform endian dependent behavior but because these are + * nonced hashes (random) and this state is only ever used locally it is safe. + * All that matters is local consistency. */ -class CSignatureCacheHasher +class SignatureCacheHasher { public: - size_t operator()(const uint256& key) const { - return key.GetCheapHash(); + template + uint32_t operator()(const uint256& key) const + { + static_assert(hash_select <8, "SignatureCacheHasher only has 8 hashes available."); + uint32_t u; + std::memcpy(&u, key.begin()+4*hash_select, 4); + return u; } }; @@ -38,11 +47,10 @@ class CSignatureCache private: //! Entries are SHA256(nonce || signature hash || public key || signature): uint256 nonce; - typedef boost::unordered_set map_type; + typedef CuckooCache::cache map_type; map_type setValid; boost::shared_mutex cs_sigcache; - public: CSignatureCache() { @@ -56,58 +64,51 @@ public: } bool - Get(const uint256& entry) + Get(const uint256& entry, const bool erase) { boost::shared_lock lock(cs_sigcache); - return setValid.count(entry); + return setValid.contains(entry, erase); } - void Erase(const uint256& entry) + void Set(uint256& entry) { boost::unique_lock lock(cs_sigcache); - setValid.erase(entry); + setValid.insert(entry); } - - void Set(const uint256& entry) + uint32_t setup_bytes(size_t n) { - size_t nMaxCacheSize = GetArg("-maxsigcachesize", DEFAULT_MAX_SIG_CACHE_SIZE) * ((size_t) 1 << 20); - if (nMaxCacheSize <= 0) return; - - boost::unique_lock lock(cs_sigcache); - while (memusage::DynamicUsage(setValid) > nMaxCacheSize) - { - map_type::size_type s = GetRand(setValid.bucket_count()); - map_type::local_iterator it = setValid.begin(s); - if (it != setValid.end(s)) { - setValid.erase(*it); - } - } - - setValid.insert(entry); + return setValid.setup_bytes(n); } }; +/* In previous versions of this code, signatureCache was a local static variable + * in CachingTransactionSignatureChecker::VerifySignature. We initialize + * signatureCache outside of VerifySignature to avoid the atomic operation per + * call overhead associated with local static variables even though + * signatureCache could be made local to VerifySignature. +*/ +static CSignatureCache signatureCache; } -bool CachingTransactionSignatureChecker::VerifySignature(const std::vector& vchSig, const CPubKey& pubkey, const uint256& sighash) const +// To be called once in AppInit2/TestingSetup to initialize the signatureCache +void InitSignatureCache() { - static CSignatureCache signatureCache; + size_t nMaxCacheSize = GetArg("-maxsigcachesize", DEFAULT_MAX_SIG_CACHE_SIZE) * ((size_t) 1 << 20); + if (nMaxCacheSize <= 0) return; + size_t nElems = signatureCache.setup_bytes(nMaxCacheSize); + LogPrintf("Using %zu MiB out of %zu requested for signature cache, able to store %zu elements\n", + (nElems*sizeof(uint256)) >>20, nMaxCacheSize>>20, nElems); +} +bool CachingTransactionSignatureChecker::VerifySignature(const std::vector& vchSig, const CPubKey& pubkey, const uint256& sighash) const +{ uint256 entry; signatureCache.ComputeEntry(entry, sighash, vchSig, pubkey); - - if (signatureCache.Get(entry)) { - if (!store) { - signatureCache.Erase(entry); - } + if (signatureCache.Get(entry, !store)) return true; - } - if (!TransactionSignatureChecker::VerifySignature(vchSig, pubkey, sighash)) return false; - - if (store) { + if (store) signatureCache.Set(entry); - } return true; } diff --git a/src/script/sigcache.h b/src/script/sigcache.h index 44551ec2b..5243fc0a4 100644 --- a/src/script/sigcache.h +++ b/src/script/sigcache.h @@ -10,9 +10,10 @@ #include -// DoS prevention: limit cache size to less than 40MB (over 500000 -// entries on 64-bit systems). -static const unsigned int DEFAULT_MAX_SIG_CACHE_SIZE = 40; +// DoS prevention: limit cache size to 32MB (over 1000000 entries on 64-bit +// systems). Due to how we count cache size, actual memory usage is slightly +// more (~32.25 MB) +static const unsigned int DEFAULT_MAX_SIG_CACHE_SIZE = 32; class CPubKey; @@ -27,4 +28,6 @@ public: bool VerifySignature(const std::vector& vchSig, const CPubKey& vchPubKey, const uint256& sighash) const; }; +void InitSignatureCache(); + #endif // BITCOIN_SCRIPT_SIGCACHE_H -- cgit v1.2.3 From 2ddfcfd2d67bc2bd8aa4682ceaba6a59614e54d1 Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Tue, 13 Dec 2016 19:36:46 -0800 Subject: Make CScript (and prevector) c++11 movable. Such moves are used when reallocating vectors that contain them, for example. --- src/script/script.h | 1 - 1 file changed, 1 deletion(-) (limited to 'src/script') diff --git a/src/script/script.h b/src/script/script.h index 76419c149..a2b9d1b79 100644 --- a/src/script/script.h +++ b/src/script/script.h @@ -394,7 +394,6 @@ protected: } public: CScript() { } - CScript(const CScript& b) : CScriptBase(b.begin(), b.end()) { } CScript(const_iterator pbegin, const_iterator pend) : CScriptBase(pbegin, pend) { } CScript(std::vector::const_iterator pbegin, std::vector::const_iterator pend) : CScriptBase(pbegin, pend) { } CScript(const unsigned char* pbegin, const unsigned char* pend) : CScriptBase(pbegin, pend) { } -- cgit v1.2.3 From 27765b6403cece54320374b37afb01a0cfe571c3 Mon Sep 17 00:00:00 2001 From: isle2983 Date: Sat, 31 Dec 2016 11:01:21 -0700 Subject: Increment MIT Licence copyright header year on files modified in 2016 Edited via: $ contrib/devtools/copyright_header.py update . --- src/script/bitcoinconsensus.cpp | 2 +- src/script/interpreter.cpp | 2 +- src/script/ismine.cpp | 2 +- src/script/ismine.h | 2 +- src/script/script.cpp | 2 +- src/script/script.h | 2 +- src/script/script_error.cpp | 2 +- src/script/script_error.h | 2 +- src/script/sigcache.cpp | 2 +- src/script/sigcache.h | 2 +- src/script/sign.h | 2 +- src/script/standard.cpp | 2 +- src/script/standard.h | 2 +- 13 files changed, 13 insertions(+), 13 deletions(-) (limited to 'src/script') diff --git a/src/script/bitcoinconsensus.cpp b/src/script/bitcoinconsensus.cpp index be7fa5d3b..c4ab441e2 100644 --- a/src/script/bitcoinconsensus.cpp +++ b/src/script/bitcoinconsensus.cpp @@ -1,5 +1,5 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2015 The Bitcoin Core developers +// Copyright (c) 2009-2016 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp index 1410d0b73..f9b783588 100644 --- a/src/script/interpreter.cpp +++ b/src/script/interpreter.cpp @@ -1,5 +1,5 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2015 The Bitcoin Core developers +// Copyright (c) 2009-2016 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/script/ismine.cpp b/src/script/ismine.cpp index 7467d23b2..608a8de8f 100644 --- a/src/script/ismine.cpp +++ b/src/script/ismine.cpp @@ -1,5 +1,5 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2015 The Bitcoin Core developers +// Copyright (c) 2009-2016 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/script/ismine.h b/src/script/ismine.h index ec7a620e3..1aa5937b3 100644 --- a/src/script/ismine.h +++ b/src/script/ismine.h @@ -1,5 +1,5 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2015 The Bitcoin Core developers +// Copyright (c) 2009-2016 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/script/script.cpp b/src/script/script.cpp index ddf677556..828ce1a05 100644 --- a/src/script/script.cpp +++ b/src/script/script.cpp @@ -1,5 +1,5 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2015 The Bitcoin Core developers +// Copyright (c) 2009-2016 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/script/script.h b/src/script/script.h index a2b9d1b79..654dff462 100644 --- a/src/script/script.h +++ b/src/script/script.h @@ -1,5 +1,5 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2015 The Bitcoin Core developers +// Copyright (c) 2009-2016 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/script/script_error.cpp b/src/script/script_error.cpp index 2c5359fe8..c9d13c92a 100644 --- a/src/script/script_error.cpp +++ b/src/script/script_error.cpp @@ -1,5 +1,5 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2014 The Bitcoin Core developers +// Copyright (c) 2009-2016 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/script/script_error.h b/src/script/script_error.h index 430836991..3200e9470 100644 --- a/src/script/script_error.h +++ b/src/script/script_error.h @@ -1,5 +1,5 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2014 The Bitcoin Core developers +// Copyright (c) 2009-2016 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/script/sigcache.cpp b/src/script/sigcache.cpp index b78d7b607..09bedc546 100644 --- a/src/script/sigcache.cpp +++ b/src/script/sigcache.cpp @@ -1,5 +1,5 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2015 The Bitcoin Core developers +// Copyright (c) 2009-2016 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/script/sigcache.h b/src/script/sigcache.h index 5243fc0a4..c123a9ba0 100644 --- a/src/script/sigcache.h +++ b/src/script/sigcache.h @@ -1,5 +1,5 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2015 The Bitcoin Core developers +// Copyright (c) 2009-2016 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/script/sign.h b/src/script/sign.h index f9aa6fca2..1cfc53c6c 100644 --- a/src/script/sign.h +++ b/src/script/sign.h @@ -1,5 +1,5 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2015 The Bitcoin Core developers +// Copyright (c) 2009-2016 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/script/standard.cpp b/src/script/standard.cpp index bb178f49f..4b9bec9aa 100644 --- a/src/script/standard.cpp +++ b/src/script/standard.cpp @@ -1,5 +1,5 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2015 The Bitcoin Core developers +// Copyright (c) 2009-2016 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/script/standard.h b/src/script/standard.h index 72aaea0b7..097e0c374 100644 --- a/src/script/standard.h +++ b/src/script/standard.h @@ -1,5 +1,5 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2015 The Bitcoin Core developers +// Copyright (c) 2009-2016 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -- cgit v1.2.3 From cc16d99f1dc8305b1b255f1cc0f2b1516aa77ed0 Mon Sep 17 00:00:00 2001 From: practicalswift Date: Wed, 18 Jan 2017 16:15:37 +0100 Subject: [trivial] Fix typos in comments --- src/script/script.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/script') diff --git a/src/script/script.cpp b/src/script/script.cpp index 828ce1a05..9f4741b1c 100644 --- a/src/script/script.cpp +++ b/src/script/script.cpp @@ -129,7 +129,7 @@ const char* GetOpName(opcodetype opcode) case OP_CHECKMULTISIG : return "OP_CHECKMULTISIG"; case OP_CHECKMULTISIGVERIFY : return "OP_CHECKMULTISIGVERIFY"; - // expanson + // expansion case OP_NOP1 : return "OP_NOP1"; case OP_CHECKLOCKTIMEVERIFY : return "OP_CHECKLOCKTIMEVERIFY"; case OP_CHECKSEQUENCEVERIFY : return "OP_CHECKSEQUENCEVERIFY"; -- cgit v1.2.3 From 55c403b8febe02555c52bac7028cd6b1f006fad1 Mon Sep 17 00:00:00 2001 From: John Newbery Date: Wed, 15 Feb 2017 14:19:16 -0500 Subject: Ensure `-maxsigcachesize` is in valid range - If the -maxsigcachesize parameter is set to zero, setup a minimum sized sigcache (2 elements) rather than segfaulting. - Handle maxsigcachesize being negative - Handle maxsigcachesize being too large --- src/script/sigcache.cpp | 5 +++-- src/script/sigcache.h | 2 ++ 2 files changed, 5 insertions(+), 2 deletions(-) (limited to 'src/script') diff --git a/src/script/sigcache.cpp b/src/script/sigcache.cpp index 09bedc546..6f364e42d 100644 --- a/src/script/sigcache.cpp +++ b/src/script/sigcache.cpp @@ -93,8 +93,9 @@ static CSignatureCache signatureCache; // To be called once in AppInit2/TestingSetup to initialize the signatureCache void InitSignatureCache() { - size_t nMaxCacheSize = GetArg("-maxsigcachesize", DEFAULT_MAX_SIG_CACHE_SIZE) * ((size_t) 1 << 20); - if (nMaxCacheSize <= 0) return; + // nMaxCacheSize is unsigned. If -maxsigcachesize is set to zero, + // setup_bytes creates the minimum possible cache (2 elements). + size_t nMaxCacheSize = std::min(std::max((int64_t)0, GetArg("-maxsigcachesize", DEFAULT_MAX_SIG_CACHE_SIZE)), MAX_MAX_SIG_CACHE_SIZE) * ((size_t) 1 << 20); size_t nElems = signatureCache.setup_bytes(nMaxCacheSize); LogPrintf("Using %zu MiB out of %zu requested for signature cache, able to store %zu elements\n", (nElems*sizeof(uint256)) >>20, nMaxCacheSize>>20, nElems); diff --git a/src/script/sigcache.h b/src/script/sigcache.h index c123a9ba0..238952bb9 100644 --- a/src/script/sigcache.h +++ b/src/script/sigcache.h @@ -14,6 +14,8 @@ // systems). Due to how we count cache size, actual memory usage is slightly // more (~32.25 MB) static const unsigned int DEFAULT_MAX_SIG_CACHE_SIZE = 32; +// Maximum sig cache size allowed +static const int64_t MAX_MAX_SIG_CACHE_SIZE = 16384; class CPubKey; -- cgit v1.2.3