diff options
-rw-r--r-- | .travis.yml | 2 | ||||
-rwxr-xr-x | qa/rpc-tests/test_framework.py | 8 | ||||
-rw-r--r-- | src/Makefile.am | 3 | ||||
-rw-r--r-- | src/qt/forms/coincontroldialog.ui | 12 | ||||
-rw-r--r-- | src/rest.cpp | 206 | ||||
-rw-r--r-- | src/rpcserver.cpp | 7 | ||||
-rw-r--r-- | src/rpcserver.h | 6 | ||||
-rw-r--r-- | src/rpcwallet.cpp | 4 | ||||
-rw-r--r-- | src/script/interpreter.cpp | 297 | ||||
-rw-r--r-- | src/script/interpreter.h | 6 | ||||
-rw-r--r-- | src/script/script_error.cpp | 67 | ||||
-rw-r--r-- | src/script/script_error.h | 53 | ||||
-rw-r--r-- | src/test/multisig_tests.cpp | 37 | ||||
-rw-r--r-- | src/test/script_P2SH_tests.cpp | 19 | ||||
-rw-r--r-- | src/test/script_tests.cpp | 66 | ||||
-rw-r--r-- | src/test/transaction_tests.cpp | 10 | ||||
-rw-r--r-- | src/txmempool.cpp | 38 | ||||
-rw-r--r-- | src/txmempool.h | 44 | ||||
-rw-r--r-- | src/util.cpp | 48 | ||||
-rw-r--r-- | src/util.h | 37 | ||||
-rw-r--r-- | src/utilmoneystr.cpp | 2 | ||||
-rw-r--r-- | src/utilmoneystr.h | 2 | ||||
-rw-r--r-- | src/utilstrencodings.cpp | 8 | ||||
-rw-r--r-- | src/utilstrencodings.h | 9 | ||||
-rw-r--r-- | src/utiltime.cpp | 13 | ||||
-rw-r--r-- | src/utiltime.h | 2 |
26 files changed, 752 insertions, 254 deletions
diff --git a/.travis.yml b/.travis.yml index f6b011ee1c..567428daf9 100644 --- a/.travis.yml +++ b/.travis.yml @@ -40,7 +40,7 @@ matrix: env: HOST=i686-w64-mingw32 PACKAGES="nsis gcc-mingw-w64-i686 g++-mingw-w64-i686 binutils-mingw-w64-i686 mingw-w64-dev wine bc" RUN_TESTS=true GOAL="deploy" BITCOIN_CONFIG="--enable-gui" MAKEJOBS="-j2" install: - if [ -n "$PACKAGES" ]; then travis_retry sudo apt-get update; fi - - if [ -n "$PACKAGES" ]; then travis_retry sudo apt-get install --no-upgrade -qq $PACKAGES; fi + - if [ -n "$PACKAGES" ]; then travis_retry sudo apt-get install --no-install-recommends --no-upgrade -qq $PACKAGES; fi before_script: - unset CC; unset CXX - mkdir -p depends/SDKs depends/sdk-sources diff --git a/qa/rpc-tests/test_framework.py b/qa/rpc-tests/test_framework.py index c3396a5a83..9591c024fb 100755 --- a/qa/rpc-tests/test_framework.py +++ b/qa/rpc-tests/test_framework.py @@ -44,8 +44,8 @@ class BitcoinTestFramework(object): # on outward. This ensures that chains are properly reorganised. if not split: connect_nodes_bi(self.nodes, 1, 2) - sync_blocks(self.nodes[1:2]) - sync_mempools(self.nodes[1:2]) + sync_blocks(self.nodes[1:3]) + sync_mempools(self.nodes[1:3]) connect_nodes_bi(self.nodes, 0, 1) connect_nodes_bi(self.nodes, 2, 3) @@ -63,9 +63,9 @@ class BitcoinTestFramework(object): def sync_all(self): if self.is_network_split: - sync_blocks(self.nodes[:1]) + sync_blocks(self.nodes[:2]) sync_blocks(self.nodes[2:]) - sync_mempools(self.nodes[:1]) + sync_mempools(self.nodes[:2]) sync_mempools(self.nodes[2:]) else: sync_blocks(self.nodes) diff --git a/src/Makefile.am b/src/Makefile.am index 8b5d009842..3da833d733 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -112,6 +112,7 @@ BITCOIN_CORE_H = \ script/sigcache.h \ script/sign.h \ script/standard.h \ + script/script_error.h \ serialize.h \ streams.h \ sync.h \ @@ -165,6 +166,7 @@ libbitcoin_server_a_SOURCES = \ net.cpp \ noui.cpp \ pow.cpp \ + rest.cpp \ rpcblockchain.cpp \ rpcmining.cpp \ rpcmisc.cpp \ @@ -235,6 +237,7 @@ libbitcoin_common_a_SOURCES = \ script/script.cpp \ script/sign.cpp \ script/standard.cpp \ + script/script_error.cpp \ $(BITCOIN_CORE_H) # util: shared between all executables. diff --git a/src/qt/forms/coincontroldialog.ui b/src/qt/forms/coincontroldialog.ui index cbe58fec65..c1fef6b9b1 100644 --- a/src/qt/forms/coincontroldialog.ui +++ b/src/qt/forms/coincontroldialog.ui @@ -11,7 +11,7 @@ </rect> </property> <property name="windowTitle"> - <string>Coin Control Address Selection</string> + <string>Coin Selection</string> </property> <layout class="QVBoxLayout" name="verticalLayout"> <item> @@ -379,9 +379,6 @@ <property name="text"> <string>Tree mode</string> </property> - <property name="checked"> - <bool>true</bool> - </property> </widget> </item> <item> @@ -395,6 +392,9 @@ <property name="text"> <string>List mode</string> </property> + <property name="checked"> + <bool>true</bool> + </property> </widget> </item> <item> @@ -451,12 +451,12 @@ </column> <column> <property name="text"> - <string notr="true">Label</string> + <string>Received with label</string> </property> </column> <column> <property name="text"> - <string>Address</string> + <string>Received with address</string> </property> </column> <column> diff --git a/src/rest.cpp b/src/rest.cpp new file mode 100644 index 0000000000..9a8793a517 --- /dev/null +++ b/src/rest.cpp @@ -0,0 +1,206 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2012 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include <boost/algorithm/string.hpp> +#include "rpcserver.h" +#include "streams.h" +#include "utilstrencodings.h" +#include "core/block.h" +#include "core/transaction.h" +#include "version.h" +#include "main.h" +#include "sync.h" + +using namespace std; +using namespace json_spirit; + +enum RetFormat { + RF_BINARY, + RF_HEX, + RF_JSON, +}; + +static const struct { + enum RetFormat rf; + const char *name; +} rf_names[] = { + { RF_BINARY, "binary" }, // default, if match not found + { RF_HEX, "hex" }, + { RF_JSON, "json" }, +}; + +class RestErr { +public: + enum HTTPStatusCode status; + string message; +}; + +extern void TxToJSON(const CTransaction& tx, const uint256 hashBlock, Object& entry); +extern Object blockToJSON(const CBlock& block, const CBlockIndex* blockindex); + +static RestErr RESTERR(enum HTTPStatusCode status, string message) +{ + RestErr re; + re.status = status; + re.message = message; + return re; +} + +static enum RetFormat ParseDataFormat(const string& format) +{ + for (unsigned int i = 0; i < ARRAYLEN(rf_names); i++) + if (format == rf_names[i].name) + return rf_names[i].rf; + + return rf_names[0].rf; +} + +static bool ParseHashStr(const string& strReq, uint256& v) +{ + if (!IsHex(strReq) || (strReq.size() != 64)) + return false; + + v.SetHex(strReq); + return true; +} + +static bool rest_block(AcceptedConnection *conn, + string& strReq, + map<string, string>& mapHeaders, + bool fRun) +{ + vector<string> params; + boost::split(params, strReq, boost::is_any_of("/")); + + enum RetFormat rf = ParseDataFormat(params.size() > 1 ? params[1] : string("")); + + string hashStr = params[0]; + uint256 hash; + if (!ParseHashStr(hashStr, hash)) + throw RESTERR(HTTP_BAD_REQUEST, "Invalid hash: " + hashStr); + + CBlock block; + CBlockIndex* pblockindex = NULL; + { + LOCK(cs_main); + if (mapBlockIndex.count(hash) == 0) + throw RESTERR(HTTP_NOT_FOUND, hashStr + " not found"); + + pblockindex = mapBlockIndex[hash]; + if (!ReadBlockFromDisk(block, pblockindex)) + throw RESTERR(HTTP_NOT_FOUND, hashStr + " not found"); + } + + CDataStream ssBlock(SER_NETWORK, PROTOCOL_VERSION); + ssBlock << block; + + switch (rf) { + case RF_BINARY: { + string binaryBlock = ssBlock.str(); + conn->stream() << HTTPReply(HTTP_OK, binaryBlock, fRun, true, "application/octet-stream") << binaryBlock << std::flush; + return true; + } + + case RF_HEX: { + string strHex = HexStr(ssBlock.begin(), ssBlock.end()) + "\n";; + conn->stream() << HTTPReply(HTTP_OK, strHex, fRun, false, "text/plain") << std::flush; + return true; + } + + case RF_JSON: { + Object objBlock = blockToJSON(block, pblockindex); + string strJSON = write_string(Value(objBlock), false) + "\n"; + conn->stream() << HTTPReply(HTTP_OK, strJSON, fRun) << std::flush; + return true; + } + } + + // not reached + return true; // continue to process further HTTP reqs on this cxn +} + +static bool rest_tx(AcceptedConnection *conn, + string& strReq, + map<string, string>& mapHeaders, + bool fRun) +{ + vector<string> params; + boost::split(params, strReq, boost::is_any_of("/")); + + enum RetFormat rf = ParseDataFormat(params.size() > 1 ? params[1] : string("")); + + string hashStr = params[0]; + uint256 hash; + if (!ParseHashStr(hashStr, hash)) + throw RESTERR(HTTP_BAD_REQUEST, "Invalid hash: " + hashStr); + + CTransaction tx; + uint256 hashBlock = 0; + if (!GetTransaction(hash, tx, hashBlock, true)) + throw RESTERR(HTTP_NOT_FOUND, hashStr + " not found"); + + CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION); + ssTx << tx; + + switch (rf) { + case RF_BINARY: { + string binaryTx = ssTx.str(); + conn->stream() << HTTPReply(HTTP_OK, binaryTx, fRun, true, "application/octet-stream") << binaryTx << std::flush; + return true; + } + + case RF_HEX: { + string strHex = HexStr(ssTx.begin(), ssTx.end()) + "\n";; + conn->stream() << HTTPReply(HTTP_OK, strHex, fRun, false, "text/plain") << std::flush; + return true; + } + + case RF_JSON: { + Object objTx; + TxToJSON(tx, hashBlock, objTx); + string strJSON = write_string(Value(objTx), false) + "\n"; + conn->stream() << HTTPReply(HTTP_OK, strJSON, fRun) << std::flush; + return true; + } + } + + // not reached + return true; // continue to process further HTTP reqs on this cxn +} + +static const struct { + const char *prefix; + bool (*handler)(AcceptedConnection *conn, + string& strURI, + map<string, string>& mapHeaders, + bool fRun); +} uri_prefixes[] = { + { "/rest/tx/", rest_tx }, + { "/rest/block/", rest_block }, +}; + +bool HTTPReq_REST(AcceptedConnection *conn, + string& strURI, + map<string, string>& mapHeaders, + bool fRun) +{ + try { + for (unsigned int i = 0; i < ARRAYLEN(uri_prefixes); i++) { + unsigned int plen = strlen(uri_prefixes[i].prefix); + if (strURI.substr(0, plen) == uri_prefixes[i].prefix) { + string strReq = strURI.substr(plen); + return uri_prefixes[i].handler(conn, strReq, mapHeaders, fRun); + } + } + } + catch (RestErr& re) { + conn->stream() << HTTPReply(re.status, re.message + "\r\n", false, false, "text/plain") << std::flush; + return false; + } + + conn->stream() << HTTPError(HTTP_NOT_FOUND, false) << std::flush; + return false; +} + diff --git a/src/rpcserver.cpp b/src/rpcserver.cpp index 709e9e9402..01005c1cee 100644 --- a/src/rpcserver.cpp +++ b/src/rpcserver.cpp @@ -947,9 +947,16 @@ void ServiceConnection(AcceptedConnection *conn) if (mapHeaders["connection"] == "close") fRun = false; + // Process via JSON-RPC API if (strURI == "/") { if (!HTTPReq_JSONRPC(conn, strRequest, mapHeaders, fRun)) break; + + // Process via HTTP REST API + } else if (strURI.substr(0, 6) == "/rest/") { + if (!HTTPReq_REST(conn, strURI, mapHeaders, fRun)) + break; + } else { conn->stream() << HTTPError(HTTP_NOT_FOUND, false) << std::flush; break; diff --git a/src/rpcserver.h b/src/rpcserver.h index 9a0681bfa3..b3234f65f2 100644 --- a/src/rpcserver.h +++ b/src/rpcserver.h @@ -219,4 +219,10 @@ extern json_spirit::Value gettxout(const json_spirit::Array& params, bool fHelp) extern json_spirit::Value verifychain(const json_spirit::Array& params, bool fHelp); extern json_spirit::Value getchaintips(const json_spirit::Array& params, bool fHelp); +// in rest.cpp +extern bool HTTPReq_REST(AcceptedConnection *conn, + std::string& strURI, + std::map<std::string, std::string>& mapHeaders, + bool fRun); + #endif // BITCOIN_RPCSERVER_H diff --git a/src/rpcwallet.cpp b/src/rpcwallet.cpp index f2b5e2061e..4d9e5ea137 100644 --- a/src/rpcwallet.cpp +++ b/src/rpcwallet.cpp @@ -1067,7 +1067,7 @@ Value listreceivedbyaddress(const Array& params, bool fHelp) "\nList balances by receiving address.\n" "\nArguments:\n" "1. minconf (numeric, optional, default=1) The minimum number of confirmations before payments are included.\n" - "2. includeempty (numeric, optional, dafault=false) Whether to include addresses that haven't received any payments.\n" + "2. includeempty (numeric, optional, default=false) Whether to include addresses that haven't received any payments.\n" "3. includeWatchonly (bool, optional, default=false) Whether to include watchonly addresses (see 'importaddress').\n" "\nResult:\n" @@ -1335,7 +1335,7 @@ Value listaccounts(const Array& params, bool fHelp) "listaccounts ( minconf includeWatchonly)\n" "\nReturns Object that has account names as keys, account balances as values.\n" "\nArguments:\n" - "1. minconf (numeric, optional, default=1) Only onclude transactions with at least this many confirmations\n" + "1. minconf (numeric, optional, default=1) Only include transactions with at least this many confirmations\n" "2. includeWatchonly (bool, optional, default=false) Include balances in watchonly addresses (see 'importaddress')\n" "\nResult:\n" "{ (json object where keys are account names, and values are numeric balances\n" diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp index 5fda6248c2..cf81fe30a2 100644 --- a/src/script/interpreter.cpp +++ b/src/script/interpreter.cpp @@ -13,7 +13,6 @@ #include "pubkey.h" #include "script/script.h" #include "uint256.h" -#include "util.h" using namespace std; @@ -26,6 +25,24 @@ static const CScriptNum bnOne(1); static const CScriptNum bnFalse(0); static const CScriptNum bnTrue(1); +namespace { + +inline bool set_success(ScriptError* ret) +{ + if (ret) + *ret = SCRIPT_ERR_OK; + return true; +} + +inline bool set_error(ScriptError* ret, const ScriptError serror) +{ + if (ret) + *ret = serror; + return false; +} + +} // anon namespace + bool CastToBool(const valtype& vch) { for (unsigned int i = 0; i < vch.size(); i++) @@ -55,16 +72,23 @@ static inline void popstack(vector<valtype>& stack) } bool static IsCompressedOrUncompressedPubKey(const valtype &vchPubKey) { - if (vchPubKey.size() < 33) - return error("Non-canonical public key: too short"); + if (vchPubKey.size() < 33) { + // Non-canonical public key: too short + return false; + } if (vchPubKey[0] == 0x04) { - if (vchPubKey.size() != 65) - return error("Non-canonical public key: invalid length for uncompressed key"); + if (vchPubKey.size() != 65) { + // Non-canonical public key: invalid length for uncompressed key + return false; + } } else if (vchPubKey[0] == 0x02 || vchPubKey[0] == 0x03) { - if (vchPubKey.size() != 33) - return error("Non-canonical public key: invalid length for compressed key"); + if (vchPubKey.size() != 33) { + // Non-canonical public key: invalid length for compressed key + return false; + } } else { - return error("Non-canonical public key: neither compressed nor uncompressed"); + // Non-canonical public key: neither compressed nor uncompressed + return false; } return true; } @@ -79,47 +103,74 @@ bool static IsCompressedOrUncompressedPubKey(const valtype &vchPubKey) { */ bool static IsDERSignature(const valtype &vchSig) { - if (vchSig.size() < 9) - return error("Non-canonical signature: too short"); - if (vchSig.size() > 73) - return error("Non-canonical signature: too long"); - if (vchSig[0] != 0x30) - return error("Non-canonical signature: wrong type"); - if (vchSig[1] != vchSig.size()-3) - return error("Non-canonical signature: wrong length marker"); + if (vchSig.size() < 9) { + // Non-canonical signature: too short + return false; + } + if (vchSig.size() > 73) { + // Non-canonical signature: too long + return false; + } + if (vchSig[0] != 0x30) { + // Non-canonical signature: wrong type + return false; + } + if (vchSig[1] != vchSig.size()-3) { + // Non-canonical signature: wrong length marker + return false; + } unsigned int nLenR = vchSig[3]; - if (5 + nLenR >= vchSig.size()) - return error("Non-canonical signature: S length misplaced"); + if (5 + nLenR >= vchSig.size()) { + // Non-canonical signature: S length misplaced + return false; + } unsigned int nLenS = vchSig[5+nLenR]; - if ((unsigned long)(nLenR+nLenS+7) != vchSig.size()) - return error("Non-canonical signature: R+S length mismatch"); + if ((unsigned long)(nLenR+nLenS+7) != vchSig.size()) { + // Non-canonical signature: R+S length mismatch + return false; + } const unsigned char *R = &vchSig[4]; - if (R[-2] != 0x02) - return error("Non-canonical signature: R value type mismatch"); - if (nLenR == 0) - return error("Non-canonical signature: R length is zero"); - if (R[0] & 0x80) - return error("Non-canonical signature: R value negative"); - if (nLenR > 1 && (R[0] == 0x00) && !(R[1] & 0x80)) - return error("Non-canonical signature: R value excessively padded"); + if (R[-2] != 0x02) { + // Non-canonical signature: R value type mismatch + return false; + } + if (nLenR == 0) { + // Non-canonical signature: R length is zero + return false; + } + if (R[0] & 0x80) { + // Non-canonical signature: R value negative + return false; + } + if (nLenR > 1 && (R[0] == 0x00) && !(R[1] & 0x80)) { + // Non-canonical signature: R value excessively padded + return false; + } const unsigned char *S = &vchSig[6+nLenR]; - if (S[-2] != 0x02) - return error("Non-canonical signature: S value type mismatch"); - if (nLenS == 0) - return error("Non-canonical signature: S length is zero"); - if (S[0] & 0x80) - return error("Non-canonical signature: S value negative"); - if (nLenS > 1 && (S[0] == 0x00) && !(S[1] & 0x80)) - return error("Non-canonical signature: S value excessively padded"); - + if (S[-2] != 0x02) { + // Non-canonical signature: S value type mismatch + return false; + } + if (nLenS == 0) { + // Non-canonical signature: S length is zero + return false; + } + if (S[0] & 0x80) { + // Non-canonical signature: S value negative + return false; + } + if (nLenS > 1 && (S[0] == 0x00) && !(S[1] & 0x80)) { + // Non-canonical signature: S value excessively padded + return false; + } return true; } -bool static IsLowDERSignature(const valtype &vchSig) { +bool static IsLowDERSignature(const valtype &vchSig, ScriptError* serror) { if (!IsDERSignature(vchSig)) { - return false; + return set_error(serror, SCRIPT_ERR_SIG_DER); } unsigned int nLenR = vchSig[3]; unsigned int nLenS = vchSig[5+nLenR]; @@ -128,7 +179,7 @@ bool static IsLowDERSignature(const valtype &vchSig) { // complement modulo the order could have been used instead, which is // one byte shorter when encoded correctly. if (!eccrypto::CheckSignatureElement(S, nLenS, true)) - return error("Non-canonical signature: S value is unnecessarily high"); + return set_error(serror, SCRIPT_ERR_SIG_HIGH_S); return true; } @@ -139,18 +190,19 @@ bool static IsDefinedHashtypeSignature(const valtype &vchSig) { } unsigned char nHashType = vchSig[vchSig.size() - 1] & (~(SIGHASH_ANYONECANPAY)); if (nHashType < SIGHASH_ALL || nHashType > SIGHASH_SINGLE) - return error("Non-canonical signature: unknown hashtype byte"); + return false; return true; } -bool static CheckSignatureEncoding(const valtype &vchSig, unsigned int flags) { +bool static CheckSignatureEncoding(const valtype &vchSig, unsigned int flags, ScriptError* serror) { if ((flags & (SCRIPT_VERIFY_DERSIG | SCRIPT_VERIFY_LOW_S | SCRIPT_VERIFY_STRICTENC)) != 0 && !IsDERSignature(vchSig)) { - return false; - } else if ((flags & SCRIPT_VERIFY_LOW_S) != 0 && !IsLowDERSignature(vchSig)) { + return set_error(serror, SCRIPT_ERR_SIG_DER); + } else if ((flags & SCRIPT_VERIFY_LOW_S) != 0 && !IsLowDERSignature(vchSig, serror)) { + // serror is set return false; } else if ((flags & SCRIPT_VERIFY_STRICTENC) != 0 && !IsDefinedHashtypeSignature(vchSig)) { - return false; + return set_error(serror, SCRIPT_ERR_SIG_HASHTYPE); } return true; } @@ -185,7 +237,7 @@ bool static CheckMinimalPush(const valtype& data, opcodetype opcode) { return true; } -bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, unsigned int flags, const BaseSignatureChecker& checker) +bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, unsigned int flags, const BaseSignatureChecker& checker, ScriptError* serror) { CScript::const_iterator pc = script.begin(); CScript::const_iterator pend = script.end(); @@ -194,8 +246,9 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, un valtype vchPushValue; vector<bool> vfExec; vector<valtype> altstack; + set_error(serror, SCRIPT_ERR_UNKNOWN_ERROR); if (script.size() > 10000) - return false; + return set_error(serror, SCRIPT_ERR_SCRIPT_SIZE); int nOpCount = 0; bool fRequireMinimal = (flags & SCRIPT_VERIFY_MINIMALDATA) != 0; @@ -209,13 +262,13 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, un // Read instruction // if (!script.GetOp(pc, opcode, vchPushValue)) - return false; + return set_error(serror, SCRIPT_ERR_BAD_OPCODE); if (vchPushValue.size() > MAX_SCRIPT_ELEMENT_SIZE) - return false; + 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) - return false; + return set_error(serror, SCRIPT_ERR_OP_COUNT); if (opcode == OP_CAT || opcode == OP_SUBSTR || @@ -232,11 +285,11 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, un opcode == OP_MOD || opcode == OP_LSHIFT || opcode == OP_RSHIFT) - return false; // Disabled opcodes. + return set_error(serror, SCRIPT_ERR_DISABLED_OPCODE); // Disabled opcodes. if (fExec && 0 <= opcode && opcode <= OP_PUSHDATA4) { if (fRequireMinimal && !CheckMinimalPush(vchPushValue, opcode)) { - return false; + return set_error(serror, SCRIPT_ERR_MINIMALDATA); } stack.push_back(vchPushValue); } else if (fExec || (OP_IF <= opcode && opcode <= OP_ENDIF)) @@ -288,7 +341,7 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, un if (fExec) { if (stack.size() < 1) - return false; + return set_error(serror, SCRIPT_ERR_UNBALANCED_CONDITIONAL); valtype& vch = stacktop(-1); fValue = CastToBool(vch); if (opcode == OP_NOTIF) @@ -302,7 +355,7 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, un case OP_ELSE: { if (vfExec.empty()) - return false; + return set_error(serror, SCRIPT_ERR_UNBALANCED_CONDITIONAL); vfExec.back() = !vfExec.back(); } break; @@ -310,7 +363,7 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, un case OP_ENDIF: { if (vfExec.empty()) - return false; + return set_error(serror, SCRIPT_ERR_UNBALANCED_CONDITIONAL); vfExec.pop_back(); } break; @@ -320,18 +373,18 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, un // (true -- ) or // (false -- false) and return if (stack.size() < 1) - return false; + return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION); bool fValue = CastToBool(stacktop(-1)); if (fValue) popstack(stack); else - return false; + return set_error(serror, SCRIPT_ERR_VERIFY); } break; case OP_RETURN: { - return false; + return set_error(serror, SCRIPT_ERR_OP_RETURN); } break; @@ -342,7 +395,7 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, un case OP_TOALTSTACK: { if (stack.size() < 1) - return false; + return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION); altstack.push_back(stacktop(-1)); popstack(stack); } @@ -351,7 +404,7 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, un case OP_FROMALTSTACK: { if (altstack.size() < 1) - return false; + return set_error(serror, SCRIPT_ERR_INVALID_ALTSTACK_OPERATION); stack.push_back(altstacktop(-1)); popstack(altstack); } @@ -361,7 +414,7 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, un { // (x1 x2 -- ) if (stack.size() < 2) - return false; + return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION); popstack(stack); popstack(stack); } @@ -371,7 +424,7 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, un { // (x1 x2 -- x1 x2 x1 x2) if (stack.size() < 2) - return false; + return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION); valtype vch1 = stacktop(-2); valtype vch2 = stacktop(-1); stack.push_back(vch1); @@ -383,7 +436,7 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, un { // (x1 x2 x3 -- x1 x2 x3 x1 x2 x3) if (stack.size() < 3) - return false; + return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION); valtype vch1 = stacktop(-3); valtype vch2 = stacktop(-2); valtype vch3 = stacktop(-1); @@ -397,7 +450,7 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, un { // (x1 x2 x3 x4 -- x1 x2 x3 x4 x1 x2) if (stack.size() < 4) - return false; + return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION); valtype vch1 = stacktop(-4); valtype vch2 = stacktop(-3); stack.push_back(vch1); @@ -409,7 +462,7 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, un { // (x1 x2 x3 x4 x5 x6 -- x3 x4 x5 x6 x1 x2) if (stack.size() < 6) - return false; + return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION); valtype vch1 = stacktop(-6); valtype vch2 = stacktop(-5); stack.erase(stack.end()-6, stack.end()-4); @@ -422,7 +475,7 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, un { // (x1 x2 x3 x4 -- x3 x4 x1 x2) if (stack.size() < 4) - return false; + return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION); swap(stacktop(-4), stacktop(-2)); swap(stacktop(-3), stacktop(-1)); } @@ -432,7 +485,7 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, un { // (x - 0 | x x) if (stack.size() < 1) - return false; + return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION); valtype vch = stacktop(-1); if (CastToBool(vch)) stack.push_back(vch); @@ -451,7 +504,7 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, un { // (x -- ) if (stack.size() < 1) - return false; + return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION); popstack(stack); } break; @@ -460,7 +513,7 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, un { // (x -- x x) if (stack.size() < 1) - return false; + return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION); valtype vch = stacktop(-1); stack.push_back(vch); } @@ -470,7 +523,7 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, un { // (x1 x2 -- x2) if (stack.size() < 2) - return false; + return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION); stack.erase(stack.end() - 2); } break; @@ -479,7 +532,7 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, un { // (x1 x2 -- x1 x2 x1) if (stack.size() < 2) - return false; + return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION); valtype vch = stacktop(-2); stack.push_back(vch); } @@ -491,11 +544,11 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, un // (xn ... x2 x1 x0 n - xn ... x2 x1 x0 xn) // (xn ... x2 x1 x0 n - ... x2 x1 x0 xn) if (stack.size() < 2) - return false; + return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION); int n = CScriptNum(stacktop(-1), fRequireMinimal).getint(); popstack(stack); if (n < 0 || n >= (int)stack.size()) - return false; + return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION); valtype vch = stacktop(-n-1); if (opcode == OP_ROLL) stack.erase(stack.end()-n-1); @@ -509,7 +562,7 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, un // x2 x1 x3 after first swap // x2 x3 x1 after second swap if (stack.size() < 3) - return false; + return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION); swap(stacktop(-3), stacktop(-2)); swap(stacktop(-2), stacktop(-1)); } @@ -519,7 +572,7 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, un { // (x1 x2 -- x2 x1) if (stack.size() < 2) - return false; + return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION); swap(stacktop(-2), stacktop(-1)); } break; @@ -528,7 +581,7 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, un { // (x1 x2 -- x2 x1 x2) if (stack.size() < 2) - return false; + return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION); valtype vch = stacktop(-1); stack.insert(stack.end()-2, vch); } @@ -539,7 +592,7 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, un { // (in -- in size) if (stack.size() < 1) - return false; + return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION); CScriptNum bn(stacktop(-1).size()); stack.push_back(bn.getvch()); } @@ -555,7 +608,7 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, un { // (x1 x2 - bool) if (stack.size() < 2) - return false; + return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION); valtype& vch1 = stacktop(-2); valtype& vch2 = stacktop(-1); bool fEqual = (vch1 == vch2); @@ -572,7 +625,7 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, un if (fEqual) popstack(stack); else - return false; + return set_error(serror, SCRIPT_ERR_EQUALVERIFY); } } break; @@ -590,7 +643,7 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, un { // (in -- out) if (stack.size() < 1) - return false; + return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION); CScriptNum bn(stacktop(-1), fRequireMinimal); switch (opcode) { @@ -623,7 +676,7 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, un { // (x1 x2 -- out) if (stack.size() < 2) - return false; + return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION); CScriptNum bn1(stacktop(-2), fRequireMinimal); CScriptNum bn2(stacktop(-1), fRequireMinimal); CScriptNum bn(0); @@ -659,7 +712,7 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, un if (CastToBool(stacktop(-1))) popstack(stack); else - return false; + return set_error(serror, SCRIPT_ERR_NUMEQUALVERIFY); } } break; @@ -668,7 +721,7 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, un { // (x min max -- out) if (stack.size() < 3) - return false; + return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION); CScriptNum bn1(stacktop(-3), fRequireMinimal); CScriptNum bn2(stacktop(-2), fRequireMinimal); CScriptNum bn3(stacktop(-1), fRequireMinimal); @@ -692,7 +745,7 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, un { // (in -- hash) if (stack.size() < 1) - return false; + return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION); valtype& vch = stacktop(-1); valtype vchHash((opcode == OP_RIPEMD160 || opcode == OP_SHA1 || opcode == OP_HASH160) ? 20 : 32); if (opcode == OP_RIPEMD160) @@ -722,7 +775,7 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, un { // (sig pubkey -- bool) if (stack.size() < 2) - return false; + return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION); valtype& vchSig = stacktop(-2); valtype& vchPubKey = stacktop(-1); @@ -733,10 +786,10 @@ 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)) { + if (!CheckSignatureEncoding(vchSig, flags, serror)) { + //serror is set return false; } - bool fSuccess = CheckPubKeyEncoding(vchPubKey, flags) && checker.CheckSig(vchSig, vchPubKey, scriptCode); popstack(stack); @@ -747,7 +800,7 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, un if (fSuccess) popstack(stack); else - return false; + return set_error(serror, SCRIPT_ERR_CHECKSIGVERIFY); } } break; @@ -759,26 +812,26 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, un int i = 1; if ((int)stack.size() < i) - return false; + return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION); int nKeysCount = CScriptNum(stacktop(-i), fRequireMinimal).getint(); if (nKeysCount < 0 || nKeysCount > 20) - return false; + return set_error(serror, SCRIPT_ERR_PUBKEY_COUNT); nOpCount += nKeysCount; if (nOpCount > 201) - return false; + return set_error(serror, SCRIPT_ERR_OP_COUNT); int ikey = ++i; i += nKeysCount; if ((int)stack.size() < i) - return false; + return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION); int nSigsCount = CScriptNum(stacktop(-i), fRequireMinimal).getint(); if (nSigsCount < 0 || nSigsCount > nKeysCount) - return false; + return set_error(serror, SCRIPT_ERR_SIG_COUNT); int isig = ++i; i += nSigsCount; if ((int)stack.size() < i) - return false; + return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION); // Subset of script starting at the most recent codeseparator CScript scriptCode(pbegincodehash, pend); @@ -796,7 +849,8 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, un valtype& vchSig = stacktop(-isig); valtype& vchPubKey = stacktop(-ikey); - if (!CheckSignatureEncoding(vchSig, flags)) { + if (!CheckSignatureEncoding(vchSig, flags, serror)) { + // serror is set return false; } @@ -827,9 +881,9 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, un // so optionally verify it is exactly equal to zero prior // to removing it from the stack. if (stack.size() < 1) - return false; + return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION); if ((flags & SCRIPT_VERIFY_NULLDUMMY) && stacktop(-1).size()) - return error("CHECKMULTISIG dummy argument not null"); + return set_error(serror, SCRIPT_ERR_SIG_NULLDUMMY); popstack(stack); stack.push_back(fSuccess ? vchTrue : vchFalse); @@ -839,29 +893,29 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, un if (fSuccess) popstack(stack); else - return false; + return set_error(serror, SCRIPT_ERR_CHECKMULTISIGVERIFY); } } break; default: - return false; + return set_error(serror, SCRIPT_ERR_BAD_OPCODE); } // Size limits if (stack.size() + altstack.size() > 1000) - return false; + return set_error(serror, SCRIPT_ERR_STACK_SIZE); } } catch (...) { - return false; + return set_error(serror, SCRIPT_ERR_UNKNOWN_ERROR); } if (!vfExec.empty()) - return false; + return set_error(serror, SCRIPT_ERR_UNBALANCED_CONDITIONAL); - return true; + return set_success(serror); } namespace { @@ -966,14 +1020,14 @@ public: uint256 SignatureHash(const CScript& scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType) { if (nIn >= txTo.vin.size()) { - LogPrintf("ERROR: SignatureHash() : nIn=%d out of range\n", nIn); + // nIn out of range return 1; } // Check for invalid use of SIGHASH_SINGLE if ((nHashType & 0x1f) == SIGHASH_SINGLE) { if (nIn >= txTo.vout.size()) { - LogPrintf("ERROR: SignatureHash() : nOut=%d out of range\n", nIn); + // nOut out of range return 1; } } @@ -1013,30 +1067,35 @@ bool SignatureChecker::CheckSig(const vector<unsigned char>& vchSigIn, const vec return true; } -bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, unsigned int flags, const BaseSignatureChecker& checker) +bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, unsigned int flags, const BaseSignatureChecker& checker, ScriptError* serror) { + set_error(serror, SCRIPT_ERR_UNKNOWN_ERROR); + if ((flags & SCRIPT_VERIFY_SIGPUSHONLY) != 0 && !scriptSig.IsPushOnly()) { - return false; + return set_error(serror, SCRIPT_ERR_SIG_PUSHONLY); } vector<vector<unsigned char> > stack, stackCopy; - if (!EvalScript(stack, scriptSig, flags, checker)) + if (!EvalScript(stack, scriptSig, flags, checker, serror)) + // serror is set return false; if (flags & SCRIPT_VERIFY_P2SH) stackCopy = stack; - if (!EvalScript(stack, scriptPubKey, flags, checker)) + if (!EvalScript(stack, scriptPubKey, flags, checker, serror)) + // serror is set return false; if (stack.empty()) - return false; + return set_error(serror, SCRIPT_ERR_EVAL_FALSE); if (CastToBool(stack.back()) == false) - return false; + return set_error(serror, SCRIPT_ERR_EVAL_FALSE); // Additional validation for spend-to-script-hash transactions: if ((flags & SCRIPT_VERIFY_P2SH) && scriptPubKey.IsPayToScriptHash()) { - if (!scriptSig.IsPushOnly()) // scriptSig must be literals-only - return false; // or validation fails + // scriptSig must be literals-only or validation fails + if (!scriptSig.IsPushOnly()) + return set_error(serror, SCRIPT_ERR_SIG_PUSHONLY); // stackCopy cannot be empty here, because if it was the // P2SH HASH <> EQUAL scriptPubKey would be evaluated with @@ -1047,12 +1106,16 @@ bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, unsigne CScript pubKey2(pubKeySerialized.begin(), pubKeySerialized.end()); popstack(stackCopy); - if (!EvalScript(stackCopy, pubKey2, flags, checker)) + if (!EvalScript(stackCopy, pubKey2, flags, checker, serror)) + // serror is set return false; if (stackCopy.empty()) - return false; - return CastToBool(stackCopy.back()); + return set_error(serror, SCRIPT_ERR_EVAL_FALSE); + if (!CastToBool(stackCopy.back())) + return set_error(serror, SCRIPT_ERR_EVAL_FALSE); + else + return set_success(serror); } - return true; + return set_success(serror); } diff --git a/src/script/interpreter.h b/src/script/interpreter.h index ed899fc411..14cccc558f 100644 --- a/src/script/interpreter.h +++ b/src/script/interpreter.h @@ -6,6 +6,8 @@ #ifndef BITCOIN_SCRIPT_INTERPRETER_H #define BITCOIN_SCRIPT_INTERPRETER_H +#include "script_error.h" + #include <vector> #include <stdint.h> #include <string> @@ -85,7 +87,7 @@ public: bool CheckSig(const std::vector<unsigned char>& scriptSig, const std::vector<unsigned char>& vchPubKey, const CScript& scriptCode) const; }; -bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript& script, unsigned int flags, const BaseSignatureChecker& checker); -bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, unsigned int flags, const BaseSignatureChecker& checker); +bool EvalScript(std::vector<std::vector<unsigned char> >& 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); #endif // BITCOIN_SCRIPT_INTERPRETER_H diff --git a/src/script/script_error.cpp b/src/script/script_error.cpp new file mode 100644 index 0000000000..4a3df268ec --- /dev/null +++ b/src/script/script_error.cpp @@ -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. + +#include "script_error.h" + +const char* ScriptErrorString(const ScriptError serror) +{ + switch (serror) + { + case SCRIPT_ERR_OK: + return "No error"; + case SCRIPT_ERR_EVAL_FALSE: + return "Script evaluated without error but finished with a false/empty top stack element"; + case SCRIPT_ERR_VERIFY: + return "Script failed an OP_VERIFY operation"; + case SCRIPT_ERR_EQUALVERIFY: + return "Script failed an OP_EQUALVERIFY operation"; + case SCRIPT_ERR_CHECKMULTISIGVERIFY: + return "Script failed an OP_CHECKMULTISIGVERIFY operation"; + case SCRIPT_ERR_CHECKSIGVERIFY: + return "Script failed an OP_CHECKSIGVERIFY operation"; + case SCRIPT_ERR_NUMEQUALVERIFY: + return "Script failed an OP_NUMEQUALVERIFY operation"; + case SCRIPT_ERR_SCRIPT_SIZE: + return "Script is too big"; + case SCRIPT_ERR_PUSH_SIZE: + return "Push value size limit exceeded"; + case SCRIPT_ERR_OP_COUNT: + return "Operation limit exceeded"; + case SCRIPT_ERR_STACK_SIZE: + return "Stack size limit exceeded"; + case SCRIPT_ERR_SIG_COUNT: + return "Signature count negative or greater than pubkey count"; + case SCRIPT_ERR_PUBKEY_COUNT: + return "Pubkey count negative or limit exceeded"; + case SCRIPT_ERR_BAD_OPCODE: + return "Opcode missing or not understood"; + case SCRIPT_ERR_DISABLED_OPCODE: + return "Attempted to use a disabled opcode"; + case SCRIPT_ERR_INVALID_STACK_OPERATION: + return "Operation not valid with the current stack size"; + case SCRIPT_ERR_INVALID_ALTSTACK_OPERATION: + return "Operation not valid with the current altstack size"; + case SCRIPT_ERR_OP_RETURN: + return "OP_RETURN was encountered"; + case SCRIPT_ERR_UNBALANCED_CONDITIONAL: + return "Invalid OP_IF construction"; + case SCRIPT_ERR_SIG_HASHTYPE: + return "Signature hash type missing or not understood"; + case SCRIPT_ERR_SIG_DER: + return "Non-canonical DER signature"; + case SCRIPT_ERR_MINIMALDATA: + return "Data push larger than necessary"; + case SCRIPT_ERR_SIG_PUSHONLY: + return "Only non-push operators allowed in signatures"; + case SCRIPT_ERR_SIG_HIGH_S: + return "Non-canonical signature: S value is unnecessarily high"; + case SCRIPT_ERR_SIG_NULLDUMMY: + return "Dummy CHECKMULTISIG argument must be zero"; + case SCRIPT_ERR_UNKNOWN_ERROR: + case SCRIPT_ERR_ERROR_COUNT: + default: break; + } + return "unknown error"; +} diff --git a/src/script/script_error.h b/src/script/script_error.h new file mode 100644 index 0000000000..ae6626b257 --- /dev/null +++ b/src/script/script_error.h @@ -0,0 +1,53 @@ +// 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_SCRIPT_ERROR_H +#define BITCOIN_SCRIPT_ERROR_H + +typedef enum ScriptError_t +{ + SCRIPT_ERR_OK = 0, + SCRIPT_ERR_UNKNOWN_ERROR, + SCRIPT_ERR_EVAL_FALSE, + SCRIPT_ERR_OP_RETURN, + + /* Max sizes */ + SCRIPT_ERR_SCRIPT_SIZE, + SCRIPT_ERR_PUSH_SIZE, + SCRIPT_ERR_OP_COUNT, + SCRIPT_ERR_STACK_SIZE, + SCRIPT_ERR_SIG_COUNT, + SCRIPT_ERR_PUBKEY_COUNT, + + /* Failed verify operations */ + SCRIPT_ERR_VERIFY, + SCRIPT_ERR_EQUALVERIFY, + SCRIPT_ERR_CHECKMULTISIGVERIFY, + SCRIPT_ERR_CHECKSIGVERIFY, + SCRIPT_ERR_NUMEQUALVERIFY, + + /* Logical/Format/Canonical errors */ + SCRIPT_ERR_BAD_OPCODE, + SCRIPT_ERR_DISABLED_OPCODE, + SCRIPT_ERR_INVALID_STACK_OPERATION, + SCRIPT_ERR_INVALID_ALTSTACK_OPERATION, + SCRIPT_ERR_UNBALANCED_CONDITIONAL, + + /* BIP62 */ + SCRIPT_ERR_SIG_HASHTYPE, + SCRIPT_ERR_SIG_DER, + SCRIPT_ERR_MINIMALDATA, + SCRIPT_ERR_SIG_PUSHONLY, + SCRIPT_ERR_SIG_HIGH_S, + SCRIPT_ERR_SIG_NULLDUMMY, + + SCRIPT_ERR_ERROR_COUNT +} ScriptError; + +#define SCRIPT_ERR_LAST SCRIPT_ERR_ERROR_COUNT + +const char* ScriptErrorString(const ScriptError error); + +#endif // BITCOIN_SCRIPT_ERROR_H diff --git a/src/test/multisig_tests.cpp b/src/test/multisig_tests.cpp index e9fc86779a..8d06caa147 100644 --- a/src/test/multisig_tests.cpp +++ b/src/test/multisig_tests.cpp @@ -6,6 +6,7 @@ #include "keystore.h" #include "main.h" #include "script/script.h" +#include "script/script_error.h" #include "script/interpreter.h" #include "script/sign.h" #include "uint256.h" @@ -46,6 +47,7 @@ BOOST_AUTO_TEST_CASE(multisig_verify) { unsigned int flags = SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_STRICTENC; + ScriptError err; CKey key[4]; for (int i = 0; i < 4; i++) key[i].MakeNewKey(true); @@ -82,19 +84,22 @@ BOOST_AUTO_TEST_CASE(multisig_verify) keys.clear(); keys += key[0],key[1]; // magic operator+= from boost.assign s = sign_multisig(a_and_b, keys, txTo[0], 0); - BOOST_CHECK(VerifyScript(s, a_and_b, flags, SignatureChecker(txTo[0], 0))); + BOOST_CHECK(VerifyScript(s, a_and_b, flags, SignatureChecker(txTo[0], 0), &err)); + BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_OK, ScriptErrorString(err)); for (int i = 0; i < 4; i++) { keys.clear(); keys += key[i]; s = sign_multisig(a_and_b, keys, txTo[0], 0); - BOOST_CHECK_MESSAGE(!VerifyScript(s, a_and_b, flags, SignatureChecker(txTo[0], 0)), strprintf("a&b 1: %d", i)); + BOOST_CHECK_MESSAGE(!VerifyScript(s, a_and_b, flags, SignatureChecker(txTo[0], 0), &err), strprintf("a&b 1: %d", i)); + BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_INVALID_STACK_OPERATION, ScriptErrorString(err)); keys.clear(); keys += key[1],key[i]; s = sign_multisig(a_and_b, keys, txTo[0], 0); - BOOST_CHECK_MESSAGE(!VerifyScript(s, a_and_b, flags, SignatureChecker(txTo[0], 0)), strprintf("a&b 2: %d", i)); + BOOST_CHECK_MESSAGE(!VerifyScript(s, a_and_b, flags, SignatureChecker(txTo[0], 0), &err), strprintf("a&b 2: %d", i)); + BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_EVAL_FALSE, ScriptErrorString(err)); } // Test a OR b: @@ -104,16 +109,24 @@ BOOST_AUTO_TEST_CASE(multisig_verify) keys += key[i]; s = sign_multisig(a_or_b, keys, txTo[1], 0); if (i == 0 || i == 1) - BOOST_CHECK_MESSAGE(VerifyScript(s, a_or_b, flags, SignatureChecker(txTo[1], 0)), strprintf("a|b: %d", i)); + { + BOOST_CHECK_MESSAGE(VerifyScript(s, a_or_b, flags, SignatureChecker(txTo[1], 0), &err), strprintf("a|b: %d", i)); + BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_OK, ScriptErrorString(err)); + } else - BOOST_CHECK_MESSAGE(!VerifyScript(s, a_or_b, flags, SignatureChecker(txTo[1], 0)), strprintf("a|b: %d", i)); + { + BOOST_CHECK_MESSAGE(!VerifyScript(s, a_or_b, flags, SignatureChecker(txTo[1], 0), &err), strprintf("a|b: %d", i)); + BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_EVAL_FALSE, ScriptErrorString(err)); + } } s.clear(); s << OP_0 << OP_0; - BOOST_CHECK(!VerifyScript(s, a_or_b, flags, SignatureChecker(txTo[1], 0))); + BOOST_CHECK(!VerifyScript(s, a_or_b, flags, SignatureChecker(txTo[1], 0), &err)); + BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_SIG_DER, ScriptErrorString(err)); s.clear(); s << OP_0 << OP_1; - BOOST_CHECK(!VerifyScript(s, a_or_b, flags, SignatureChecker(txTo[1], 0))); + BOOST_CHECK(!VerifyScript(s, a_or_b, flags, SignatureChecker(txTo[1], 0), &err)); + BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_SIG_DER, ScriptErrorString(err)); for (int i = 0; i < 4; i++) @@ -123,9 +136,15 @@ BOOST_AUTO_TEST_CASE(multisig_verify) keys += key[i],key[j]; s = sign_multisig(escrow, keys, txTo[2], 0); if (i < j && i < 3 && j < 3) - BOOST_CHECK_MESSAGE(VerifyScript(s, escrow, flags, SignatureChecker(txTo[2], 0)), strprintf("escrow 1: %d %d", i, j)); + { + BOOST_CHECK_MESSAGE(VerifyScript(s, escrow, flags, SignatureChecker(txTo[2], 0), &err), strprintf("escrow 1: %d %d", i, j)); + BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_OK, ScriptErrorString(err)); + } else - BOOST_CHECK_MESSAGE(!VerifyScript(s, escrow, flags, SignatureChecker(txTo[2], 0)), strprintf("escrow 2: %d %d", i, j)); + { + BOOST_CHECK_MESSAGE(!VerifyScript(s, escrow, flags, SignatureChecker(txTo[2], 0), &err), strprintf("escrow 2: %d %d", i, j)); + BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_EVAL_FALSE, ScriptErrorString(err)); + } } } diff --git a/src/test/script_P2SH_tests.cpp b/src/test/script_P2SH_tests.cpp index fcab652783..a969eefa05 100644 --- a/src/test/script_P2SH_tests.cpp +++ b/src/test/script_P2SH_tests.cpp @@ -6,6 +6,7 @@ #include "keystore.h" #include "main.h" #include "script/script.h" +#include "script/script_error.h" #include "script/sign.h" #ifdef ENABLE_WALLET @@ -27,7 +28,7 @@ Serialize(const CScript& s) } static bool -Verify(const CScript& scriptSig, const CScript& scriptPubKey, bool fStrict) +Verify(const CScript& scriptSig, const CScript& scriptPubKey, bool fStrict, ScriptError& err) { // Create dummy to/from transactions: CMutableTransaction txFrom; @@ -42,7 +43,7 @@ Verify(const CScript& scriptSig, const CScript& scriptPubKey, bool fStrict) txTo.vin[0].scriptSig = scriptSig; txTo.vout[0].nValue = 1; - return VerifyScript(scriptSig, scriptPubKey, fStrict ? SCRIPT_VERIFY_P2SH : SCRIPT_VERIFY_NONE, SignatureChecker(txTo, 0)); + return VerifyScript(scriptSig, scriptPubKey, fStrict ? SCRIPT_VERIFY_P2SH : SCRIPT_VERIFY_NONE, SignatureChecker(txTo, 0), &err); } @@ -124,6 +125,7 @@ BOOST_AUTO_TEST_CASE(sign) BOOST_AUTO_TEST_CASE(norecurse) { + ScriptError err; // Make sure only the outer pay-to-script-hash does the // extra-validation thing: CScript invalidAsScript; @@ -135,7 +137,8 @@ BOOST_AUTO_TEST_CASE(norecurse) scriptSig << Serialize(invalidAsScript); // Should not verify, because it will try to execute OP_INVALIDOPCODE - BOOST_CHECK(!Verify(scriptSig, p2sh, true)); + BOOST_CHECK(!Verify(scriptSig, p2sh, true, err)); + BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_BAD_OPCODE, ScriptErrorString(err)); // Try to recur, and verification should succeed because // the inner HASH160 <> EQUAL should only check the hash: @@ -143,7 +146,8 @@ BOOST_AUTO_TEST_CASE(norecurse) CScript scriptSig2; scriptSig2 << Serialize(invalidAsScript) << Serialize(p2sh); - BOOST_CHECK(Verify(scriptSig2, p2sh2, true)); + BOOST_CHECK(Verify(scriptSig2, p2sh2, true, err)); + BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_OK, ScriptErrorString(err)); } BOOST_AUTO_TEST_CASE(set) @@ -238,6 +242,7 @@ BOOST_AUTO_TEST_CASE(switchover) { // Test switch over code CScript notValid; + ScriptError err; notValid << OP_11 << OP_12 << OP_EQUALVERIFY; CScript scriptSig; scriptSig << Serialize(notValid); @@ -246,9 +251,11 @@ BOOST_AUTO_TEST_CASE(switchover) // Validation should succeed under old rules (hash is correct): - BOOST_CHECK(Verify(scriptSig, fund, false)); + BOOST_CHECK(Verify(scriptSig, fund, false, err)); + BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_OK, ScriptErrorString(err)); // Fail under new: - BOOST_CHECK(!Verify(scriptSig, fund, true)); + BOOST_CHECK(!Verify(scriptSig, fund, true, err)); + BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_EQUALVERIFY, ScriptErrorString(err)); } BOOST_AUTO_TEST_CASE(AreInputsStandard) diff --git a/src/test/script_tests.cpp b/src/test/script_tests.cpp index cff1664a1e..ede13b23c2 100644 --- a/src/test/script_tests.cpp +++ b/src/test/script_tests.cpp @@ -10,6 +10,7 @@ #include "keystore.h" #include "main.h" #include "script/script.h" +#include "script/script_error.h" #include "script/sign.h" #include "util.h" @@ -92,7 +93,9 @@ CMutableTransaction BuildSpendingTransaction(const CScript& scriptSig, const CMu void DoTest(const CScript& scriptPubKey, const CScript& scriptSig, int flags, bool expect, const std::string& message) { - BOOST_CHECK_MESSAGE(VerifyScript(scriptSig, scriptPubKey, flags, SignatureChecker(BuildSpendingTransaction(scriptSig, BuildCreditingTransaction(scriptPubKey)), 0)) == expect, message); + ScriptError err; + BOOST_CHECK_MESSAGE(VerifyScript(scriptSig, scriptPubKey, flags, SignatureChecker(BuildSpendingTransaction(scriptSig, BuildCreditingTransaction(scriptPubKey)), 0), &err) == expect, message); + BOOST_CHECK_MESSAGE(expect == (err == SCRIPT_ERR_OK), std::string(ScriptErrorString(err)) + ": " + message); } void static NegateSignatureS(std::vector<unsigned char>& vchSig) { @@ -590,20 +593,25 @@ BOOST_AUTO_TEST_CASE(script_PushData) static const unsigned char pushdata2[] = { OP_PUSHDATA2, 1, 0, 0x5a }; static const unsigned char pushdata4[] = { OP_PUSHDATA4, 1, 0, 0, 0, 0x5a }; + ScriptError err; vector<vector<unsigned char> > directStack; - BOOST_CHECK(EvalScript(directStack, CScript(&direct[0], &direct[sizeof(direct)]), true, BaseSignatureChecker())); + BOOST_CHECK(EvalScript(directStack, CScript(&direct[0], &direct[sizeof(direct)]), true, BaseSignatureChecker(), &err)); + BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_OK, ScriptErrorString(err)); vector<vector<unsigned char> > pushdata1Stack; - BOOST_CHECK(EvalScript(pushdata1Stack, CScript(&pushdata1[0], &pushdata1[sizeof(pushdata1)]), true, BaseSignatureChecker())); + BOOST_CHECK(EvalScript(pushdata1Stack, CScript(&pushdata1[0], &pushdata1[sizeof(pushdata1)]), true, BaseSignatureChecker(), &err)); BOOST_CHECK(pushdata1Stack == directStack); + BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_OK, ScriptErrorString(err)); vector<vector<unsigned char> > pushdata2Stack; - BOOST_CHECK(EvalScript(pushdata2Stack, CScript(&pushdata2[0], &pushdata2[sizeof(pushdata2)]), true, BaseSignatureChecker())); + BOOST_CHECK(EvalScript(pushdata2Stack, CScript(&pushdata2[0], &pushdata2[sizeof(pushdata2)]), true, BaseSignatureChecker(), &err)); BOOST_CHECK(pushdata2Stack == directStack); + BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_OK, ScriptErrorString(err)); vector<vector<unsigned char> > pushdata4Stack; - BOOST_CHECK(EvalScript(pushdata4Stack, CScript(&pushdata4[0], &pushdata4[sizeof(pushdata4)]), true, BaseSignatureChecker())); + BOOST_CHECK(EvalScript(pushdata4Stack, CScript(&pushdata4[0], &pushdata4[sizeof(pushdata4)]), true, BaseSignatureChecker(), &err)); BOOST_CHECK(pushdata4Stack == directStack); + BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_OK, ScriptErrorString(err)); } CScript @@ -640,6 +648,7 @@ sign_multisig(CScript scriptPubKey, const CKey &key, CTransaction transaction) BOOST_AUTO_TEST_CASE(script_CHECKMULTISIG12) { + ScriptError err; CKey key1, key2, key3; key1.MakeNewKey(true); key2.MakeNewKey(false); @@ -652,19 +661,24 @@ BOOST_AUTO_TEST_CASE(script_CHECKMULTISIG12) CMutableTransaction txTo12 = BuildSpendingTransaction(CScript(), txFrom12); CScript goodsig1 = sign_multisig(scriptPubKey12, key1, txTo12); - BOOST_CHECK(VerifyScript(goodsig1, scriptPubKey12, flags, SignatureChecker(txTo12, 0))); + BOOST_CHECK(VerifyScript(goodsig1, scriptPubKey12, flags, SignatureChecker(txTo12, 0), &err)); + BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_OK, ScriptErrorString(err)); txTo12.vout[0].nValue = 2; - BOOST_CHECK(!VerifyScript(goodsig1, scriptPubKey12, flags, SignatureChecker(txTo12, 0))); + BOOST_CHECK(!VerifyScript(goodsig1, scriptPubKey12, flags, SignatureChecker(txTo12, 0), &err)); + BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_EVAL_FALSE, ScriptErrorString(err)); CScript goodsig2 = sign_multisig(scriptPubKey12, key2, txTo12); - BOOST_CHECK(VerifyScript(goodsig2, scriptPubKey12, flags, SignatureChecker(txTo12, 0))); + BOOST_CHECK(VerifyScript(goodsig2, scriptPubKey12, flags, SignatureChecker(txTo12, 0), &err)); + BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_OK, ScriptErrorString(err)); CScript badsig1 = sign_multisig(scriptPubKey12, key3, txTo12); - BOOST_CHECK(!VerifyScript(badsig1, scriptPubKey12, flags, SignatureChecker(txTo12, 0))); + BOOST_CHECK(!VerifyScript(badsig1, scriptPubKey12, flags, SignatureChecker(txTo12, 0), &err)); + BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_EVAL_FALSE, ScriptErrorString(err)); } BOOST_AUTO_TEST_CASE(script_CHECKMULTISIG23) { + ScriptError err; CKey key1, key2, key3, key4; key1.MakeNewKey(true); key2.MakeNewKey(false); @@ -680,46 +694,55 @@ BOOST_AUTO_TEST_CASE(script_CHECKMULTISIG23) std::vector<CKey> keys; keys.push_back(key1); keys.push_back(key2); CScript goodsig1 = sign_multisig(scriptPubKey23, keys, txTo23); - BOOST_CHECK(VerifyScript(goodsig1, scriptPubKey23, flags, SignatureChecker(txTo23, 0))); + BOOST_CHECK(VerifyScript(goodsig1, scriptPubKey23, flags, SignatureChecker(txTo23, 0), &err)); + BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_OK, ScriptErrorString(err)); keys.clear(); keys.push_back(key1); keys.push_back(key3); CScript goodsig2 = sign_multisig(scriptPubKey23, keys, txTo23); - BOOST_CHECK(VerifyScript(goodsig2, scriptPubKey23, flags, SignatureChecker(txTo23, 0))); + BOOST_CHECK(VerifyScript(goodsig2, scriptPubKey23, flags, SignatureChecker(txTo23, 0), &err)); + BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_OK, ScriptErrorString(err)); keys.clear(); keys.push_back(key2); keys.push_back(key3); CScript goodsig3 = sign_multisig(scriptPubKey23, keys, txTo23); - BOOST_CHECK(VerifyScript(goodsig3, scriptPubKey23, flags, SignatureChecker(txTo23, 0))); + BOOST_CHECK(VerifyScript(goodsig3, scriptPubKey23, flags, SignatureChecker(txTo23, 0), &err)); + BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_OK, ScriptErrorString(err)); keys.clear(); keys.push_back(key2); keys.push_back(key2); // Can't re-use sig CScript badsig1 = sign_multisig(scriptPubKey23, keys, txTo23); - BOOST_CHECK(!VerifyScript(badsig1, scriptPubKey23, flags, SignatureChecker(txTo23, 0))); + BOOST_CHECK(!VerifyScript(badsig1, scriptPubKey23, flags, SignatureChecker(txTo23, 0), &err)); + BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_EVAL_FALSE, ScriptErrorString(err)); keys.clear(); keys.push_back(key2); keys.push_back(key1); // sigs must be in correct order CScript badsig2 = sign_multisig(scriptPubKey23, keys, txTo23); - BOOST_CHECK(!VerifyScript(badsig2, scriptPubKey23, flags, SignatureChecker(txTo23, 0))); + BOOST_CHECK(!VerifyScript(badsig2, scriptPubKey23, flags, SignatureChecker(txTo23, 0), &err)); + BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_EVAL_FALSE, ScriptErrorString(err)); keys.clear(); keys.push_back(key3); keys.push_back(key2); // sigs must be in correct order CScript badsig3 = sign_multisig(scriptPubKey23, keys, txTo23); - BOOST_CHECK(!VerifyScript(badsig3, scriptPubKey23, flags, SignatureChecker(txTo23, 0))); + BOOST_CHECK(!VerifyScript(badsig3, scriptPubKey23, flags, SignatureChecker(txTo23, 0), &err)); + BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_EVAL_FALSE, ScriptErrorString(err)); keys.clear(); keys.push_back(key4); keys.push_back(key2); // sigs must match pubkeys CScript badsig4 = sign_multisig(scriptPubKey23, keys, txTo23); - BOOST_CHECK(!VerifyScript(badsig4, scriptPubKey23, flags, SignatureChecker(txTo23, 0))); + BOOST_CHECK(!VerifyScript(badsig4, scriptPubKey23, flags, SignatureChecker(txTo23, 0), &err)); + BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_EVAL_FALSE, ScriptErrorString(err)); keys.clear(); keys.push_back(key1); keys.push_back(key4); // sigs must match pubkeys CScript badsig5 = sign_multisig(scriptPubKey23, keys, txTo23); - BOOST_CHECK(!VerifyScript(badsig5, scriptPubKey23, flags, SignatureChecker(txTo23, 0))); + BOOST_CHECK(!VerifyScript(badsig5, scriptPubKey23, flags, SignatureChecker(txTo23, 0), &err)); + BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_EVAL_FALSE, ScriptErrorString(err)); keys.clear(); // Must have signatures CScript badsig6 = sign_multisig(scriptPubKey23, keys, txTo23); - BOOST_CHECK(!VerifyScript(badsig6, scriptPubKey23, flags, SignatureChecker(txTo23, 0))); + BOOST_CHECK(!VerifyScript(badsig6, scriptPubKey23, flags, SignatureChecker(txTo23, 0), &err)); + BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_INVALID_STACK_OPERATION, ScriptErrorString(err)); } BOOST_AUTO_TEST_CASE(script_combineSigs) @@ -833,11 +856,13 @@ BOOST_AUTO_TEST_CASE(script_combineSigs) BOOST_AUTO_TEST_CASE(script_standard_push) { + ScriptError err; for (int i=0; i<67000; i++) { CScript script; script << i; BOOST_CHECK_MESSAGE(script.IsPushOnly(), "Number " << i << " is not pure push."); - BOOST_CHECK_MESSAGE(VerifyScript(script, CScript() << OP_1, SCRIPT_VERIFY_MINIMALDATA, BaseSignatureChecker()), "Number " << i << " push is not minimal data."); + BOOST_CHECK_MESSAGE(VerifyScript(script, CScript() << OP_1, SCRIPT_VERIFY_MINIMALDATA, BaseSignatureChecker(), &err), "Number " << i << " push is not minimal data."); + BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_OK, ScriptErrorString(err)); } for (unsigned int i=0; i<=MAX_SCRIPT_ELEMENT_SIZE; i++) { @@ -845,7 +870,8 @@ BOOST_AUTO_TEST_CASE(script_standard_push) CScript script; script << data; BOOST_CHECK_MESSAGE(script.IsPushOnly(), "Length " << i << " is not pure push."); - BOOST_CHECK_MESSAGE(VerifyScript(script, CScript() << OP_1, SCRIPT_VERIFY_MINIMALDATA, BaseSignatureChecker()), "Length " << i << " push is not minimal data."); + BOOST_CHECK_MESSAGE(VerifyScript(script, CScript() << OP_1, SCRIPT_VERIFY_MINIMALDATA, BaseSignatureChecker(), &err), "Length " << i << " push is not minimal data."); + BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_OK, ScriptErrorString(err)); } } diff --git a/src/test/transaction_tests.cpp b/src/test/transaction_tests.cpp index d4c9b1a0ea..bf3a60c04f 100644 --- a/src/test/transaction_tests.cpp +++ b/src/test/transaction_tests.cpp @@ -10,6 +10,7 @@ #include "keystore.h" #include "main.h" #include "script/script.h" +#include "script/script_error.h" #include "core_io.h" #include <map> @@ -86,6 +87,7 @@ BOOST_AUTO_TEST_CASE(tx_valid) // verifyFlags is a comma separated list of script verification flags to apply, or "NONE" Array tests = read_json(std::string(json_tests::tx_valid, json_tests::tx_valid + sizeof(json_tests::tx_valid))); + ScriptError err; BOOST_FOREACH(Value& tv, tests) { Array test = tv.get_array(); @@ -142,8 +144,9 @@ BOOST_AUTO_TEST_CASE(tx_valid) unsigned int verify_flags = ParseScriptFlags(test[2].get_str()); BOOST_CHECK_MESSAGE(VerifyScript(tx.vin[i].scriptSig, mapprevOutScriptPubKeys[tx.vin[i].prevout], - verify_flags, SignatureChecker(tx, i)), + verify_flags, SignatureChecker(tx, i), &err), strTest); + BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_OK, ScriptErrorString(err)); } } } @@ -160,6 +163,7 @@ BOOST_AUTO_TEST_CASE(tx_invalid) // verifyFlags is a comma separated list of script verification flags to apply, or "NONE" Array tests = read_json(std::string(json_tests::tx_invalid, json_tests::tx_invalid + sizeof(json_tests::tx_invalid))); + ScriptError err; BOOST_FOREACH(Value& tv, tests) { Array test = tv.get_array(); @@ -215,10 +219,10 @@ BOOST_AUTO_TEST_CASE(tx_invalid) unsigned int verify_flags = ParseScriptFlags(test[2].get_str()); fValid = VerifyScript(tx.vin[i].scriptSig, mapprevOutScriptPubKeys[tx.vin[i].prevout], - verify_flags, SignatureChecker(tx, i)); + verify_flags, SignatureChecker(tx, i), &err); } - BOOST_CHECK_MESSAGE(!fValid, strTest); + BOOST_CHECK_MESSAGE(err != SCRIPT_ERR_OK, ScriptErrorString(err)); } } } diff --git a/src/txmempool.cpp b/src/txmempool.cpp index b5070d5104..e13f1cc350 100644 --- a/src/txmempool.cpp +++ b/src/txmempool.cpp @@ -1,6 +1,6 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2013 The Bitcoin developers -// Distributed under the MIT/X11 software license, see the accompanying +// 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 "txmempool.h" @@ -45,9 +45,9 @@ CTxMemPoolEntry::GetPriority(unsigned int currentHeight) const return dResult; } -// -// Keep track of fee/priority for transactions confirmed within N blocks -// +/** + * Keep track of fee/priority for transactions confirmed within N blocks + */ class CBlockAverage { private: @@ -86,8 +86,10 @@ public: return prioritySamples.size(); } - // Used as belt-and-suspenders check when reading to detect - // file corruption + /** + * Used as belt-and-suspenders check when reading to detect + * file corruption + */ bool AreSane(const std::vector<CFeeRate>& vecFee, const CFeeRate& minRelayFee) { BOOST_FOREACH(CFeeRate fee, vecFee) @@ -139,16 +141,20 @@ public: class CMinerPolicyEstimator { private: - // Records observed averages transactions that confirmed within one block, two blocks, - // three blocks etc. + /** + * Records observed averages transactions that confirmed within one block, two blocks, + * three blocks etc. + */ std::vector<CBlockAverage> history; std::vector<CFeeRate> sortedFeeSamples; std::vector<double> sortedPrioritySamples; int nBestSeenHeight; - // nBlocksAgo is 0 based, i.e. transactions that confirmed in the highest seen block are - // nBlocksAgo == 0, transactions in the block before that are nBlocksAgo == 1 etc. + /** + * nBlocksAgo is 0 based, i.e. transactions that confirmed in the highest seen block are + * nBlocksAgo == 0, transactions in the block before that are nBlocksAgo == 1 etc. + */ void seenTxConfirm(const CFeeRate& feeRate, const CFeeRate& minRelayFee, double dPriority, int nBlocksAgo) { // Last entry records "everything else". @@ -248,7 +254,9 @@ public: } } - // Can return CFeeRate(0) if we don't have any data for that many blocks back. nBlocksToConfirm is 1 based. + /** + * Can return CFeeRate(0) if we don't have any data for that many blocks back. nBlocksToConfirm is 1 based. + */ CFeeRate estimateFee(int nBlocksToConfirm) { nBlocksToConfirm--; @@ -332,7 +340,7 @@ public: size_t numEntries; filein >> numEntries; if (numEntries <= 0 || numEntries > 10000) - throw runtime_error("Corrupt estimates file. Must have between 1 and 10k entires."); + throw runtime_error("Corrupt estimates file. Must have between 1 and 10k entries."); std::vector<CBlockAverage> fileHistory; @@ -462,7 +470,9 @@ void CTxMemPool::removeConflicts(const CTransaction &tx, std::list<CTransaction> } } -// Called when a block is connected. Removes from mempool and updates the miner fee estimator. +/** + * Called when a block is connected. Removes from mempool and updates the miner fee estimator. + */ void CTxMemPool::removeForBlock(const std::vector<CTransaction>& vtx, unsigned int nBlockHeight, std::list<CTransaction>& conflicts) { diff --git a/src/txmempool.h b/src/txmempool.h index 2ec80cb860..0d3c8bba6a 100644 --- a/src/txmempool.h +++ b/src/txmempool.h @@ -1,6 +1,6 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2013 The Bitcoin developers -// Distributed under the MIT/X11 software license, see the accompanying +// 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_TXMEMPOOL_H @@ -25,19 +25,19 @@ inline bool AllowFree(double dPriority) /** Fake height value used in CCoins to signify they are only in the memory pool (since 0.8) */ static const unsigned int MEMPOOL_HEIGHT = 0x7FFFFFFF; -/* +/** * CTxMemPool stores these: */ class CTxMemPoolEntry { private: CTransaction tx; - CAmount nFee; // Cached to avoid expensive parent-transaction lookups - size_t nTxSize; // ... and avoid recomputing tx size - size_t nModSize; // ... and modified size for priority - int64_t nTime; // Local time when entering the mempool - double dPriority; // Priority when entering the mempool - unsigned int nHeight; // Chain height when entering the mempool + CAmount nFee; //! Cached to avoid expensive parent-transaction lookups + size_t nTxSize; //! ... and avoid recomputing tx size + size_t nModSize; //! ... and modified size for priority + int64_t nTime; //! Local time when entering the mempool + double dPriority; //! Priority when entering the mempool + unsigned int nHeight; //! Chain height when entering the mempool public: CTxMemPoolEntry(const CTransaction& _tx, const CAmount& _nFee, @@ -68,7 +68,7 @@ public: bool IsNull() const { return (ptx == NULL && n == (uint32_t) -1); } }; -/* +/** * CTxMemPool stores valid-according-to-the-current-best-chain * transactions that may be included in the next block. * @@ -81,12 +81,12 @@ public: class CTxMemPool { private: - bool fSanityCheck; // Normally false, true if -checkmempool or -regtest + bool fSanityCheck; //! Normally false, true if -checkmempool or -regtest unsigned int nTransactionsUpdated; CMinerPolicyEstimator* minerPolicyEstimator; - CFeeRate minRelayFee; // Passed to constructor to avoid dependency on main - uint64_t totalTxSize; // sum of all mempool tx' byte sizes + CFeeRate minRelayFee; //! Passed to constructor to avoid dependency on main + uint64_t totalTxSize; //! sum of all mempool tx' byte sizes public: mutable CCriticalSection cs; @@ -97,7 +97,7 @@ public: CTxMemPool(const CFeeRate& _minRelayFee); ~CTxMemPool(); - /* + /** * If sanity-checking is turned on, check makes sure the pool is * consistent (does not contain two transactions that spend the same inputs, * all inputs are in the mapNextTx array). If sanity-checking is turned off, @@ -141,19 +141,21 @@ public: bool lookup(uint256 hash, CTransaction& result) const; - // Estimate fee rate needed to get into the next - // nBlocks + /** Estimate fee rate needed to get into the next nBlocks */ CFeeRate estimateFee(int nBlocks) const; - // Estimate priority needed to get into the next - // nBlocks + + /** Estimate priority needed to get into the next nBlocks */ double estimatePriority(int nBlocks) const; - // Write/Read estimates to disk + + /** Write/Read estimates to disk */ bool WriteFeeEstimates(CAutoFile& fileout) const; bool ReadFeeEstimates(CAutoFile& filein); }; -/** CCoinsView that brings transactions from a memorypool into view. - It does not check for spendings by memory pool transactions. */ +/** + * CCoinsView that brings transactions from a memorypool into view. + * It does not check for spendings by memory pool transactions. + */ class CCoinsViewMemPool : public CCoinsViewBacked { protected: diff --git a/src/util.cpp b/src/util.cpp index 0f5c036352..0cdf4e614d 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -1,6 +1,6 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2009-2014 The Bitcoin developers -// Distributed under the MIT/X11 software license, see the accompanying +// Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #if defined(HAVE_CONFIG_H) @@ -105,7 +105,7 @@ bool fLogTimestamps = false; bool fLogIPs = false; volatile bool fReopenDebugLog = false; -// Init OpenSSL library multithreading support +/** Init OpenSSL library multithreading support */ static CCriticalSection** ppmutexOpenSSL; void locking_callback(int mode, int i, const char* file, int line) { @@ -149,18 +149,22 @@ public: } instance_of_cinit; -// LogPrintf() has been broken a couple of times now -// by well-meaning people adding mutexes in the most straightforward way. -// It breaks because it may be called by global destructors during shutdown. -// Since the order of destruction of static/global objects is undefined, -// defining a mutex as a global object doesn't work (the mutex gets -// destroyed, and then some later destructor calls OutputDebugStringF, -// maybe indirectly, and you get a core dump at shutdown trying to lock -// the mutex). +/** + * LogPrintf() has been broken a couple of times now + * by well-meaning people adding mutexes in the most straightforward way. + * It breaks because it may be called by global destructors during shutdown. + * Since the order of destruction of static/global objects is undefined, + * defining a mutex as a global object doesn't work (the mutex gets + * destroyed, and then some later destructor calls OutputDebugStringF, + * maybe indirectly, and you get a core dump at shutdown trying to lock + * the mutex). + */ static boost::once_flag debugPrintInitFlag = BOOST_ONCE_INIT; -// We use boost::call_once() to make sure these are initialized -// in a thread-safe manner the first time called: +/** + * We use boost::call_once() to make sure these are initialized + * in a thread-safe manner the first time called: + */ static FILE* fileout = NULL; static boost::mutex* mutexDebugLog = NULL; @@ -500,9 +504,11 @@ bool RenameOver(boost::filesystem::path src, boost::filesystem::path dest) #endif /* WIN32 */ } -// Ignores exceptions thrown by Boost's create_directory if the requested directory exists. -// Specifically handles case where path p exists, but it wasn't possible for the user to -// write to the parent directory. +/** + * Ignores exceptions thrown by Boost's create_directory if the requested directory exists. + * Specifically handles case where path p exists, but it wasn't possible for the user to + * write to the parent directory. + */ bool TryCreateDirectory(const boost::filesystem::path& p) { try @@ -542,8 +548,10 @@ bool TruncateFile(FILE *file, unsigned int length) { #endif } -// this function tries to raise the file descriptor limit to the requested number. -// It returns the actual file descriptor limit (which may be more or less than nMinFD) +/** + * this function tries to raise the file descriptor limit to the requested number. + * It returns the actual file descriptor limit (which may be more or less than nMinFD) + */ int RaiseFileDescriptorLimit(int nMinFD) { #if defined(WIN32) return 2048; @@ -563,8 +571,10 @@ int RaiseFileDescriptorLimit(int nMinFD) { #endif } -// this function tries to make a particular range of a file allocated (corresponding to disk space) -// it is advisory, and the range specified in the arguments will never contain live data +/** + * this function tries to make a particular range of a file allocated (corresponding to disk space) + * it is advisory, and the range specified in the arguments will never contain live data + */ void AllocateFileRange(FILE *file, unsigned int offset, unsigned int length) { #if defined(WIN32) // Windows-specific version diff --git a/src/util.h b/src/util.h index 4b2415278b..a4aaf29f91 100644 --- a/src/util.h +++ b/src/util.h @@ -1,6 +1,6 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2009-2014 The Bitcoin developers -// Distributed under the MIT/X11 software license, see the accompanying +// Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. /** @@ -40,25 +40,26 @@ extern volatile bool fReopenDebugLog; void SetupEnvironment(); -/* Return true if log accepts specified category */ +/** Return true if log accepts specified category */ bool LogAcceptCategory(const char* category); -/* Send a string to the log output */ +/** Send a string to the log output */ int LogPrintStr(const std::string &str); #define LogPrintf(...) LogPrint(NULL, __VA_ARGS__) -/* When we switch to C++11, this can be switched to variadic templates instead +/** + * When we switch to C++11, this can be switched to variadic templates instead * of this macro-based construction (see tinyformat.h). */ #define MAKE_ERROR_AND_LOG_FUNC(n) \ - /* Print to debug.log if -debug=category switch is given OR category is NULL. */ \ + /** Print to debug.log if -debug=category switch is given OR category is NULL. */ \ template<TINYFORMAT_ARGTYPES(n)> \ static inline int LogPrint(const char* category, const char* format, TINYFORMAT_VARARGS(n)) \ { \ if(!LogAcceptCategory(category)) return 0; \ return LogPrintStr(tfm::format(format, TINYFORMAT_PASSARGS(n))); \ } \ - /* Log error and return false */ \ + /** Log error and return false */ \ template<TINYFORMAT_ARGTYPES(n)> \ static inline bool error(const char* format, TINYFORMAT_VARARGS(n)) \ { \ @@ -68,7 +69,8 @@ int LogPrintStr(const std::string &str); TINYFORMAT_FOREACH_ARGNUM(MAKE_ERROR_AND_LOG_FUNC) -/* Zero-arg versions of logging and error, these are not covered by +/** + * Zero-arg versions of logging and error, these are not covered by * TINYFORMAT_FOREACH_ARGNUM */ static inline int LogPrint(const char* category, const char* format) @@ -162,13 +164,15 @@ bool SoftSetBoolArg(const std::string& strArg, bool fValue); void SetThreadPriority(int nPriority); void RenameThread(const char* name); -// Standard wrapper for do-something-forever thread functions. -// "Forever" really means until the thread is interrupted. -// Use it like: -// new boost::thread(boost::bind(&LoopForever<void (*)()>, "dumpaddr", &DumpAddresses, 900000)); -// or maybe: -// boost::function<void()> f = boost::bind(&FunctionWithArg, argument); -// threadGroup.create_thread(boost::bind(&LoopForever<boost::function<void()> >, "nothing", f, milliseconds)); +/** + * Standard wrapper for do-something-forever thread functions. + * "Forever" really means until the thread is interrupted. + * Use it like: + * new boost::thread(boost::bind(&LoopForever<void (*)()>, "dumpaddr", &DumpAddresses, 900000)); + * or maybe: + * boost::function<void()> f = boost::bind(&FunctionWithArg, argument); + * threadGroup.create_thread(boost::bind(&LoopForever<boost::function<void()> >, "nothing", f, milliseconds)); + */ template <typename Callable> void LoopForever(const char* name, Callable func, int64_t msecs) { std::string s = strprintf("bitcoin-%s", name); @@ -196,7 +200,10 @@ template <typename Callable> void LoopForever(const char* name, Callable func, throw; } } -// .. and a wrapper that just calls func once + +/** + * .. and a wrapper that just calls func once + */ template <typename Callable> void TraceThread(const char* name, Callable func) { std::string s = strprintf("bitcoin-%s", name); diff --git a/src/utilmoneystr.cpp b/src/utilmoneystr.cpp index 267a5b845c..085adae85e 100644 --- a/src/utilmoneystr.cpp +++ b/src/utilmoneystr.cpp @@ -1,6 +1,6 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2009-2014 The Bitcoin developers -// Distributed under the MIT/X11 software license, see the accompanying +// Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include "utilmoneystr.h" diff --git a/src/utilmoneystr.h b/src/utilmoneystr.h index 65415afd3f..6a153db5fa 100644 --- a/src/utilmoneystr.h +++ b/src/utilmoneystr.h @@ -1,6 +1,6 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2009-2014 The Bitcoin developers -// Distributed under the MIT/X11 software license, see the accompanying +// Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. /** diff --git a/src/utilstrencodings.cpp b/src/utilstrencodings.cpp index 15094e5999..a961b3c5cd 100644 --- a/src/utilstrencodings.cpp +++ b/src/utilstrencodings.cpp @@ -1,6 +1,6 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2009-2014 The Bitcoin developers -// Distributed under the MIT/X11 software license, see the accompanying +// Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include "utilstrencodings.h" @@ -14,8 +14,10 @@ using namespace std; -// safeChars chosen to allow simple messages/URLs/email addresses, but avoid anything -// even possibly remotely dangerous like & or > +/** + * safeChars chosen to allow simple messages/URLs/email addresses, but avoid anything + * even possibly remotely dangerous like & or > + */ static string safeChars("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890 .,;_/:?@()"); string SanitizeString(const string& str) { diff --git a/src/utilstrencodings.h b/src/utilstrencodings.h index 0b8c1a1781..0c0171b894 100644 --- a/src/utilstrencodings.h +++ b/src/utilstrencodings.h @@ -1,6 +1,6 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2009-2014 The Bitcoin developers -// Distributed under the MIT/X11 software license, see the accompanying +// Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. /** @@ -19,7 +19,7 @@ #define UEND(a) ((unsigned char*)&((&(a))[1])) #define ARRAYLEN(array) (sizeof(array)/sizeof((array)[0])) -// This is needed because the foreach macro can't get over the comma in pair<t1, t2> +/** This is needed because the foreach macro can't get over the comma in pair<t1, t2> */ #define PAIRTYPE(t1, t2) std::pair<t1, t2> std::string SanitizeString(const std::string& str); @@ -45,7 +45,7 @@ int atoi(const std::string& str); /** * Convert string to signed 32-bit integer with strict parse error feedback. * @returns true if the entire string could be parsed as valid integer, - * false if not the entire string could be parsed or when overflow or underflow occured. + * false if not the entire string could be parsed or when overflow or underflow occurred. */ bool ParseInt32(const std::string& str, int32_t *out); @@ -74,7 +74,8 @@ inline std::string HexStr(const T& vch, bool fSpaces=false) return HexStr(vch.begin(), vch.end(), fSpaces); } -/** Format a paragraph of text to a fixed width, adding spaces for +/** + * Format a paragraph of text to a fixed width, adding spaces for * indentation to any added line. */ std::string FormatParagraph(const std::string in, size_t width=79, size_t indent=0); diff --git a/src/utiltime.cpp b/src/utiltime.cpp index 78f0342cba..9c137e8aa0 100644 --- a/src/utiltime.cpp +++ b/src/utiltime.cpp @@ -1,6 +1,6 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2009-2014 The Bitcoin developers -// Distributed under the MIT/X11 software license, see the accompanying +// Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #if defined(HAVE_CONFIG_H) @@ -14,7 +14,7 @@ using namespace std; -static int64_t nMockTime = 0; // For unit testing +static int64_t nMockTime = 0; //! For unit testing int64_t GetTime() { @@ -42,9 +42,12 @@ int64_t GetTimeMicros() void MilliSleep(int64_t n) { -// Boost's sleep_for was uninterruptable when backed by nanosleep from 1.50 -// until fixed in 1.52. Use the deprecated sleep method for the broken case. -// See: https://svn.boost.org/trac/boost/ticket/7238 + +/** + * Boost's sleep_for was uninterruptable when backed by nanosleep from 1.50 + * until fixed in 1.52. Use the deprecated sleep method for the broken case. + * See: https://svn.boost.org/trac/boost/ticket/7238 + */ #if defined(HAVE_WORKING_BOOST_SLEEP_FOR) boost::this_thread::sleep_for(boost::chrono::milliseconds(n)); #elif defined(HAVE_WORKING_BOOST_SLEEP) diff --git a/src/utiltime.h b/src/utiltime.h index 6f82e5a836..9d7d42fe47 100644 --- a/src/utiltime.h +++ b/src/utiltime.h @@ -1,6 +1,6 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2009-2014 The Bitcoin developers -// Distributed under the MIT/X11 software license, see the accompanying +// Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #ifndef BITCOIN_UTILTIME_H |