From c94ee2b4e040a2a96457eedd7b1454e5e7c820e1 Mon Sep 17 00:00:00 2001 From: Matt Corallo Date: Sat, 6 Dec 2014 14:38:47 -0800 Subject: [PATCH] Use RPC calls to check bitcoin blocks are valid and confirmed --- src/Makefile.am | 11 +++++++---- src/Makefile.test.include | 2 +- src/callrpc.cpp | 39 +++++++++++++++++++++++++++++++++++-- src/callrpc.h | 10 ++++++++-- src/policy/policy.h | 3 ++- src/rpc/client.h | 2 +- src/rpc/protocol.h | 3 ++- src/script/interpreter.cpp | 19 +++++++++++++++++- src/script/interpreter.h | 10 ++++++++++ src/script/script_error.cpp | 2 ++ src/script/script_error.h | 1 + src/support/events.cpp | 33 +++++++++++++++++++++++++++++++ 12 files changed, 122 insertions(+), 13 deletions(-) create mode 100644 src/support/events.cpp diff --git a/src/Makefile.am b/src/Makefile.am index 3086c5eee5..29351ab958 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -267,6 +267,7 @@ libbitcoin_consensus_a_SOURCES = \ arith_uint256.h \ bloom.cpp \ bloom.h \ + callrpc.cpp \ consensus/merkle.cpp \ consensus/merkle.h \ consensus/params.h \ @@ -282,6 +283,7 @@ libbitcoin_consensus_a_SOURCES = \ primitives/transaction.h \ pubkey.cpp \ pubkey.h \ + rpc/protocol.cpp \ script/bitcoinconsensus.cpp \ script/interpreter.cpp \ script/interpreter.h \ @@ -289,6 +291,7 @@ libbitcoin_consensus_a_SOURCES = \ script/script.h \ script/script_error.cpp \ script/script_error.h \ + support/events.cpp \ serialize.h \ tinyformat.h \ uint256.cpp \ @@ -325,7 +328,7 @@ libbitcoin_common_a_SOURCES = \ # util: shared between all executables. # This library *must* be included to make sure that the glibc # backward-compatibility objects and their sanity checks are linked. -libbitcoin_util_a_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) +libbitcoin_util_a_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) $(EVENT_CFLAGS) libbitcoin_util_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) libbitcoin_util_a_SOURCES = \ support/lockedpool.cpp \ @@ -389,7 +392,7 @@ bitcoind_LDADD += $(BOOST_LIBS) $(BDB_LIBS) $(SSL_LIBS) $(CRYPTO_LIBS) $(MINIUPN # bitcoin-cli binary # bitcoin_cli_SOURCES = bitcoin-cli.cpp -bitcoin_cli_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) $(EVENT_CFLAGS) +bitcoin_cli_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) bitcoin_cli_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) bitcoin_cli_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) @@ -424,7 +427,7 @@ bitcoin_tx_LDADD = \ $(LIBBITCOIN_CRYPTO) \ $(LIBSECP256K1) -bitcoin_tx_LDADD += $(BOOST_LIBS) $(CRYPTO_LIBS) +bitcoin_tx_LDADD += $(BOOST_LIBS) $(CRYPTO_LIBS) $(EVENT_LIBS) # # bitcoinconsensus library # @@ -438,7 +441,7 @@ endif libbitcoinconsensus_la_LDFLAGS = $(AM_LDFLAGS) -no-undefined -version-info 1:0:0 $(RELDFLAGS) libbitcoinconsensus_la_LIBADD = $(LIBSECP256K1) -libbitcoinconsensus_la_CPPFLAGS = $(AM_CPPFLAGS) -I$(builddir)/obj -I$(srcdir)/secp256k1/include -DBUILD_BITCOIN_INTERNAL +libbitcoinconsensus_la_CPPFLAGS = $(AM_CPPFLAGS) -I$(builddir)/obj -I$(srcdir)/secp256k1/include -DBUILD_BITCOIN_INTERNAL -DBITCOIN_SCRIPT_NO_CALLRPC libbitcoinconsensus_la_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) endif diff --git a/src/Makefile.test.include b/src/Makefile.test.include index 4d44b35bb6..e572492ceb 100644 --- a/src/Makefile.test.include +++ b/src/Makefile.test.include @@ -154,7 +154,7 @@ if ENABLE_WALLET test_test_bitcoin_LDADD += $(LIBBITCOIN_WALLET) endif -test_test_bitcoin_LDADD += $(LIBBITCOIN_CONSENSUS) $(BDB_LIBS) $(SSL_LIBS) $(CRYPTO_LIBS) $(MINIUPNPC_LIBS) +test_test_bitcoin_LDADD += $(LIBBITCOIN_CONSENSUS) $(BDB_LIBS) $(SSL_LIBS) $(CRYPTO_LIBS) $(MINIUPNPC_LIBS) $(EVENT_LIBS) test_test_bitcoin_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) -static if ENABLE_ZMQ diff --git a/src/callrpc.cpp b/src/callrpc.cpp index fd576ff472..68ad025005 100644 --- a/src/callrpc.cpp +++ b/src/callrpc.cpp @@ -76,10 +76,11 @@ static void http_error_cb(enum evhttp_request_error err, void *ctx) } #endif -UniValue CallRPC(const std::string& strMethod, const UniValue& params) +UniValue CallRPC(const std::string& strMethod, const UniValue& params, int port) { std::string host = GetArg("-rpcconnect", DEFAULT_RPCCONNECT); - int port = GetArg("-rpcport", BaseParams().RPCPort()); + if (port < 0) + port = GetArg("-rpcport", BaseParams().RPCPort()); // Obtain event base raii_event_base base = obtain_event_base(); @@ -149,3 +150,37 @@ UniValue CallRPC(const std::string& strMethod, const UniValue& params) return reply; } + +bool IsConfirmedBitcoinBlock(const uint256& genesishash, const uint256& hash, int nMinConfirmationDepth) +{ + try { + UniValue params(UniValue::VARR); + params.push_back(UniValue(0)); + UniValue reply = CallRPC("getblockhash", params, GetArg("-rpcconnectport", 18332)); + if (!find_value(reply, "error").isNull()) + return false; + UniValue result = find_value(reply, "result"); + if (!result.isStr()) + return false; + if (result.get_str() != genesishash.GetHex()) + return false; + + params = UniValue(UniValue::VARR); + params.push_back(hash.GetHex()); + reply = CallRPC("getblock", params, GetArg("-rpcconnectport", 18332)); + if (!find_value(reply, "error").isNull()) + return false; + result = find_value(reply, "result"); + if (!result.isObject()) + return false; + result = find_value(result.get_obj(), "confirmations"); + return result.isNum() && result.get_int64() >= nMinConfirmationDepth; + } catch (CConnectionFailed& e) { + LogPrintf("ERROR: Lost connection to bitcoind RPC, you will want to restart after fixing this!\n"); + return false; + } catch (...) { + LogPrintf("ERROR: Failure connecting to bitcoind RPC, you will want to restart after fixing this!\n"); + return false; + } + return true; +} diff --git a/src/callrpc.h b/src/callrpc.h index 497370f33f..297e14129a 100644 --- a/src/callrpc.h +++ b/src/callrpc.h @@ -6,10 +6,15 @@ #ifndef BITCOIN_CALLRPC_H #define BITCOIN_CALLRPC_H +#include "rpc/client.h" +#include "rpc/protocol.h" +#include "uint256.h" + #include #include -#include +//#include +#include "univalue/include/univalue.h" static const bool DEFAULT_NAMED=false; static const char DEFAULT_RPCCONNECT[] = "127.0.0.1"; @@ -29,6 +34,7 @@ public: }; -UniValue CallRPC(const std::string& strMethod, const UniValue& params); +UniValue CallRPC(const std::string& strMethod, const UniValue& params, int port=-1); +bool IsConfirmedBitcoinBlock(const uint256& genesishash, const uint256& hash, int nMinConfirmationDepth); #endif // BITCOIN_CALLRPC_H diff --git a/src/policy/policy.h b/src/policy/policy.h index 9b1323ac26..12c853ef93 100644 --- a/src/policy/policy.h +++ b/src/policy/policy.h @@ -65,7 +65,8 @@ static const unsigned int STANDARD_SCRIPT_VERIFY_FLAGS = MANDATORY_SCRIPT_VERIFY SCRIPT_VERIFY_LOW_S | SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM | - SCRIPT_VERIFY_WITNESS_PUBKEYTYPE; + SCRIPT_VERIFY_WITNESS_PUBKEYTYPE | + SCRIPT_VERIFY_INCREASE_CONFIRMATIONS_REQUIRED; /** 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; diff --git a/src/rpc/client.h b/src/rpc/client.h index e7cf035d8f..531cfb464a 100644 --- a/src/rpc/client.h +++ b/src/rpc/client.h @@ -6,7 +6,7 @@ #ifndef BITCOIN_RPCCLIENT_H #define BITCOIN_RPCCLIENT_H -#include +#include "univalue/include/univalue.h" /** Convert positional arguments to command-specific RPC representation */ UniValue RPCConvertValues(const std::string& strMethod, const std::vector& strParams); diff --git a/src/rpc/protocol.h b/src/rpc/protocol.h index 47e56e712b..28d6f84430 100644 --- a/src/rpc/protocol.h +++ b/src/rpc/protocol.h @@ -12,7 +12,8 @@ #include #include -#include +#include "univalue/include/univalue.h" +//#include //! HTTP status codes enum HTTPStatusCode diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp index 63fc6ce94e..b42e58e860 100644 --- a/src/script/interpreter.cpp +++ b/src/script/interpreter.cpp @@ -20,6 +20,11 @@ #include "streams.h" #include "uint256.h" #include "utilstrencodings.h" +#include "util.h" + +#ifndef BITCOIN_SCRIPT_NO_CALLRPC +#include "callrpc.h" +#endif using namespace std; @@ -1211,7 +1216,10 @@ bool EvalScript(vector >& stack, const CScript& script, un if (withdrawOutput.scriptPubKey != expectedWithdrawScriptPubKey) return set_error(serror, SCRIPT_ERR_WITHDRAW_VERIFY_OUTPUT); - //TODO: Check that we're spending from a valid, buried bitcoin block +#ifndef BITCOIN_SCRIPT_NO_CALLRPC + if (!GetBoolArg("-blindtrust", true) && !checker.IsConfirmedBitcoinBlock(genesishash, merkleBlock.header.GetHash(), flags & SCRIPT_VERIFY_INCREASE_CONFIRMATIONS_REQUIRED)) + return set_error(serror, SCRIPT_ERR_WITHDRAW_VERIFY_BLOCKCONFIRMED); +#endif } catch (std::exception& e) { // Probably invalid encoding of something which was deserialized return set_error(serror, SCRIPT_ERR_WITHDRAW_VERIFY_FORMAT); @@ -1582,6 +1590,15 @@ CAmount TransactionSignatureChecker::GetValueInPrevIn() const return amountPreviousInput; } +bool TransactionSignatureChecker::IsConfirmedBitcoinBlock(const uint256& genesishash, const uint256& hash, bool fConservativeConfirmationRequirements) const +{ +#ifndef BITCOIN_SCRIPT_NO_CALLRPC + return ::IsConfirmedBitcoinBlock(genesishash, hash, fConservativeConfirmationRequirements ? 10 : 8); +#else + return true; +#endif +} + static bool VerifyWitnessProgram(const CScriptWitness& witness, int witversion, const std::vector& program, unsigned int flags, const BaseSignatureChecker& checker, ScriptError* serror) { vector > stack; diff --git a/src/script/interpreter.h b/src/script/interpreter.h index 175354f98b..4d121be393 100644 --- a/src/script/interpreter.h +++ b/src/script/interpreter.h @@ -111,6 +111,10 @@ enum // Execute sidechain-related opcodes instead of treating them as NOPs // SCRIPT_VERIFY_WITHDRAW = (1U << 16), + + // Dirty hack to require a higher bar of bitcoin block confirmation in mempool + // + SCRIPT_VERIFY_INCREASE_CONFIRMATIONS_REQUIRED = (1U << 17) }; bool CheckSignatureEncoding(const std::vector &vchSig, unsigned int flags, ScriptError* serror); @@ -161,6 +165,11 @@ public: return -1; } + virtual bool IsConfirmedBitcoinBlock(const uint256& genesishash, const uint256& hash, bool fConservativeConfirmationRequirements) const + { + return false; + } + virtual ~BaseSignatureChecker() {} }; @@ -203,6 +212,7 @@ public: COutPoint GetPrevOut() const; CAmount GetValueIn() const; CAmount GetValueInPrevIn() const; + bool IsConfirmedBitcoinBlock(const uint256& genesishash, const uint256& hash, bool fConservativeConfirmationRequirements) const; }; bool EvalScript(std::vector >& stack, const CScript& script, unsigned int flags, const BaseSignatureChecker& checker, SigVersion sigversion, ScriptError* error = NULL); diff --git a/src/script/script_error.cpp b/src/script/script_error.cpp index 737af82ac5..1fa38c29ef 100644 --- a/src/script/script_error.cpp +++ b/src/script/script_error.cpp @@ -95,6 +95,8 @@ const char* ScriptErrorString(const ScriptError serror) return "Withdraw proof validation failed - locking transaction misformatted"; case SCRIPT_ERR_WITHDRAW_VERIFY_OUTPUT: return "Withdraw proof validation failed - output does not match expected"; + case SCRIPT_ERR_WITHDRAW_VERIFY_BLOCKCONFIRMED: + return "Withdraw proof validation failed - lock block was not sufficiently confirmed on sending chain"; 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 b20fc42941..e5455c7b25 100644 --- a/src/script/script_error.h +++ b/src/script/script_error.h @@ -69,6 +69,7 @@ typedef enum ScriptError_t SCRIPT_ERR_WITHDRAW_VERIFY_BLOCK, SCRIPT_ERR_WITHDRAW_VERIFY_LOCKTX, SCRIPT_ERR_WITHDRAW_VERIFY_OUTPUT, + SCRIPT_ERR_WITHDRAW_VERIFY_BLOCKCONFIRMED, SCRIPT_ERR_ERROR_COUNT } ScriptError; diff --git a/src/support/events.cpp b/src/support/events.cpp new file mode 100644 index 0000000000..bc2ff86765 --- /dev/null +++ b/src/support/events.cpp @@ -0,0 +1,33 @@ +// Copyright (c) 2016 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + + +#include "events.h" + +raii_event_base obtain_event_base() { + auto result = raii_event_base(event_base_new()); + if (!result.get()) + throw std::runtime_error("cannot create event_base"); + return result; +} + +raii_event obtain_event(struct event_base* base, evutil_socket_t s, short events, event_callback_fn cb, void* arg) { + return raii_event(event_new(base, s, events, cb, arg)); +} + +raii_evhttp obtain_evhttp(struct event_base* base) { + return raii_evhttp(evhttp_new(base)); +} + +raii_evhttp_request obtain_evhttp_request(void(*cb)(struct evhttp_request *, void *), void *arg) { + return raii_evhttp_request(evhttp_request_new(cb, arg)); +} + +raii_evhttp_connection obtain_evhttp_connection_base(struct event_base* base, std::string host, uint16_t port) { + auto result = raii_evhttp_connection(evhttp_connection_base_new(base, NULL, host.c_str(), port)); + if (!result.get()) + throw std::runtime_error("create connection failed"); + return result; +} +