diff options
Diffstat (limited to 'src/script')
| -rw-r--r-- | src/script/bitcoinconsensus.cpp | 91 | ||||
| -rw-r--r-- | src/script/bitcoinconsensus.h | 67 | ||||
| -rw-r--r-- | src/script/interpreter.cpp | 24 | ||||
| -rw-r--r-- | src/script/interpreter.h | 17 | ||||
| -rw-r--r-- | src/script/script_error.cpp | 4 | ||||
| -rw-r--r-- | src/script/script_error.h | 4 | ||||
| -rw-r--r-- | src/script/standard.h | 3 |
7 files changed, 199 insertions, 11 deletions
diff --git a/src/script/bitcoinconsensus.cpp b/src/script/bitcoinconsensus.cpp new file mode 100644 index 000000000..4faa760ad --- /dev/null +++ b/src/script/bitcoinconsensus.cpp @@ -0,0 +1,91 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2014 The Bitcoin developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "bitcoinconsensus.h" + +#include "core/transaction.h" +#include "script/interpreter.h" +#include "version.h" + +namespace { + +/** A class that deserializes a single CTransaction one time. */ +class TxInputStream +{ +public: + TxInputStream(int nTypeIn, int nVersionIn, const unsigned char *txTo, size_t txToLen) : + m_type(nTypeIn), + m_version(nVersionIn), + m_data(txTo), + m_remaining(txToLen) + {} + + TxInputStream& read(char* pch, size_t nSize) + { + if (nSize > m_remaining) + throw std::ios_base::failure(std::string(__func__) + ": end of data"); + + if (pch == NULL) + throw std::ios_base::failure(std::string(__func__) + ": bad destination buffer"); + + if (m_data == NULL) + throw std::ios_base::failure(std::string(__func__) + ": bad source buffer"); + + memcpy(pch, m_data, nSize); + m_remaining -= nSize; + m_data += nSize; + return *this; + } + + template<typename T> + TxInputStream& operator>>(T& obj) + { + ::Unserialize(*this, obj, m_type, m_version); + return *this; + } + +private: + const int m_type; + const int m_version; + const unsigned char* m_data; + size_t m_remaining; +}; + +inline int set_error(bitcoinconsensus_error* ret, bitcoinconsensus_error serror) +{ + if (ret) + *ret = serror; + return 0; +} + +} // anon namespace + +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) +{ + try { + TxInputStream stream(SER_NETWORK, PROTOCOL_VERSION, txTo, txToLen); + CTransaction tx; + stream >> tx; + if (nIn >= tx.vin.size()) + return set_error(err, bitcoinconsensus_ERR_TX_INDEX); + 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); + + return VerifyScript(tx.vin[nIn].scriptSig, CScript(scriptPubKey, scriptPubKey + scriptPubKeyLen), flags, SignatureChecker(tx, nIn), NULL); + } catch (std::exception &e) { + return set_error(err, bitcoinconsensus_ERR_TX_DESERIALIZE); // Error deserializing + } +} + +unsigned int bitcoinconsensus_version() +{ + // Just use the API version for now + return BITCOINCONSENSUS_API_VER; +} diff --git a/src/script/bitcoinconsensus.h b/src/script/bitcoinconsensus.h new file mode 100644 index 000000000..15e3337a8 --- /dev/null +++ b/src/script/bitcoinconsensus.h @@ -0,0 +1,67 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2014 The Bitcoin developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef BITCOIN_BITCOINCONSENSUS_H +#define BITCOIN_BITCOINCONSENSUS_H + +#if defined(BUILD_BITCOIN_INTERNAL) && defined(HAVE_CONFIG_H) +#include "config/bitcoin-config.h" + #if defined(_WIN32) + #if defined(DLL_EXPORT) + #if defined(HAVE_FUNC_ATTRIBUTE_DLLEXPORT) + #define EXPORT_SYMBOL __declspec(dllexport) + #else + #define EXPORT_SYMBOL + #endif + #endif + #elif defined(HAVE_FUNC_ATTRIBUTE_VISIBILITY) + #define EXPORT_SYMBOL __attribute__ ((visibility ("default"))) + #endif +#elif defined(MSC_VER) && !defined(STATIC_LIBBITCOINCONSENSUS) + #define EXPORT_SYMBOL __declspec(dllimport) +#endif + +#ifndef EXPORT_SYMBOL + #define EXPORT_SYMBOL +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#define BITCOINCONSENSUS_API_VER 0 + +typedef enum bitcoinconsensus_error_t +{ + bitcoinconsensus_ERR_OK = 0, + bitcoinconsensus_ERR_TX_INDEX, + bitcoinconsensus_ERR_TX_SIZE_MISMATCH, + bitcoinconsensus_ERR_TX_DESERIALIZE, +} bitcoinconsensus_error; + +/** Script verification flags */ +enum +{ + bitcoinconsensus_SCRIPT_FLAGS_VERIFY_NONE = 0, + bitcoinconsensus_SCRIPT_FLAGS_VERIFY_P2SH = (1U << 0), // evaluate P2SH (BIP16) subscripts +}; + +/// Returns 1 if the input nIn of the serialized transaction pointed to by +/// txTo correctly spends the scriptPubKey pointed to by scriptPubKey under +/// 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 unsigned int bitcoinconsensus_version(); + +#ifdef __cplusplus +} // extern "C" +#endif + +#undef EXPORT_SYMBOL + +#endif // BITCOIN_BITCOINCONSENSUS_H diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp index cf81fe30a..5eda23731 100644 --- a/src/script/interpreter.cpp +++ b/src/script/interpreter.cpp @@ -207,9 +207,9 @@ bool static CheckSignatureEncoding(const valtype &vchSig, unsigned int flags, Sc return true; } -bool static CheckPubKeyEncoding(const valtype &vchSig, unsigned int flags) { +bool static CheckPubKeyEncoding(const valtype &vchSig, unsigned int flags, ScriptError* serror) { if ((flags & SCRIPT_VERIFY_STRICTENC) != 0 && !IsCompressedOrUncompressedPubKey(vchSig)) { - return false; + return set_error(serror, SCRIPT_ERR_PUBKEYTYPE); } return true; } @@ -329,8 +329,14 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, un // Control // case OP_NOP: + break; + case OP_NOP1: case OP_NOP2: 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) + return set_error(serror, SCRIPT_ERR_DISCOURAGE_UPGRADABLE_NOPS); + } break; case OP_IF: @@ -786,11 +792,11 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, un // Drop the signature, since there's no way for a signature to sign itself scriptCode.FindAndDelete(CScript(vchSig)); - if (!CheckSignatureEncoding(vchSig, flags, serror)) { + if (!CheckSignatureEncoding(vchSig, flags, serror) || !CheckPubKeyEncoding(vchPubKey, flags, serror)) { //serror is set return false; } - bool fSuccess = CheckPubKeyEncoding(vchPubKey, flags) && checker.CheckSig(vchSig, vchPubKey, scriptCode); + bool fSuccess = checker.CheckSig(vchSig, vchPubKey, scriptCode); popstack(stack); popstack(stack); @@ -849,13 +855,16 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, un valtype& vchSig = stacktop(-isig); valtype& vchPubKey = stacktop(-ikey); - if (!CheckSignatureEncoding(vchSig, flags, serror)) { + // 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)) { // serror is set return false; } // Check signature - bool fOk = CheckPubKeyEncoding(vchPubKey, flags) && checker.CheckSig(vchSig, vchPubKey, scriptCode); + bool fOk = checker.CheckSig(vchSig, vchPubKey, scriptCode); if (fOk) { isig++; @@ -865,7 +874,8 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, un nKeysCount--; // If there are more signatures left than keys left, - // then too many signatures have failed + // then too many signatures have failed. Exit early, + // without checking any further signatures. if (nSigsCount > nKeysCount) fSuccess = false; } diff --git a/src/script/interpreter.h b/src/script/interpreter.h index 14cccc558..35b2f6c65 100644 --- a/src/script/interpreter.h +++ b/src/script/interpreter.h @@ -35,8 +35,8 @@ enum SCRIPT_VERIFY_P2SH = (1U << 0), // Passing a non-strict-DER signature or one with undefined hashtype to a checksig operation causes script failure. - // Passing a pubkey that is not (0x04 + 64 bytes) or (0x02 or 0x03 + 32 bytes) to checksig causes that pubkey to be - // skipped (not softfork safe: this flag can widen the validity of OP_CHECKSIG OP_NOT). + // Evaluating a pubkey that is not (0x04 + 64 bytes) or (0x02 or 0x03 + 32 bytes) by checksig causes script failure. + // (softfork safe, but not used or intended as a consensus rule). SCRIPT_VERIFY_STRICTENC = (1U << 1), // Passing a non-strict-DER signature to a checksig operation causes script failure (softfork safe, BIP62 rule 1) @@ -57,7 +57,18 @@ enum // any other push causes the script to fail (BIP62 rule 3). // In addition, whenever a stack element is interpreted as a number, it must be of minimal length (BIP62 rule 4). // (softfork safe) - SCRIPT_VERIFY_MINIMALDATA = (1U << 6) + SCRIPT_VERIFY_MINIMALDATA = (1U << 6), + + // Discourage use of NOPs reserved for upgrades (NOP1-10) + // + // Provided so that nodes can avoid accepting or mining transactions + // containing executed NOP's whose meaning may change after a soft-fork, + // thus rendering the script invalid; with this flag set executing + // discouraged NOPs fails the script. This verification flag will never be + // a mandatory flag applied to scripts in a block. NOPs that are not + // executed, e.g. within an unexecuted IF ENDIF block, are *not* rejected. + SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_NOPS = (1U << 7) + }; uint256 SignatureHash(const CScript &scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType); diff --git a/src/script/script_error.cpp b/src/script/script_error.cpp index 4a3df268e..5d24ed98b 100644 --- a/src/script/script_error.cpp +++ b/src/script/script_error.cpp @@ -59,6 +59,10 @@ 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_DISCOURAGE_UPGRADABLE_NOPS: + return "NOPx reserved for soft-fork upgrades"; + case SCRIPT_ERR_PUBKEYTYPE: + return "Public key is neither compressed or uncompressed"; 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 ae6626b25..ac1f2deae 100644 --- a/src/script/script_error.h +++ b/src/script/script_error.h @@ -42,6 +42,10 @@ typedef enum ScriptError_t SCRIPT_ERR_SIG_PUSHONLY, SCRIPT_ERR_SIG_HIGH_S, SCRIPT_ERR_SIG_NULLDUMMY, + SCRIPT_ERR_PUBKEYTYPE, + + /* softfork safeness */ + SCRIPT_ERR_DISCOURAGE_UPGRADABLE_NOPS, SCRIPT_ERR_ERROR_COUNT } ScriptError; diff --git a/src/script/standard.h b/src/script/standard.h index f3dcc75fd..c4b82b4c4 100644 --- a/src/script/standard.h +++ b/src/script/standard.h @@ -47,7 +47,8 @@ static const unsigned int MANDATORY_SCRIPT_VERIFY_FLAGS = SCRIPT_VERIFY_P2SH; static const unsigned int STANDARD_SCRIPT_VERIFY_FLAGS = MANDATORY_SCRIPT_VERIFY_FLAGS | SCRIPT_VERIFY_STRICTENC | SCRIPT_VERIFY_MINIMALDATA | - SCRIPT_VERIFY_NULLDUMMY; + SCRIPT_VERIFY_NULLDUMMY | + SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_NOPS; /** 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; |