diff --git a/src/interfaces/chain.cpp b/src/interfaces/chain.cpp index 9d4e16f5f..dbf53ca9b 100644 --- a/src/interfaces/chain.cpp +++ b/src/interfaces/chain.cpp @@ -1,418 +1,431 @@ // Copyright (c) 2018-2019 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace interfaces { namespace { + bool FillBlock(const CBlockIndex *index, const FoundBlock &block, + UniqueLock &lock) { + if (!index) { + return false; + } + if (block.m_hash) { + *block.m_hash = index->GetBlockHash(); + } + if (block.m_height) { + *block.m_height = index->nHeight; + } + if (block.m_time) { + *block.m_time = index->GetBlockTime(); + } + if (block.m_max_time) { + *block.m_max_time = index->GetBlockTimeMax(); + } + if (block.m_mtp_time) { + *block.m_mtp_time = index->GetMedianTimePast(); + } + if (block.m_data) { + REVERSE_LOCK(lock); + if (!ReadBlockFromDisk(*block.m_data, index, + Params().GetConsensus())) { + block.m_data->SetNull(); + } + } + return true; + } + class LockImpl : public Chain::Lock, public UniqueLock { Optional getHeight() override { LockAssertion lock(::cs_main); int height = ::ChainActive().Height(); if (height >= 0) { return height; } return nullopt; } Optional getBlockHeight(const BlockHash &hash) override { LockAssertion lock(::cs_main); CBlockIndex *block = LookupBlockIndex(hash); if (block && ::ChainActive().Contains(block)) { return block->nHeight; } return nullopt; } BlockHash getBlockHash(int height) override { LockAssertion lock(::cs_main); CBlockIndex *block = ::ChainActive()[height]; assert(block != nullptr); return block->GetBlockHash(); } int64_t getBlockTime(int height) override { LockAssertion lock(::cs_main); CBlockIndex *block = ::ChainActive()[height]; assert(block != nullptr); return block->GetBlockTime(); } int64_t getBlockMedianTimePast(int height) override { LockAssertion lock(::cs_main); CBlockIndex *block = ::ChainActive()[height]; assert(block != nullptr); return block->GetMedianTimePast(); } bool haveBlockOnDisk(int height) override { LockAssertion lock(::cs_main); CBlockIndex *block = ::ChainActive()[height]; return block && (block->nStatus.hasData() != 0) && block->nTx > 0; } Optional findFirstBlockWithTimeAndHeight(int64_t time, int height, BlockHash *hash) override { LockAssertion lock(::cs_main); CBlockIndex *block = ::ChainActive().FindEarliestAtLeast(time, height); if (block) { if (hash) { *hash = block->GetBlockHash(); } return block->nHeight; } return nullopt; } Optional findPruned(int start_height, Optional stop_height) override { LockAssertion lock(::cs_main); if (::fPruneMode) { CBlockIndex *block = stop_height ? ::ChainActive()[*stop_height] : ::ChainActive().Tip(); while (block && block->nHeight >= start_height) { if (block->nStatus.hasData() == 0) { return block->nHeight; } block = block->pprev; } } return nullopt; } Optional findFork(const BlockHash &hash, Optional *height) override { LockAssertion lock(::cs_main); const CBlockIndex *block = LookupBlockIndex(hash); const CBlockIndex *fork = block ? ::ChainActive().FindFork(block) : nullptr; if (height) { if (block) { *height = block->nHeight; } else { height->reset(); } } if (fork) { return fork->nHeight; } return nullopt; } CBlockLocator getTipLocator() override { LockAssertion lock(::cs_main); return ::ChainActive().GetLocator(); } Optional findLocatorFork(const CBlockLocator &locator) override { LockAssertion lock(::cs_main); if (CBlockIndex *fork = FindForkInGlobalIndex(::ChainActive(), locator)) { return fork->nHeight; } return nullopt; } bool contextualCheckTransactionForCurrentBlock( const Consensus::Params ¶ms, const CTransaction &tx, TxValidationState &state) override { LockAssertion lock(::cs_main); return ContextualCheckTransactionForCurrentBlock(params, tx, state); } using UniqueLock::UniqueLock; }; // namespace interfaces class NotificationsProxy : public CValidationInterface { public: explicit NotificationsProxy( std::shared_ptr notifications) : m_notifications(std::move(notifications)) {} virtual ~NotificationsProxy() = default; void TransactionAddedToMempool(const CTransactionRef &tx) override { m_notifications->TransactionAddedToMempool(tx); } void TransactionRemovedFromMempool(const CTransactionRef &tx) override { m_notifications->TransactionRemovedFromMempool(tx); } void BlockConnected( const std::shared_ptr &block, const CBlockIndex *index, const std::vector &tx_conflicted) override { m_notifications->BlockConnected(*block, tx_conflicted, index->nHeight); } void BlockDisconnected(const std::shared_ptr &block, const CBlockIndex *index) override { m_notifications->BlockDisconnected(*block, index->nHeight); } void UpdatedBlockTip(const CBlockIndex *index, const CBlockIndex *fork_index, bool is_ibd) override { m_notifications->UpdatedBlockTip(); } void ChainStateFlushed(const CBlockLocator &locator) override { m_notifications->ChainStateFlushed(locator); } std::shared_ptr m_notifications; }; class NotificationsHandlerImpl : public Handler { public: explicit NotificationsHandlerImpl( std::shared_ptr notifications) : m_proxy(std::make_shared( std::move(notifications))) { RegisterSharedValidationInterface(m_proxy); } ~NotificationsHandlerImpl() override { disconnect(); } void disconnect() override { if (m_proxy) { UnregisterSharedValidationInterface(m_proxy); m_proxy.reset(); } } std::shared_ptr m_proxy; }; class RpcHandlerImpl : public Handler { public: explicit RpcHandlerImpl(const CRPCCommand &command) : m_command(command), m_wrapped_command(&command) { m_command.actor = [this](Config &config, const JSONRPCRequest &request, UniValue &result, bool last_handler) { if (!m_wrapped_command) { return false; } try { return m_wrapped_command->actor(config, request, result, last_handler); } catch (const UniValue &e) { // If this is not the last handler and a wallet not found // exception was thrown, return false so the next handler // can try to handle the request. Otherwise, reraise the // exception. if (!last_handler) { const UniValue &code = e["code"]; if (code.isNum() && code.get_int() == RPC_WALLET_NOT_FOUND) { return false; } } throw; } }; ::tableRPC.appendCommand(m_command.name, &m_command); } void disconnect() override final { if (m_wrapped_command) { m_wrapped_command = nullptr; ::tableRPC.removeCommand(m_command.name, &m_command); } } ~RpcHandlerImpl() override { disconnect(); } CRPCCommand m_command; const CRPCCommand *m_wrapped_command; }; class ChainImpl : public Chain { public: explicit ChainImpl(NodeContext &node, const CChainParams ¶ms) : m_node(node), m_params(params) {} std::unique_ptr lock(bool try_lock) override { auto lock = std::make_unique( ::cs_main, "cs_main", __FILE__, __LINE__, try_lock); if (try_lock && lock && !*lock) { return {}; } // Temporary to avoid CWG 1579 std::unique_ptr result = std::move(lock); return result; } - bool findBlock(const BlockHash &hash, CBlock *block, int64_t *time, - int64_t *time_max) override { - CBlockIndex *index; - { - LOCK(cs_main); - index = LookupBlockIndex(hash); - if (!index) { - return false; - } - if (time) { - *time = index->GetBlockTime(); - } - if (time_max) { - *time_max = index->GetBlockTimeMax(); - } - } - if (block && - !ReadBlockFromDisk(*block, index, Params().GetConsensus())) { - block->SetNull(); - } - return true; + bool findBlock(const BlockHash &hash, + const FoundBlock &block) override { + WAIT_LOCK(cs_main, lock); + return FillBlock(LookupBlockIndex(hash), block, lock); } void findCoins(std::map &coins) override { return FindCoins(coins); } double guessVerificationProgress(const BlockHash &block_hash) override { LOCK(cs_main); return GuessVerificationProgress(Params().TxData(), LookupBlockIndex(block_hash)); } bool hasDescendantsInMempool(const TxId &txid) override { LOCK(::g_mempool.cs); auto it = ::g_mempool.GetIter(txid); return it && (*it)->GetCountWithDescendants() > 1; } bool broadcastTransaction(const Config &config, const CTransactionRef &tx, std::string &err_string, const Amount &max_tx_fee, bool relay) override { const TransactionError err = BroadcastTransaction( m_node, config, tx, err_string, max_tx_fee, relay, /*wait_callback*/ false); // Chain clients only care about failures to accept the tx to the // mempool. Disregard non-mempool related failures. Note: this will // need to be updated if BroadcastTransactions() is updated to // return other non-mempool failures that Chain clients do not need // to know about. return err == TransactionError::OK; } void getTransactionAncestry(const TxId &txid, size_t &ancestors, size_t &descendants) override { ::g_mempool.GetTransactionAncestry(txid, ancestors, descendants); } void getPackageLimits(size_t &limit_ancestor_count, size_t &limit_descendant_count) override { limit_ancestor_count = size_t( std::max(1, gArgs.GetArg("-limitancestorcount", DEFAULT_ANCESTOR_LIMIT))); limit_descendant_count = size_t( std::max(1, gArgs.GetArg("-limitdescendantcount", DEFAULT_DESCENDANT_LIMIT))); } bool checkChainLimits(const CTransactionRef &tx) override { LockPoints lp; CTxMemPoolEntry entry(tx, Amount(), 0, 0, false, 0, lp); CTxMemPool::setEntries ancestors; auto limit_ancestor_count = gArgs.GetArg("-limitancestorcount", DEFAULT_ANCESTOR_LIMIT); auto limit_ancestor_size = gArgs.GetArg("-limitancestorsize", DEFAULT_ANCESTOR_SIZE_LIMIT) * 1000; auto limit_descendant_count = gArgs.GetArg("-limitdescendantcount", DEFAULT_DESCENDANT_LIMIT); auto limit_descendant_size = gArgs.GetArg("-limitdescendantsize", DEFAULT_DESCENDANT_SIZE_LIMIT) * 1000; std::string unused_error_string; LOCK(::g_mempool.cs); return ::g_mempool.CalculateMemPoolAncestors( entry, ancestors, limit_ancestor_count, limit_ancestor_size, limit_descendant_count, limit_descendant_size, unused_error_string); } CFeeRate estimateFee() const override { return ::g_mempool.estimateFee(); } CFeeRate relayMinFee() override { return ::minRelayTxFee; } CFeeRate relayDustFee() override { return ::dustRelayFee; } bool havePruned() override { LOCK(cs_main); return ::fHavePruned; } bool isReadyToBroadcast() override { return !::fImporting && !::fReindex && !isInitialBlockDownload(); } bool isInitialBlockDownload() override { return ::ChainstateActive().IsInitialBlockDownload(); } bool shutdownRequested() override { return ShutdownRequested(); } int64_t getAdjustedTime() override { return GetAdjustedTime(); } void initMessage(const std::string &message) override { ::uiInterface.InitMessage(message); } void initWarning(const std::string &message) override { InitWarning(message); } void initError(const std::string &message) override { InitError(message); } void showProgress(const std::string &title, int progress, bool resume_possible) override { ::uiInterface.ShowProgress(title, progress, resume_possible); } std::unique_ptr handleNotifications( std::shared_ptr notifications) override { return std::make_unique( std::move(notifications)); } void waitForNotificationsIfTipChanged(const BlockHash &old_tip) override { if (!old_tip.IsNull()) { LOCK(::cs_main); if (old_tip == ::ChainActive().Tip()->GetBlockHash()) { return; } } SyncWithValidationInterfaceQueue(); } std::unique_ptr handleRpc(const CRPCCommand &command) override { return std::make_unique(command); } bool rpcEnableDeprecated(const std::string &method) override { return IsDeprecatedRPCEnabled(gArgs, method); } void rpcRunLater(const std::string &name, std::function fn, int64_t seconds) override { RPCRunLater(name, std::move(fn), seconds); } int rpcSerializationFlags() override { return RPCSerializationFlags(); } void requestMempoolTransactions(Notifications ¬ifications) override { LOCK2(::cs_main, ::g_mempool.cs); for (const CTxMemPoolEntry &entry : ::g_mempool.mapTx) { notifications.TransactionAddedToMempool(entry.GetSharedTx()); } } NodeContext &m_node; const CChainParams &m_params; }; } // namespace std::unique_ptr MakeChain(NodeContext &node, const CChainParams ¶ms) { return std::make_unique(node, params); } } // namespace interfaces diff --git a/src/interfaces/chain.h b/src/interfaces/chain.h index 8c8e0774a..1a56ac6fa 100644 --- a/src/interfaces/chain.h +++ b/src/interfaces/chain.h @@ -1,313 +1,346 @@ // Copyright (c) 2018-2019 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #ifndef BITCOIN_INTERFACES_CHAIN_H #define BITCOIN_INTERFACES_CHAIN_H #include #include #include #include #include #include #include #include class CBlock; class CChainParams; class Coin; class Config; class CRPCCommand; class CScheduler; class TxValidationState; struct BlockHash; struct CBlockLocator; struct NodeContext; namespace Consensus { struct Params; } namespace interfaces { class Handler; class Wallet; +//! Helper for findBlock to selectively return pieces of block data. +class FoundBlock { +public: + FoundBlock &hash(BlockHash &hash) { + m_hash = &hash; + return *this; + } + FoundBlock &height(int &height) { + m_height = &height; + return *this; + } + FoundBlock &time(int64_t &time) { + m_time = &time; + return *this; + } + FoundBlock &maxTime(int64_t &max_time) { + m_max_time = &max_time; + return *this; + } + FoundBlock &mtpTime(int64_t &mtp_time) { + m_mtp_time = &mtp_time; + return *this; + } + //! Read block data from disk. If the block exists but doesn't have data + //! (for example due to pruning), the CBlock variable will be set to null. + FoundBlock &data(CBlock &data) { + m_data = &data; + return *this; + } + + BlockHash *m_hash = nullptr; + int *m_height = nullptr; + int64_t *m_time = nullptr; + int64_t *m_max_time = nullptr; + int64_t *m_mtp_time = nullptr; + CBlock *m_data = nullptr; +}; + //! Interface giving clients (wallet processes, maybe other analysis tools in //! the future) ability to access to the chain state, receive notifications, //! estimate fees, and submit transactions. //! //! TODO: Current chain methods are too low level, exposing too much of the //! internal workings of the bitcoin node, and not being very convenient to use. //! Chain methods should be cleaned up and simplified over time. Examples: //! //! * The Chain::lock() method, which lets clients delay chain tip updates //! should be removed when clients are able to respond to updates //! asynchronously //! (https://github.com/bitcoin/bitcoin/pull/10973#issuecomment-380101269). //! //! * The initMessage() and showProgress() methods which the wallet uses to send //! notifications to the GUI should go away when GUI and wallet can directly //! communicate with each other without going through the node //! (https://github.com/bitcoin/bitcoin/pull/15288#discussion_r253321096). //! //! * The handleRpc, registerRpcs, rpcEnableDeprecated methods and other RPC //! methods can go away if wallets listen for HTTP requests on their own //! ports instead of registering to handle requests on the node HTTP port. class Chain { public: virtual ~Chain() {} //! Interface for querying locked chain state, used by legacy code that //! assumes state won't change between calls. New code should avoid using //! the Lock interface and instead call higher-level Chain methods //! that return more information so the chain doesn't need to stay locked //! between calls. class Lock { public: virtual ~Lock() {} //! Get current chain height, not including genesis block (returns 0 if //! chain only contains genesis block, nullopt if chain does not contain //! any blocks). virtual Optional getHeight() = 0; //! Get block height above genesis block. Returns 0 for genesis block, //! 1 for following block, and so on. Returns nullopt for a block not //! included in the current chain. virtual Optional getBlockHeight(const BlockHash &hash) = 0; //! Get block hash. Height must be valid or this function will abort. virtual BlockHash getBlockHash(int height) = 0; //! Get block time. Height must be valid or this function will abort. virtual int64_t getBlockTime(int height) = 0; //! Get block median time past. Height must be valid or this function //! will abort. virtual int64_t getBlockMedianTimePast(int height) = 0; //! Check that the block is available on disk (i.e. has not been //! pruned), and contains transactions. virtual bool haveBlockOnDisk(int height) = 0; //! Return height of the first block in the chain with timestamp equal //! or greater than the given time and height equal or greater than the //! given height, or nullopt if there is no block with a high enough //! timestamp and height. Also return the block hash as an optional //! output parameter (to avoid the cost of a second lookup in case this //! information is needed.) virtual Optional findFirstBlockWithTimeAndHeight(int64_t time, int height, BlockHash *hash) = 0; //! Return height of last block in the specified range which is pruned, //! or nullopt if no block in the range is pruned. Range is inclusive. virtual Optional findPruned(int start_height = 0, Optional stop_height = nullopt) = 0; //! Return height of the highest block on the chain that is an ancestor //! of the specified block, or nullopt if no common ancestor is found. //! Also return the height of the specified block as an optional output //! parameter (to avoid the cost of a second hash lookup in case this //! information is desired). virtual Optional findFork(const BlockHash &hash, Optional *height) = 0; //! Get locator for the current chain tip. virtual CBlockLocator getTipLocator() = 0; //! Return height of the latest block common to locator and chain, which //! is guaranteed to be an ancestor of the block used to create the //! locator. virtual Optional findLocatorFork(const CBlockLocator &locator) = 0; //! Check if transaction will be final given chain height current time. virtual bool contextualCheckTransactionForCurrentBlock( const Consensus::Params ¶ms, const CTransaction &tx, TxValidationState &state) = 0; }; //! Return Lock interface. Chain is locked when this is called, and //! unlocked when the returned interface is freed. virtual std::unique_ptr lock(bool try_lock = false) = 0; //! Return whether node has the block and optionally return block metadata //! or contents. - //! - //! If a block pointer is provided to retrieve the block contents, and the - //! block exists but doesn't have data (for example due to pruning), the - //! block will be empty and all fields set to null. - virtual bool findBlock(const BlockHash &hash, CBlock *block = nullptr, - int64_t *time = nullptr, - int64_t *max_time = nullptr) = 0; + virtual bool findBlock(const BlockHash &hash, + const FoundBlock &block = {}) = 0; //! Look up unspent output information. Returns coins in the mempool and in //! the current chain UTXO set. Iterates through all the keys in the map and //! populates the values. virtual void findCoins(std::map &coins) = 0; //! Estimate fraction of total transactions verified if blocks up to //! the specified block hash are verified. virtual double guessVerificationProgress(const BlockHash &block_hash) = 0; //! Check if transaction has descendants in mempool. virtual bool hasDescendantsInMempool(const TxId &txid) = 0; //! Transaction is added to memory pool, if the transaction fee is below the //! amount specified by max_tx_fee, and broadcast to all peers if relay is //! set to true. Return false if the transaction could not be added due to //! the fee or for another reason. virtual bool broadcastTransaction(const Config &config, const CTransactionRef &tx, std::string &err_string, const Amount &max_tx_fee, bool relay) = 0; //! Calculate mempool ancestor and descendant counts for the given //! transaction. virtual void getTransactionAncestry(const TxId &txid, size_t &ancestors, size_t &descendants) = 0; //! Get the node's package limits. //! Currently only returns the ancestor and descendant count limits, but //! could be enhanced to return more policy settings. virtual void getPackageLimits(size_t &limit_ancestor_count, size_t &limit_descendant_count) = 0; //! Check if transaction will pass the mempool's chain limits. virtual bool checkChainLimits(const CTransactionRef &tx) = 0; //! Estimate fee virtual CFeeRate estimateFee() const = 0; //! Relay current minimum fee (from -minrelaytxfee settings). virtual CFeeRate relayMinFee() = 0; //! Relay dust fee setting (-dustrelayfee), reflecting lowest rate it's //! economical to spend. virtual CFeeRate relayDustFee() = 0; //! Check if any block has been pruned. virtual bool havePruned() = 0; //! Check if the node is ready to broadcast transactions. virtual bool isReadyToBroadcast() = 0; //! Check if in IBD. virtual bool isInitialBlockDownload() = 0; //! Check if shutdown requested. virtual bool shutdownRequested() = 0; //! Get adjusted time. virtual int64_t getAdjustedTime() = 0; //! Send init message. virtual void initMessage(const std::string &message) = 0; //! Send init warning. virtual void initWarning(const std::string &message) = 0; //! Send init error. virtual void initError(const std::string &message) = 0; //! Send progress indicator. virtual void showProgress(const std::string &title, int progress, bool resume_possible) = 0; //! Chain notifications. class Notifications { public: virtual ~Notifications() {} virtual void TransactionAddedToMempool(const CTransactionRef &tx) {} virtual void TransactionRemovedFromMempool(const CTransactionRef &ptx) { } virtual void BlockConnected(const CBlock &block, const std::vector &tx_conflicted, int height) {} virtual void BlockDisconnected(const CBlock &block, int height) {} virtual void UpdatedBlockTip() {} virtual void ChainStateFlushed(const CBlockLocator &locator) {} }; //! Register handler for notifications. virtual std::unique_ptr handleNotifications(std::shared_ptr notifications) = 0; //! Wait for pending notifications to be processed unless block hash points //! to the current chain tip. virtual void waitForNotificationsIfTipChanged(const BlockHash &old_tip) = 0; //! Register handler for RPC. Command is not copied, so reference //! needs to remain valid until Handler is disconnected. virtual std::unique_ptr handleRpc(const CRPCCommand &command) = 0; //! Check if deprecated RPC is enabled. virtual bool rpcEnableDeprecated(const std::string &method) = 0; //! Run function after given number of seconds. Cancel any previous calls //! with same name. virtual void rpcRunLater(const std::string &name, std::function fn, int64_t seconds) = 0; //! Current RPC serialization flags. virtual int rpcSerializationFlags() = 0; //! Synchronously send TransactionAddedToMempool notifications about all //! current mempool transactions to the specified handler and return after //! the last one is sent. These notifications aren't coordinated with async //! notifications sent by handleNotifications, so out of date async //! notifications from handleNotifications can arrive during and after //! synchronous notifications from requestMempoolTransactions. Clients need //! to be prepared to handle this by ignoring notifications about unknown //! removed transactions and already added new transactions. virtual void requestMempoolTransactions(Notifications ¬ifications) = 0; }; //! Interface to let node manage chain clients (wallets, or maybe tools for //! monitoring and analysis in the future). class ChainClient { public: virtual ~ChainClient() {} //! Register rpcs. virtual void registerRpcs() = 0; //! Check for errors before loading. virtual bool verify(const CChainParams &chainParams) = 0; //! Load saved state. virtual bool load(const CChainParams &chainParams) = 0; //! Start client execution and provide a scheduler. virtual void start(CScheduler &scheduler) = 0; //! Save state to disk. virtual void flush() = 0; //! Shut down client. virtual void stop() = 0; }; //! Return implementation of Chain interface. std::unique_ptr MakeChain(NodeContext &node, const CChainParams ¶ms); //! Return implementation of ChainClient interface for a wallet client. This //! function will be undefined in builds where ENABLE_WALLET is false. //! //! Currently, wallets are the only chain clients. But in the future, other //! types of chain clients could be added, such as tools for monitoring, //! analysis, or fee estimation. These clients need to expose their own //! MakeXXXClient functions returning their implementations of the ChainClient //! interface. std::unique_ptr MakeWalletClient(Chain &chain, std::vector wallet_filenames); } // namespace interfaces #endif // BITCOIN_INTERFACES_CHAIN_H diff --git a/src/sync.h b/src/sync.h index b044294bb..ed8550aad 100644 --- a/src/sync.h +++ b/src/sync.h @@ -1,361 +1,361 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2009-2016 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #ifndef BITCOIN_SYNC_H #define BITCOIN_SYNC_H #include #include #include #include #include ///////////////////////////////////////////////// // // // THE SIMPLE DEFINITION, EXCLUDING DEBUG CODE // // // ///////////////////////////////////////////////// /* RecursiveMutex mutex; std::recursive_mutex mutex; LOCK(mutex); std::unique_lock criticalblock(mutex); LOCK2(mutex1, mutex2); std::unique_lock criticalblock1(mutex1); std::unique_lock criticalblock2(mutex2); TRY_LOCK(mutex, name); std::unique_lock name(mutex, std::try_to_lock_t); ENTER_CRITICAL_SECTION(mutex); // no RAII mutex.lock(); LEAVE_CRITICAL_SECTION(mutex); // no RAII mutex.unlock(); */ /////////////////////////////// // // // THE ACTUAL IMPLEMENTATION // // // /////////////////////////////// #ifdef DEBUG_LOCKORDER void EnterCritical(const char *pszName, const char *pszFile, int nLine, void *cs, bool fTry = false); void LeaveCritical(); void CheckLastCritical(void *cs, std::string &lockname, const char *guardname, const char *file, int line); std::string LocksHeld(); void AssertLockHeldInternal(const char *pszName, const char *pszFile, int nLine, void *cs) ASSERT_EXCLUSIVE_LOCK(cs); void AssertLockNotHeldInternal(const char *pszName, const char *pszFile, int nLine, void *cs); void DeleteLock(void *cs); /** * Call abort() if a potential lock order deadlock bug is detected, instead of * just logging information and throwing a logic_error. Defaults to true, and * set to false in DEBUG_LOCKORDER unit tests. */ extern bool g_debug_lockorder_abort; #else static inline void EnterCritical(const char *pszName, const char *pszFile, int nLine, void *cs, bool fTry = false) {} static inline void LeaveCritical() {} static inline void CheckLastCritical(void *cs, std::string &lockname, const char *guardname, const char *file, int line) {} static inline void AssertLockHeldInternal(const char *pszName, const char *pszFile, int nLine, void *cs) ASSERT_EXCLUSIVE_LOCK(cs) {} static inline void AssertLockNotHeldInternal(const char *pszName, const char *pszFile, int nLine, void *cs) {} static inline void DeleteLock(void *cs) {} #endif #define AssertLockHeld(cs) AssertLockHeldInternal(#cs, __FILE__, __LINE__, &cs) #define AssertLockNotHeld(cs) \ AssertLockNotHeldInternal(#cs, __FILE__, __LINE__, &cs) /** * Template mixin that adds -Wthread-safety locking annotations and lock order * checking to a subset of the mutex API. */ template class LOCKABLE AnnotatedMixin : public PARENT { public: ~AnnotatedMixin() { DeleteLock((void *)this); } void lock() EXCLUSIVE_LOCK_FUNCTION() { PARENT::lock(); } void unlock() UNLOCK_FUNCTION() { PARENT::unlock(); } bool try_lock() EXCLUSIVE_TRYLOCK_FUNCTION(true) { return PARENT::try_lock(); } using UniqueLock = std::unique_lock; }; /** * Wrapped mutex: supports recursive locking, but no waiting * TODO: We should move away from using the recursive lock by default. */ using RecursiveMutex = AnnotatedMixin; /** Wrapped mutex: supports waiting but not recursive locking */ typedef AnnotatedMixin Mutex; #ifdef DEBUG_LOCKCONTENTION void PrintLockContention(const char *pszName, const char *pszFile, int nLine); #endif /** Wrapper around std::unique_lock style lock for Mutex. */ template class SCOPED_LOCKABLE UniqueLock : public Base { private: void Enter(const char *pszName, const char *pszFile, int nLine) { EnterCritical(pszName, pszFile, nLine, (void *)(Base::mutex())); #ifdef DEBUG_LOCKCONTENTION if (!Base::try_lock()) { PrintLockContention(pszName, pszFile, nLine); #endif Base::lock(); #ifdef DEBUG_LOCKCONTENTION } #endif } bool TryEnter(const char *pszName, const char *pszFile, int nLine) { EnterCritical(pszName, pszFile, nLine, (void *)(Base::mutex()), true); Base::try_lock(); if (!Base::owns_lock()) { LeaveCritical(); } return Base::owns_lock(); } public: UniqueLock(Mutex &mutexIn, const char *pszName, const char *pszFile, int nLine, bool fTry = false) EXCLUSIVE_LOCK_FUNCTION(mutexIn) : Base(mutexIn, std::defer_lock) { if (fTry) { TryEnter(pszName, pszFile, nLine); } else { Enter(pszName, pszFile, nLine); } } UniqueLock(Mutex *pmutexIn, const char *pszName, const char *pszFile, int nLine, bool fTry = false) EXCLUSIVE_LOCK_FUNCTION(pmutexIn) { if (!pmutexIn) { return; } *static_cast(this) = Base(*pmutexIn, std::defer_lock); if (fTry) { TryEnter(pszName, pszFile, nLine); } else { Enter(pszName, pszFile, nLine); } } ~UniqueLock() UNLOCK_FUNCTION() { if (Base::owns_lock()) { LeaveCritical(); } } operator bool() { return Base::owns_lock(); } protected: // needed for reverse_lock UniqueLock() {} public: /** * An RAII-style reverse lock. Unlocks on construction and locks on * destruction. */ class reverse_lock { public: explicit reverse_lock(UniqueLock &_lock, const char *_guardname, const char *_file, int _line) : lock(_lock), file(_file), line(_line) { CheckLastCritical((void *)lock.mutex(), lockname, _guardname, _file, _line); lock.unlock(); LeaveCritical(); lock.swap(templock); } ~reverse_lock() { templock.swap(lock); EnterCritical(lockname.c_str(), file.c_str(), line, (void *)lock.mutex()); lock.lock(); } private: reverse_lock(reverse_lock const &); reverse_lock &operator=(reverse_lock const &); UniqueLock &lock; UniqueLock templock; std::string lockname; const std::string file; const int line; }; friend class reverse_lock; }; #define REVERSE_LOCK(g) \ - decltype(g)::reverse_lock PASTE2(revlock, __COUNTER__)(g, #g, __FILE__, \ - __LINE__) + typename std::decay::type::reverse_lock PASTE2( \ + revlock, __COUNTER__)(g, #g, __FILE__, __LINE__) template using DebugLock = UniqueLock::type>::type>; #define LOCK(cs) \ DebugLock PASTE2(criticalblock, \ __COUNTER__)(cs, #cs, __FILE__, __LINE__) #define LOCK2(cs1, cs2) \ DebugLock criticalblock1(cs1, #cs1, __FILE__, __LINE__); \ DebugLock criticalblock2(cs2, #cs2, __FILE__, __LINE__); #define TRY_LOCK(cs, name) \ DebugLock name(cs, #cs, __FILE__, __LINE__, true) #define WAIT_LOCK(cs, name) \ DebugLock name(cs, #cs, __FILE__, __LINE__) #define ENTER_CRITICAL_SECTION(cs) \ { \ EnterCritical(#cs, __FILE__, __LINE__, (void *)(&cs)); \ (cs).lock(); \ } #define LEAVE_CRITICAL_SECTION(cs) \ { \ (cs).unlock(); \ LeaveCritical(); \ } //! Run code while locking a mutex. //! //! Examples: //! //! WITH_LOCK(cs, shared_val = shared_val + 1); //! //! int val = WITH_LOCK(cs, return shared_val); //! #define WITH_LOCK(cs, code) \ [&] { \ LOCK(cs); \ code; \ }() class CSemaphore { private: std::condition_variable condition; std::mutex mutex; int value; public: explicit CSemaphore(int init) : value(init) {} void wait() { std::unique_lock lock(mutex); condition.wait(lock, [&]() { return value >= 1; }); value--; } bool try_wait() { std::lock_guard lock(mutex); if (value < 1) { return false; } value--; return true; } void post() { { std::lock_guard lock(mutex); value++; } condition.notify_one(); } }; /** RAII-style semaphore lock */ class CSemaphoreGrant { private: CSemaphore *sem; bool fHaveGrant; public: void Acquire() { if (fHaveGrant) { return; } sem->wait(); fHaveGrant = true; } void Release() { if (!fHaveGrant) { return; } sem->post(); fHaveGrant = false; } bool TryAcquire() { if (!fHaveGrant && sem->try_wait()) { fHaveGrant = true; } return fHaveGrant; } void MoveTo(CSemaphoreGrant &grant) { grant.Release(); grant.sem = sem; grant.fHaveGrant = fHaveGrant; fHaveGrant = false; } CSemaphoreGrant() : sem(nullptr), fHaveGrant(false) {} explicit CSemaphoreGrant(CSemaphore &sema, bool fTry = false) : sem(&sema), fHaveGrant(false) { if (fTry) { TryAcquire(); } else { Acquire(); } } ~CSemaphoreGrant() { Release(); } operator bool() const { return fHaveGrant; } }; // Utility class for indicating to compiler thread analysis that a mutex is // locked (when it couldn't be determined otherwise). struct SCOPED_LOCKABLE LockAssertion { template explicit LockAssertion(Mutex &mutex) EXCLUSIVE_LOCK_FUNCTION(mutex) { #ifdef DEBUG_LOCKORDER AssertLockHeld(mutex); #endif } ~LockAssertion() UNLOCK_FUNCTION() {} }; #endif // BITCOIN_SYNC_H diff --git a/src/test/CMakeLists.txt b/src/test/CMakeLists.txt index 36a239c95..42c3f85ec 100644 --- a/src/test/CMakeLists.txt +++ b/src/test/CMakeLists.txt @@ -1,247 +1,248 @@ # Copyright (c) 2018 The Bitcoin developers project(bitcoin-test) # Process json files. file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/data") function(gen_json_header NAME) set(HEADERS "") foreach(f ${ARGN}) set(h "${CMAKE_CURRENT_BINARY_DIR}/${f}.h") # Get the proper name for the test variable. get_filename_component(TEST_NAME ${f} NAME_WE) add_custom_command(OUTPUT ${h} COMMAND "${Python_EXECUTABLE}" "${CMAKE_CURRENT_SOURCE_DIR}/data/generate_header.py" "${TEST_NAME}" "${CMAKE_CURRENT_SOURCE_DIR}/${f}" > ${h} MAIN_DEPENDENCY ${f} DEPENDS "data/generate_header.py" VERBATIM ) list(APPEND HEADERS ${h}) endforeach(f) set(${NAME} "${HEADERS}" PARENT_SCOPE) endfunction() gen_json_header(JSON_HEADERS data/base58_encode_decode.json data/blockfilters.json data/key_io_valid.json data/key_io_invalid.json data/script_tests.json data/sighash.json data/tx_invalid.json data/tx_valid.json ) include(TestSuite) create_test_suite(bitcoin) add_dependencies(check check-bitcoin) # An utility library for bitcoin related test suites. add_library(testutil OBJECT util/blockfilter.cpp util/logging.cpp util/mining.cpp util/setup_common.cpp util/str.cpp util/transaction_utils.cpp util/wallet.cpp ) target_link_libraries(testutil server) if(BUILD_BITCOIN_WALLET) set(BITCOIN_WALLET_TEST_FIXTURE ../wallet/test/init_test_fixture.cpp ../wallet/test/wallet_test_fixture.cpp ) set(BITCOIN_WALLET_TESTS ../wallet/test/db_tests.cpp ../wallet/test/coinselector_tests.cpp ../wallet/test/init_tests.cpp ../wallet/test/ismine_tests.cpp ../wallet/test/psbt_wallet_tests.cpp ../wallet/test/wallet_tests.cpp ../wallet/test/walletdb_tests.cpp ../wallet/test/wallet_crypto_tests.cpp ) endif() add_boost_unit_tests_to_suite(bitcoin test_bitcoin fixture.cpp jsonutil.cpp scriptflags.cpp sigutil.cpp # Tests generated from JSON ${JSON_HEADERS} # Wallet test fixture ${BITCOIN_WALLET_TEST_FIXTURE} TESTS activation_tests.cpp addrman_tests.cpp allocator_tests.cpp amount_tests.cpp arith_uint256_tests.cpp base32_tests.cpp base58_tests.cpp base64_tests.cpp bip32_tests.cpp bitmanip_tests.cpp blockchain_tests.cpp blockcheck_tests.cpp blockencodings_tests.cpp blockfilter_tests.cpp blockfilter_index_tests.cpp blockindex_tests.cpp blockstatus_tests.cpp bloom_tests.cpp bswap_tests.cpp cashaddr_tests.cpp cashaddrenc_tests.cpp checkdatasig_tests.cpp checkpoints_tests.cpp checkqueue_tests.cpp coins_tests.cpp compilerbug_tests.cpp compress_tests.cpp config_tests.cpp core_io_tests.cpp crypto_tests.cpp cuckoocache_tests.cpp dbwrapper_tests.cpp denialofservice_tests.cpp descriptor_tests.cpp dstencode_tests.cpp excessiveblock_tests.cpp feerate_tests.cpp finalization_tests.cpp flatfile_tests.cpp fs_tests.cpp getarg_tests.cpp hash_tests.cpp + interfaces_tests.cpp inv_tests.cpp key_io_tests.cpp key_tests.cpp lcg_tests.cpp limitedmap_tests.cpp mempool_tests.cpp merkle_tests.cpp merkleblock_tests.cpp miner_tests.cpp monolith_opcodes_tests.cpp multisig_tests.cpp net_tests.cpp netbase_tests.cpp op_reversebytes_tests.cpp pmt_tests.cpp policyestimator_tests.cpp prevector_tests.cpp radix_tests.cpp raii_event_tests.cpp random_tests.cpp rcu_tests.cpp reverselock_tests.cpp rpc_tests.cpp rpc_server_tests.cpp rwcollection_tests.cpp sanity_tests.cpp scheduler_tests.cpp schnorr_tests.cpp script_bitfield_tests.cpp script_commitment_tests.cpp script_p2sh_tests.cpp script_standard_tests.cpp script_tests.cpp scriptnum_tests.cpp serialize_tests.cpp settings_tests.cpp sigcache_tests.cpp sigencoding_tests.cpp sighash_tests.cpp sighashtype_tests.cpp sigcheckcount_tests.cpp skiplist_tests.cpp streams_tests.cpp sync_tests.cpp timedata_tests.cpp torcontrol_tests.cpp transaction_tests.cpp txindex_tests.cpp txvalidation_tests.cpp txvalidationcache_tests.cpp uint256_tests.cpp undo_tests.cpp util_tests.cpp util_threadnames_tests.cpp validation_block_tests.cpp validation_tests.cpp validationinterface_tests.cpp versionbits_tests.cpp work_comparator_tests.cpp # RPC Tests ../rpc/test/server_tests.cpp # Wallet tests ${BITCOIN_WALLET_TESTS} ) function(add_boost_test_runners_with_upgrade_activated SUITE EXECUTABLE) set(SUITE_UPGRADE_ACTIVATED "${SUITE}-upgrade-activated") get_target_from_suite(${SUITE_UPGRADE_ACTIVATED} TARGET_UPGRADE_ACTIVATED) if(NOT TARGET ${TARGET_UPGRADE_ACTIVATED}) create_test_suite_with_parent_targets( ${SUITE_UPGRADE_ACTIVATED} check-upgrade-activated check-upgrade-activated-extended ) add_dependencies(${TARGET_UPGRADE_ACTIVATED} ${EXECUTABLE}) endif() get_target_from_suite(${SUITE} SUITE_TARGET) get_target_property(BOOST_TESTS ${SUITE_TARGET} UNIT_TESTS) get_target_from_suite(${SUITE_UPGRADE_ACTIVATED} SUITE_UPGRADE_ACTIVATED_TARGET) set(HRF_LOGGER "HRF,test_suite") foreach(_test_name ${BOOST_TESTS}) if(ENABLE_JUNIT_REPORT) set(JUNIT_LOGGER ":JUNIT,message,${SUITE_UPGRADE_ACTIVATED}-${_test_name}.xml") endif() add_test_runner( ${SUITE_UPGRADE_ACTIVATED} "${_test_name}" ${EXECUTABLE} JUNIT "--run_test=${_test_name}" "--logger=${HRF_LOGGER}${JUNIT_LOGGER}" # Dec. 1st, 2019 at 00:00:00 -- "-testsuitename=Bitcoin ABC unit tests with next upgrade activated" -axionactivationtime=1575158400 ) endforeach() endfunction() add_boost_test_runners_with_upgrade_activated(bitcoin test_bitcoin) target_link_libraries(test_bitcoin rpcclient testutil) if(TARGET bitcoinconsensus-shared) target_link_libraries(test_bitcoin bitcoinconsensus-shared) else() target_link_libraries(test_bitcoin bitcoinconsensus) endif() add_subdirectory(fuzz) diff --git a/src/test/interfaces_tests.cpp b/src/test/interfaces_tests.cpp new file mode 100644 index 000000000..66729da77 --- /dev/null +++ b/src/test/interfaces_tests.cpp @@ -0,0 +1,53 @@ +// Copyright (c) 2020 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 +#include +#include +#include + +#include + +using interfaces::FoundBlock; + +BOOST_FIXTURE_TEST_SUITE(interfaces_tests, TestChain100Setup) + +BOOST_AUTO_TEST_CASE(findBlock) { + auto chain = interfaces::MakeChain(m_node, Params()); + auto &active = ChainActive(); + + BlockHash hash; + BOOST_CHECK( + chain->findBlock(active[10]->GetBlockHash(), FoundBlock().hash(hash))); + BOOST_CHECK_EQUAL(hash, active[10]->GetBlockHash()); + + int height = -1; + BOOST_CHECK(chain->findBlock(active[20]->GetBlockHash(), + FoundBlock().height(height))); + BOOST_CHECK_EQUAL(height, active[20]->nHeight); + + CBlock data; + BOOST_CHECK( + chain->findBlock(active[30]->GetBlockHash(), FoundBlock().data(data))); + BOOST_CHECK_EQUAL(data.GetHash(), active[30]->GetBlockHash()); + + int64_t time = -1; + BOOST_CHECK( + chain->findBlock(active[40]->GetBlockHash(), FoundBlock().time(time))); + BOOST_CHECK_EQUAL(time, active[40]->GetBlockTime()); + + int64_t max_time = -1; + BOOST_CHECK(chain->findBlock(active[50]->GetBlockHash(), + FoundBlock().maxTime(max_time))); + BOOST_CHECK_EQUAL(max_time, active[50]->GetBlockTimeMax()); + + int64_t mtp_time = -1; + BOOST_CHECK(chain->findBlock(active[60]->GetBlockHash(), + FoundBlock().mtpTime(mtp_time))); + BOOST_CHECK_EQUAL(mtp_time, active[60]->GetMedianTimePast()); + + BOOST_CHECK(!chain->findBlock(BlockHash(), FoundBlock())); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index 4e1693031..9663688d4 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -1,4890 +1,4892 @@ // Copyright (c) 2010 Satoshi Nakamoto // Copyright (c) 2009-2019 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 #include // for GetConsensus. #include #include #include #include #include #include #include #include #include #include #include #include #include