diff --git a/src/coins.h b/src/coins.h index 5273263c0..f7b59d6a8 100644 --- a/src/coins.h +++ b/src/coins.h @@ -1,316 +1,316 @@ // 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_COINS_H #define BITCOIN_COINS_H #include #include #include #include #include #include #include #include #include /** * A UTXO entry. * * Serialized format: * - VARINT((coinbase ? 1 : 0) | (height << 1)) * - the non-spent CTxOut (via CTxOutCompressor) */ class Coin { //! Unspent transaction output. CTxOut out; //! Whether containing transaction was a coinbase and height at which the //! transaction was included into a block. uint32_t nHeightAndIsCoinBase; public: //! Empty constructor Coin() : nHeightAndIsCoinBase(0) {} //! Constructor from a CTxOut and height/coinbase information. Coin(CTxOut outIn, uint32_t nHeightIn, bool IsCoinbase) : out(std::move(outIn)), nHeightAndIsCoinBase((nHeightIn << 1) | IsCoinbase) {} uint32_t GetHeight() const { return nHeightAndIsCoinBase >> 1; } bool IsCoinBase() const { return nHeightAndIsCoinBase & 0x01; } bool IsSpent() const { return out.IsNull(); } CTxOut &GetTxOut() { return out; } const CTxOut &GetTxOut() const { return out; } void Clear() { out.SetNull(); nHeightAndIsCoinBase = 0; } template void Serialize(Stream &s) const { assert(!IsSpent()); ::Serialize(s, VARINT(nHeightAndIsCoinBase)); ::Serialize(s, CTxOutCompressor(REF(out))); } template void Unserialize(Stream &s) { ::Unserialize(s, VARINT(nHeightAndIsCoinBase)); ::Unserialize(s, CTxOutCompressor(out)); } size_t DynamicMemoryUsage() const { return memusage::DynamicUsage(out.scriptPubKey); } }; class SaltedOutpointHasher { private: /** Salt */ const uint64_t k0, k1; public: SaltedOutpointHasher(); /** * This *must* return size_t. With Boost 1.46 on 32-bit systems the * unordered_map will behave unpredictably if the custom hasher returns a * uint64_t, resulting in failures when syncing the chain (#4634). * Note: This information above might be outdated as the unordered map * container type has meanwhile been switched to the C++ standard library * implementation. */ size_t operator()(const COutPoint &outpoint) const { return SipHashUint256Extra(k0, k1, outpoint.GetTxId(), outpoint.GetN()); } }; struct CCoinsCacheEntry { // The actual cached data. Coin coin; uint8_t flags; enum Flags { // This cache entry is potentially different from the version in the // parent view. DIRTY = (1 << 0), // The parent view does not have this entry (or it is pruned). FRESH = (1 << 1), /* Note that FRESH is a performance optimization with which we can erase coins that are fully spent if we know we do not need to flush the changes to the parent cache. It is always safe to not mark FRESH if that condition is not guaranteed. */ }; CCoinsCacheEntry() : flags(0) {} explicit CCoinsCacheEntry(Coin coinIn) : coin(std::move(coinIn)), flags(0) {} }; typedef std::unordered_map CCoinsMap; /** Cursor for iterating over CoinsView state */ class CCoinsViewCursor { public: CCoinsViewCursor(const uint256 &hashBlockIn) : hashBlock(hashBlockIn) {} virtual ~CCoinsViewCursor() {} virtual bool GetKey(COutPoint &key) const = 0; virtual bool GetValue(Coin &coin) const = 0; virtual unsigned int GetValueSize() const = 0; virtual bool Valid() const = 0; virtual void Next() = 0; //! Get best block at the time this cursor was created const uint256 &GetBestBlock() const { return hashBlock; } private: uint256 hashBlock; }; /** Abstract view on the open txout dataset. */ class CCoinsView { public: //! Retrieve the Coin (unspent transaction output) for a given outpoint. virtual bool GetCoin(const COutPoint &outpoint, Coin &coin) const; //! Just check whether we have data for a given outpoint. //! This may (but cannot always) return true for spent outputs. virtual bool HaveCoin(const COutPoint &outpoint) const; //! Retrieve the block hash whose state this CCoinsView currently represents virtual uint256 GetBestBlock() const; //! Retrieve the range of blocks that may have been only partially written. //! If the database is in a consistent state, the result is the empty //! vector. //! Otherwise, a two-element vector is returned consisting of the new and //! the old block hash, in that order. virtual std::vector GetHeadBlocks() const; //! Do a bulk modification (multiple Coin changes + BestBlock change). //! The passed mapCoins can be modified. virtual bool BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock); //! Get a cursor to iterate over the whole state virtual CCoinsViewCursor *Cursor() const; //! As we use CCoinsViews polymorphically, have a virtual destructor virtual ~CCoinsView() {} //! Estimate database size (0 if not implemented) virtual size_t EstimateSize() const { return 0; } }; /** CCoinsView backed by another CCoinsView */ class CCoinsViewBacked : public CCoinsView { protected: CCoinsView *base; public: CCoinsViewBacked(CCoinsView *viewIn); bool GetCoin(const COutPoint &outpoint, Coin &coin) const override; bool HaveCoin(const COutPoint &outpoint) const override; uint256 GetBestBlock() const override; std::vector GetHeadBlocks() const override; void SetBackend(CCoinsView &viewIn); bool BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock) override; CCoinsViewCursor *Cursor() const override; size_t EstimateSize() const override; }; /** * CCoinsView that adds a memory cache for transactions to another CCoinsView */ class CCoinsViewCache : public CCoinsViewBacked { protected: /** * Make mutable so that we can "fill the cache" even from Get-methods * declared as "const". */ mutable uint256 hashBlock; mutable CCoinsMap cacheCoins; /* Cached dynamic memory usage for the inner Coin objects. */ mutable size_t cachedCoinsUsage; public: CCoinsViewCache(CCoinsView *baseIn); + /** + * By deleting the copy constructor, we prevent accidentally using it when + * one intends to create a cache on top of a base cache. + */ + CCoinsViewCache(const CCoinsViewCache &) = delete; + // Standard CCoinsView methods bool GetCoin(const COutPoint &outpoint, Coin &coin) const override; bool HaveCoin(const COutPoint &outpoint) const override; uint256 GetBestBlock() const override; void SetBestBlock(const uint256 &hashBlock); bool BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock) override; CCoinsViewCursor *Cursor() const override { throw std::logic_error( "CCoinsViewCache cursor iteration not supported."); } /** * Check if we have the given utxo already loaded in this cache. * The semantics are the same as HaveCoin(), but no calls to the backing * CCoinsView are made. */ bool HaveCoinInCache(const COutPoint &outpoint) const; /** * Return a reference to a Coin in the cache, or a pruned one if not found. * This is more efficient than GetCoin. Modifications to other cache entries * are allowed while accessing the returned pointer. */ const Coin &AccessCoin(const COutPoint &output) const; /** * Add a coin. Set potential_overwrite to true if a non-pruned version may * already exist. */ void AddCoin(const COutPoint &outpoint, Coin coin, bool potential_overwrite); /** * Spend a coin. Pass moveto in order to get the deleted data. * If no unspent output exists for the passed outpoint, this call has no * effect. */ bool SpendCoin(const COutPoint &outpoint, Coin *moveto = nullptr); /** * Push the modifications applied to this cache to its base. * Failure to call this method before destruction will cause the changes to * be forgotten. If false is returned, the state of this cache (and its * backing view) will be undefined. */ bool Flush(); /** * Removes the UTXO with the given outpoint from the cache, if it is not * modified. */ void Uncache(const COutPoint &outpoint); //! Calculate the size of the cache (in number of transaction outputs) unsigned int GetCacheSize() const; //! Calculate the size of the cache (in bytes) size_t DynamicMemoryUsage() const; /** * Amount of bitcoins coming in to a transaction * Note that lightweight clients may not know anything besides the hash of * previous transactions, so may not be able to calculate this. * * @param[in] tx transaction for which we are checking input total * @return Sum of value of all inputs (scriptSigs) */ Amount GetValueIn(const CTransaction &tx) const; //! Check whether all prevouts of the transaction are present in the UTXO //! set represented by this view bool HaveInputs(const CTransaction &tx) const; /** * Return priority of tx at height nHeight. Also calculate the sum of the * values of the inputs that are already in the chain. These are the inputs * that will age and increase priority as new blocks are added to the chain. */ double GetPriority(const CTransaction &tx, int nHeight, Amount &inChainInputValue) const; const CTxOut &GetOutputFor(const CTxIn &input) const; private: CCoinsMap::iterator FetchCoin(const COutPoint &outpoint) const; - - /** - * By making the copy constructor private, we prevent accidentally using it - * when one intends to create a cache on top of a base cache. - */ - CCoinsViewCache(const CCoinsViewCache &); }; //! Utility function to add all of a transaction's outputs to a cache. // When check is false, this assumes that overwrites are only possible for // coinbase transactions. // When check is true, the underlying view may be queried to determine whether // an addition is an overwrite. // TODO: pass in a boolean to limit these possible overwrites to known // (pre-BIP34) cases. void AddCoins(CCoinsViewCache &cache, const CTransaction &tx, int nHeight, bool check = false); //! Utility function to find any unspent output with a given txid. // This function can be quite expensive because in the event of a transaction // which is not found in the cache, it can cause up to MAX_OUTPUTS_PER_BLOCK // lookups to database, so it should be used with care. const Coin &AccessByTxid(const CCoinsViewCache &cache, const TxId &txid); #endif // BITCOIN_COINS_H diff --git a/src/net.h b/src/net.h index ecadb36fe..f67fa91c0 100644 --- a/src/net.h +++ b/src/net.h @@ -1,876 +1,875 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2009-2016 The Bitcoin Core developers // Copyright (c) 2017 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_NET_H #define BITCOIN_NET_H #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifndef WIN32 #include #endif class Config; class CNode; class CScheduler; /** * Time between pings automatically sent out for latency probing and keepalive * (in seconds). */ static const int PING_INTERVAL = 2 * 60; /** * Time after which to disconnect, after waiting for a ping response (or * inactivity). */ static const int TIMEOUT_INTERVAL = 20 * 60; /** Run the feeler connection loop once every 2 minutes or 120 seconds. **/ static const int FEELER_INTERVAL = 120; /** The maximum number of entries in an 'inv' protocol message */ static const unsigned int MAX_INV_SZ = 50000; static_assert(MAX_PROTOCOL_MESSAGE_LENGTH > MAX_INV_SZ * sizeof(CInv), "Max protocol message length must be greater than largest " "possible INV message"); /** The maximum number of new addresses to accumulate before announcing. */ static const unsigned int MAX_ADDR_TO_SEND = 1000; /** Maximum length of strSubVer in `version` message */ static const unsigned int MAX_SUBVERSION_LENGTH = 256; /** Maximum number of automatic outgoing nodes */ static const int MAX_OUTBOUND_CONNECTIONS = 8; /** Maximum number of addnode outgoing nodes */ static const int MAX_ADDNODE_CONNECTIONS = 8; /** -listen default */ static const bool DEFAULT_LISTEN = true; /** -upnp default */ #ifdef USE_UPNP static const bool DEFAULT_UPNP = USE_UPNP; #else static const bool DEFAULT_UPNP = false; #endif /** The maximum number of entries in mapAskFor */ static const size_t MAPASKFOR_MAX_SZ = MAX_INV_SZ; /** The maximum number of entries in setAskFor (larger due to getdata latency)*/ static const size_t SETASKFOR_MAX_SZ = 2 * MAX_INV_SZ; /** The maximum number of peer connections to maintain. */ static const unsigned int DEFAULT_MAX_PEER_CONNECTIONS = 125; /** The default for -maxuploadtarget. 0 = Unlimited */ static const uint64_t DEFAULT_MAX_UPLOAD_TARGET = 0; /** The default timeframe for -maxuploadtarget. 1 day. */ static const uint64_t MAX_UPLOAD_TIMEFRAME = 60 * 60 * 24; /** Default for blocks only*/ static const bool DEFAULT_BLOCKSONLY = false; static const bool DEFAULT_FORCEDNSSEED = false; static const size_t DEFAULT_MAXRECEIVEBUFFER = 5 * 1000; static const size_t DEFAULT_MAXSENDBUFFER = 1 * 1000; // Default 24-hour ban. // NOTE: When adjusting this, update rpcnet:setban's help ("24h") static const unsigned int DEFAULT_MISBEHAVING_BANTIME = 60 * 60 * 24; typedef int64_t NodeId; struct AddedNodeInfo { std::string strAddedNode; CService resolvedAddress; bool fConnected; bool fInbound; }; struct CNodeStats; class CClientUIInterface; struct CSerializedNetMsg { CSerializedNetMsg() = default; CSerializedNetMsg(CSerializedNetMsg &&) = default; CSerializedNetMsg &operator=(CSerializedNetMsg &&) = default; // No copying, only moves. CSerializedNetMsg(const CSerializedNetMsg &msg) = delete; CSerializedNetMsg &operator=(const CSerializedNetMsg &) = delete; std::vector data; std::string command; }; class NetEventsInterface; class CConnman { public: enum NumConnections { CONNECTIONS_NONE = 0, CONNECTIONS_IN = (1U << 0), CONNECTIONS_OUT = (1U << 1), CONNECTIONS_ALL = (CONNECTIONS_IN | CONNECTIONS_OUT), }; struct Options { ServiceFlags nLocalServices = NODE_NONE; int nMaxConnections = 0; int nMaxOutbound = 0; int nMaxAddnode = 0; int nMaxFeeler = 0; int nBestHeight = 0; CClientUIInterface *uiInterface = nullptr; NetEventsInterface *m_msgproc = nullptr; unsigned int nSendBufferMaxSize = 0; unsigned int nReceiveFloodSize = 0; uint64_t nMaxOutboundTimeframe = 0; uint64_t nMaxOutboundLimit = 0; std::vector vSeedNodes; std::vector vWhitelistedRange; std::vector vBinds, vWhiteBinds; bool m_use_addrman_outgoing = true; std::vector m_specified_outgoing; std::vector m_added_nodes; }; void Init(const Options &connOptions) { nLocalServices = connOptions.nLocalServices; nMaxConnections = connOptions.nMaxConnections; nMaxOutbound = std::min(connOptions.nMaxOutbound, connOptions.nMaxConnections); nMaxAddnode = connOptions.nMaxAddnode; nMaxFeeler = connOptions.nMaxFeeler; nBestHeight = connOptions.nBestHeight; clientInterface = connOptions.uiInterface; m_msgproc = connOptions.m_msgproc; nSendBufferMaxSize = connOptions.nSendBufferMaxSize; nReceiveFloodSize = connOptions.nReceiveFloodSize; { LOCK(cs_totalBytesSent); nMaxOutboundTimeframe = connOptions.nMaxOutboundTimeframe; nMaxOutboundLimit = connOptions.nMaxOutboundLimit; } vWhitelistedRange = connOptions.vWhitelistedRange; { LOCK(cs_vAddedNodes); vAddedNodes = connOptions.m_added_nodes; } } CConnman(const Config &configIn, uint64_t seed0, uint64_t seed1); ~CConnman(); bool Start(CScheduler &scheduler, const Options &options); void Stop(); void Interrupt(); bool GetNetworkActive() const { return fNetworkActive; }; void SetNetworkActive(bool active); void OpenNetworkConnection(const CAddress &addrConnect, bool fCountFailure, CSemaphoreGrant *grantOutbound = nullptr, const char *strDest = nullptr, bool fOneShot = false, bool fFeeler = false, bool manual_connection = false); bool CheckIncomingNonce(uint64_t nonce); bool ForNode(NodeId id, std::function func); void PushMessage(CNode *pnode, CSerializedNetMsg &&msg); template void ForEachNode(Callable &&func) { LOCK(cs_vNodes); for (auto &&node : vNodes) { if (NodeFullyConnected(node)) { func(node); } } }; template void ForEachNode(Callable &&func) const { LOCK(cs_vNodes); for (auto &&node : vNodes) { if (NodeFullyConnected(node)) { func(node); } } }; template void ForEachNodeThen(Callable &&pre, CallableAfter &&post) { LOCK(cs_vNodes); for (auto &&node : vNodes) { if (NodeFullyConnected(node)) { pre(node); } } post(); }; template void ForEachNodeThen(Callable &&pre, CallableAfter &&post) const { LOCK(cs_vNodes); for (auto &&node : vNodes) { if (NodeFullyConnected(node)) { pre(node); } } post(); }; // Addrman functions size_t GetAddressCount() const; void SetServices(const CService &addr, ServiceFlags nServices); void MarkAddressGood(const CAddress &addr); void AddNewAddresses(const std::vector &vAddr, const CAddress &addrFrom, int64_t nTimePenalty = 0); std::vector GetAddresses(); // Denial-of-service detection/prevention. The idea is to detect peers that // are behaving badly and disconnect/ban them, but do it in a // one-coding-mistake-won't-shatter-the-entire-network way. // IMPORTANT: There should be nothing I can give a node that it will forward // on that will make that node's peers drop it. If there is, an attacker can // isolate a node and/or try to split the network. Dropping a node for // sending stuff that is invalid now but might be valid in a later version // is also dangerous, because it can cause a network split between nodes // running old code and nodes running new code. void Ban(const CNetAddr &netAddr, const BanReason &reason, int64_t bantimeoffset = 0, bool sinceUnixEpoch = false); void Ban(const CSubNet &subNet, const BanReason &reason, int64_t bantimeoffset = 0, bool sinceUnixEpoch = false); // Needed for unit testing. void ClearBanned(); bool IsBanned(CNetAddr ip); bool IsBanned(CSubNet subnet); bool Unban(const CNetAddr &ip); bool Unban(const CSubNet &ip); void GetBanned(banmap_t &banmap); void SetBanned(const banmap_t &banmap); // This allows temporarily exceeding nMaxOutbound, with the goal of finding // a peer that is better than all our current peers. void SetTryNewOutboundPeer(bool flag); bool GetTryNewOutboundPeer(); // Return the number of outbound peers we have in excess of our target (eg, // if we previously called SetTryNewOutboundPeer(true), and have since set // to false, we may have extra peers that we wish to disconnect). This may // return a value less than (num_outbound_connections - num_outbound_slots) // in cases where some outbound connections are not yet fully connected, or // not yet fully disconnected. int GetExtraOutboundCount(); bool AddNode(const std::string &node); bool RemoveAddedNode(const std::string &node); std::vector GetAddedNodeInfo(); size_t GetNodeCount(NumConnections num); void GetNodeStats(std::vector &vstats); bool DisconnectNode(const std::string &node); bool DisconnectNode(NodeId id); ServiceFlags GetLocalServices() const; //! set the max outbound target in bytes. void SetMaxOutboundTarget(uint64_t limit); uint64_t GetMaxOutboundTarget(); //! set the timeframe for the max outbound target. void SetMaxOutboundTimeframe(uint64_t timeframe); uint64_t GetMaxOutboundTimeframe(); //! check if the outbound target is reached. // If param historicalBlockServingLimit is set true, the function will // response true if the limit for serving historical blocks has been // reached. bool OutboundTargetReached(bool historicalBlockServingLimit); //! response the bytes left in the current max outbound cycle // in case of no limit, it will always response 0 uint64_t GetOutboundTargetBytesLeft(); //! response the time in second left in the current max outbound cycle // in case of no limit, it will always response 0 uint64_t GetMaxOutboundTimeLeftInCycle(); uint64_t GetTotalBytesRecv(); uint64_t GetTotalBytesSent(); void SetBestHeight(int height); int GetBestHeight() const; /** Get a unique deterministic randomizer. */ CSipHasher GetDeterministicRandomizer(uint64_t id) const; unsigned int GetReceiveFloodSize() const; void WakeMessageHandler(); private: struct ListenSocket { SOCKET socket; bool whitelisted; ListenSocket(SOCKET socket_, bool whitelisted_) : socket(socket_), whitelisted(whitelisted_) {} }; bool BindListenPort(const CService &bindAddr, std::string &strError, bool fWhitelisted = false); bool Bind(const CService &addr, unsigned int flags); bool InitBinds(const std::vector &binds, const std::vector &whiteBinds); void ThreadOpenAddedConnections(); void AddOneShot(const std::string &strDest); void ProcessOneShot(); void ThreadOpenConnections(std::vector connect); void ThreadMessageHandler(); void AcceptConnection(const ListenSocket &hListenSocket); void ThreadSocketHandler(); void ThreadDNSAddressSeed(); uint64_t CalculateKeyedNetGroup(const CAddress &ad) const; CNode *FindNode(const CNetAddr &ip); CNode *FindNode(const CSubNet &subNet); CNode *FindNode(const std::string &addrName); CNode *FindNode(const CService &addr); bool AttemptToEvictConnection(); CNode *ConnectNode(CAddress addrConnect, const char *pszDest, bool fCountFailure); bool IsWhitelistedRange(const CNetAddr &addr); void DeleteNode(CNode *pnode); NodeId GetNewNodeId(); size_t SocketSendData(CNode *pnode) const; //! check is the banlist has unwritten changes bool BannedSetIsDirty(); //! set the "dirty" flag for the banlist void SetBannedSetDirty(bool dirty = true); //! clean unused entries (if bantime has expired) void SweepBanned(); void DumpAddresses(); void DumpData(); void DumpBanlist(); // Network stats void RecordBytesRecv(uint64_t bytes); void RecordBytesSent(uint64_t bytes); // Whether the node should be passed out in ForEach* callbacks static bool NodeFullyConnected(const CNode *pnode); const Config *config; // Network usage totals CCriticalSection cs_totalBytesRecv; CCriticalSection cs_totalBytesSent; uint64_t nTotalBytesRecv GUARDED_BY(cs_totalBytesRecv); uint64_t nTotalBytesSent GUARDED_BY(cs_totalBytesSent); // outbound limit & stats uint64_t nMaxOutboundTotalBytesSentInCycle GUARDED_BY(cs_totalBytesSent); uint64_t nMaxOutboundCycleStartTime GUARDED_BY(cs_totalBytesSent); uint64_t nMaxOutboundLimit GUARDED_BY(cs_totalBytesSent); uint64_t nMaxOutboundTimeframe GUARDED_BY(cs_totalBytesSent); // Whitelisted ranges. Any node connecting from these is automatically // whitelisted (as well as those connecting to whitelisted binds). std::vector vWhitelistedRange; unsigned int nSendBufferMaxSize; unsigned int nReceiveFloodSize; std::vector vhListenSocket; std::atomic fNetworkActive; banmap_t setBanned; CCriticalSection cs_setBanned; bool setBannedIsDirty; bool fAddressesInitialized; CAddrMan addrman; std::deque vOneShots; CCriticalSection cs_vOneShots; std::vector vAddedNodes GUARDED_BY(cs_vAddedNodes); CCriticalSection cs_vAddedNodes; std::vector vNodes; std::list vNodesDisconnected; mutable CCriticalSection cs_vNodes; std::atomic nLastNodeId; /** Services this instance offers */ ServiceFlags nLocalServices; std::unique_ptr semOutbound; std::unique_ptr semAddnode; int nMaxConnections; int nMaxOutbound; int nMaxAddnode; int nMaxFeeler; std::atomic nBestHeight; CClientUIInterface *clientInterface; NetEventsInterface *m_msgproc; /** SipHasher seeds for deterministic randomness */ const uint64_t nSeed0, nSeed1; /** flag for waking the message processor. */ bool fMsgProcWake; std::condition_variable condMsgProc; Mutex mutexMsgProc; std::atomic flagInterruptMsgProc; CThreadInterrupt interruptNet; std::thread threadDNSAddressSeed; std::thread threadSocketHandler; std::thread threadOpenAddedConnections; std::thread threadOpenConnections; std::thread threadMessageHandler; /** * Flag for deciding to connect to an extra outbound peer, in excess of * nMaxOutbound. * This takes the place of a feeler connection. */ std::atomic_bool m_try_another_outbound_peer; friend struct CConnmanTest; }; extern std::unique_ptr g_connman; void Discover(); void StartMapPort(); void InterruptMapPort(); void StopMapPort(); unsigned short GetListenPort(); bool BindListenPort(const CService &bindAddr, std::string &strError, bool fWhitelisted = false); /** * Interface for message handling */ class NetEventsInterface { public: virtual bool ProcessMessages(const Config &config, CNode *pnode, std::atomic &interrupt) = 0; virtual bool SendMessages(const Config &config, CNode *pnode, std::atomic &interrupt) = 0; virtual void InitializeNode(const Config &config, CNode *pnode) = 0; virtual void FinalizeNode(const Config &config, NodeId id, bool &update_connection_time) = 0; protected: /** * Protected destructor so that instances can only be deleted by derived * classes. If that restriction is no longer desired, this should be made * public and virtual. */ ~NetEventsInterface() = default; }; enum { // unknown LOCAL_NONE, // address a local interface listens on LOCAL_IF, // address explicit bound to LOCAL_BIND, // address reported by UPnP LOCAL_UPNP, // address explicitly specified (-externalip=) LOCAL_MANUAL, LOCAL_MAX }; bool IsPeerAddrLocalGood(CNode *pnode); void AdvertiseLocal(CNode *pnode); void SetLimited(enum Network net, bool fLimited = true); bool IsLimited(enum Network net); bool IsLimited(const CNetAddr &addr); bool AddLocal(const CService &addr, int nScore = LOCAL_NONE); bool AddLocal(const CNetAddr &addr, int nScore = LOCAL_NONE); void RemoveLocal(const CService &addr); bool SeenLocal(const CService &addr); bool IsLocal(const CService &addr); bool GetLocal(CService &addr, const CNetAddr *paddrPeer = nullptr); bool IsReachable(enum Network net); bool IsReachable(const CNetAddr &addr); CAddress GetLocalAddress(const CNetAddr *paddrPeer, ServiceFlags nLocalServices); extern bool fDiscover; extern bool fListen; extern bool fRelayTxes; extern limitedmap mapAlreadyAskedFor; struct LocalServiceInfo { int nScore; int nPort; }; extern CCriticalSection cs_mapLocalHost; extern std::map mapLocalHost; // Command, total bytes typedef std::map mapMsgCmdSize; /** * POD that contains various stats about a node. * Usually constructed from CConman::GetNodeStats. Stats are filled from the * node using CNode::copyStats. */ struct CNodeStats { NodeId nodeid; ServiceFlags nServices; bool fRelayTxes; int64_t nLastSend; int64_t nLastRecv; int64_t nTimeConnected; int64_t nTimeOffset; std::string addrName; int nVersion; std::string cleanSubVer; bool fInbound; bool m_manual_connection; int nStartingHeight; uint64_t nSendBytes; mapMsgCmdSize mapSendBytesPerMsgCmd; uint64_t nRecvBytes; mapMsgCmdSize mapRecvBytesPerMsgCmd; bool fWhitelisted; double dPingTime; double dPingWait; double dMinPing; // Our address, as reported by the peer std::string addrLocal; // Address of this peer CAddress addr; // Bind address of our side of the connection CAddress addrBind; }; class CNetMessage { private: mutable CHash256 hasher; mutable uint256 data_hash; public: // Parsing header (false) or data (true) bool in_data; // Partially received header. CDataStream hdrbuf; // Complete header. CMessageHeader hdr; uint32_t nHdrPos; // Received message data. CDataStream vRecv; uint32_t nDataPos; // Time (in microseconds) of message receipt. int64_t nTime; CNetMessage(const CMessageHeader::MessageMagic &pchMessageStartIn, int nTypeIn, int nVersionIn) : hdrbuf(nTypeIn, nVersionIn), hdr(pchMessageStartIn), vRecv(nTypeIn, nVersionIn) { hdrbuf.resize(24); in_data = false; nHdrPos = 0; nDataPos = 0; nTime = 0; } bool complete() const { if (!in_data) { return false; } return (hdr.nMessageSize == nDataPos); } const uint256 &GetMessageHash() const; void SetVersion(int nVersionIn) { hdrbuf.SetVersion(nVersionIn); vRecv.SetVersion(nVersionIn); } int readHeader(const Config &config, const char *pch, uint32_t nBytes); int readData(const char *pch, uint32_t nBytes); }; /** Information about a peer */ class CNode { friend class CConnman; public: // socket std::atomic nServices; SOCKET hSocket; // Total size of all vSendMsg entries. size_t nSendSize; // Offset inside the first vSendMsg already sent. size_t nSendOffset; uint64_t nSendBytes; std::deque> vSendMsg; CCriticalSection cs_vSend; CCriticalSection cs_hSocket; CCriticalSection cs_vRecv; CCriticalSection cs_vProcessMsg; std::list vProcessMsg; size_t nProcessQueueSize; CCriticalSection cs_sendProcessing; std::deque vRecvGetData; uint64_t nRecvBytes; std::atomic nRecvVersion; std::atomic nLastSend; std::atomic nLastRecv; const int64_t nTimeConnected; std::atomic nTimeOffset; // Address of this peer const CAddress addr; // Bind address of our side of the connection const CAddress addrBind; std::atomic nVersion; // strSubVer is whatever byte array we read from the wire. However, this // field is intended to be printed out, displayed to humans in various forms // and so on. So we sanitize it and store the sanitized version in // cleanSubVer. The original should be used when dealing with the network or // wire types and the cleaned string used when displayed or logged. std::string strSubVer, cleanSubVer; // Used for both cleanSubVer and strSubVer. CCriticalSection cs_SubVer; // This peer can bypass DoS banning. bool fWhitelisted; // If true this node is being used as a short lived feeler. bool fFeeler; bool fOneShot; bool m_manual_connection; bool fClient; // after BIP159 bool m_limited_node; const bool fInbound; std::atomic_bool fSuccessfullyConnected; std::atomic_bool fDisconnect; // We use fRelayTxes for two purposes - // a) it allows us to not relay tx invs before receiving the peer's version // message. // b) the peer may tell us in its version message that we should not relay // tx invs unless it loads a bloom filter. // protected by cs_filter bool fRelayTxes; bool fSentAddr; CSemaphoreGrant grantOutbound; CCriticalSection cs_filter; std::unique_ptr pfilter; std::atomic nRefCount; const uint64_t nKeyedNetGroup; std::atomic_bool fPauseRecv; std::atomic_bool fPauseSend; protected: mapMsgCmdSize mapSendBytesPerMsgCmd; mapMsgCmdSize mapRecvBytesPerMsgCmd; public: uint256 hashContinue; std::atomic nStartingHeight; // flood relay std::vector vAddrToSend; CRollingBloomFilter addrKnown; bool fGetAddr; std::set setKnown; int64_t nNextAddrSend; int64_t nNextLocalAddrSend; // Inventory based relay. CRollingBloomFilter filterInventoryKnown; // Set of transaction ids we still have to announce. They are sorted by the // mempool before relay, so the order is not important. std::set setInventoryTxToSend; // List of block ids we still have announce. There is no final sorting // before sending, as they are always sent immediately and in the order // requested. std::vector vInventoryBlockToSend; CCriticalSection cs_inventory; std::set setAskFor; std::multimap mapAskFor; int64_t nNextInvSend; // Used for headers announcements - unfiltered blocks to relay. Also // protected by cs_inventory. std::vector vBlockHashesToAnnounce; // Used for BIP35 mempool sending, also protected by cs_inventory. bool fSendMempool; // Last time a "MEMPOOL" request was serviced. std::atomic timeLastMempoolReq; // Block and TXN accept times std::atomic nLastBlockTime; std::atomic nLastTXTime; // Ping time measurement: // The pong reply we're expecting, or 0 if no pong expected. std::atomic nPingNonceSent; // Time (in usec) the last ping was sent, or 0 if no ping was ever sent. std::atomic nPingUsecStart; // Last measured round-trip time. std::atomic nPingUsecTime; // Best measured round-trip time. std::atomic nMinPingUsecTime; // Whether a ping is requested. std::atomic fPingQueued; // Minimum fee rate with which to filter inv's to this node Amount minFeeFilter; CCriticalSection cs_feeFilter; Amount lastSentFeeFilter; int64_t nextSendTimeFeeFilter; CNode(NodeId id, ServiceFlags nLocalServicesIn, int nMyStartingHeightIn, SOCKET hSocketIn, const CAddress &addrIn, uint64_t nKeyedNetGroupIn, uint64_t nLocalHostNonceIn, const CAddress &addrBindIn, const std::string &addrNameIn = "", bool fInboundIn = false); ~CNode(); + CNode(const CNode &) = delete; + CNode &operator=(const CNode &) = delete; private: - CNode(const CNode &); - void operator=(const CNode &); const NodeId id; - const uint64_t nLocalHostNonce; // Services offered to this peer const ServiceFlags nLocalServices; const int nMyStartingHeight; int nSendVersion; // Used only by SocketHandler thread. std::list vRecvMsg; mutable CCriticalSection cs_addrName; std::string addrName; // Our address, as reported by the peer CService addrLocal; mutable CCriticalSection cs_addrLocal; public: NodeId GetId() const { return id; } uint64_t GetLocalNonce() const { return nLocalHostNonce; } int GetMyStartingHeight() const { return nMyStartingHeight; } int GetRefCount() const { assert(nRefCount >= 0); return nRefCount; } bool ReceiveMsgBytes(const Config &config, const char *pch, uint32_t nBytes, bool &complete); void SetRecvVersion(int nVersionIn) { nRecvVersion = nVersionIn; } int GetRecvVersion() const { return nRecvVersion; } void SetSendVersion(int nVersionIn); int GetSendVersion() const; CService GetAddrLocal() const; //! May not be called more than once void SetAddrLocal(const CService &addrLocalIn); CNode *AddRef() { nRefCount++; return this; } void Release() { nRefCount--; } void AddAddressKnown(const CAddress &_addr) { addrKnown.insert(_addr.GetKey()); } void PushAddress(const CAddress &_addr, FastRandomContext &insecure_rand) { // Known checking here is only to save space from duplicates. // SendMessages will filter it again for knowns that were added // after addresses were pushed. if (_addr.IsValid() && !addrKnown.contains(_addr.GetKey())) { if (vAddrToSend.size() >= MAX_ADDR_TO_SEND) { vAddrToSend[insecure_rand.randrange(vAddrToSend.size())] = _addr; } else { vAddrToSend.push_back(_addr); } } } void AddInventoryKnown(const CInv &inv) { LOCK(cs_inventory); filterInventoryKnown.insert(inv.hash); } void PushInventory(const CInv &inv) { LOCK(cs_inventory); if (inv.type == MSG_TX) { if (!filterInventoryKnown.contains(inv.hash)) { setInventoryTxToSend.insert(inv.hash); } } else if (inv.type == MSG_BLOCK) { vInventoryBlockToSend.push_back(inv.hash); } } void PushBlockHash(const uint256 &hash) { LOCK(cs_inventory); vBlockHashesToAnnounce.push_back(hash); } void AskFor(const CInv &inv); void CloseSocketDisconnect(); void copyStats(CNodeStats &stats); ServiceFlags GetLocalServices() const { return nLocalServices; } std::string GetAddrName() const; //! Sets the addrName only if it was not previously set void MaybeSetAddrName(const std::string &addrNameIn); }; /** * Return a timestamp in the future (in microseconds) for exponentially * distributed events. */ int64_t PoissonNextSend(int64_t nNow, int average_interval_seconds); std::string getSubVersionEB(uint64_t MaxBlockSize); std::string userAgent(const Config &config); #endif // BITCOIN_NET_H diff --git a/src/streams.h b/src/streams.h index 9c9e1e409..444e05ae9 100644 --- a/src/streams.h +++ b/src/streams.h @@ -1,820 +1,820 @@ // 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_STREAMS_H #define BITCOIN_STREAMS_H #include #include #include #include #include #include #include #include #include #include #include #include #include #include template class OverrideStream { Stream *stream; const int nType; const int nVersion; public: OverrideStream(Stream *stream_, int nType_, int nVersion_) : stream(stream_), nType(nType_), nVersion(nVersion_) {} template OverrideStream &operator<<(const T &obj) { // Serialize to this stream ::Serialize(*this, obj); return (*this); } template OverrideStream &operator>>(T &&obj) { // Unserialize from this stream ::Unserialize(*this, obj); return (*this); } void write(const char *pch, size_t nSize) { stream->write(pch, nSize); } void read(char *pch, size_t nSize) { stream->read(pch, nSize); } int GetVersion() const { return nVersion; } int GetType() const { return nType; } }; template OverrideStream WithOrVersion(S *s, int nVersionFlag) { return OverrideStream(s, s->GetType(), s->GetVersion() | nVersionFlag); } /** * Minimal stream for overwriting and/or appending to an existing byte vector. * * The referenced vector will grow as necessary. */ class CVectorWriter { public: /** * @param[in] nTypeIn Serialization Type * @param[in] nVersionIn Serialization Version (including any flags) * @param[in] vchDataIn Referenced byte vector to overwrite/append * @param[in] nPosIn Starting position. Vector index where writes should * start. The vector will initially grow as necessary to max(nPosIn, * vec.size()). So to append, use vec.size(). */ CVectorWriter(int nTypeIn, int nVersionIn, std::vector &vchDataIn, size_t nPosIn) : nType(nTypeIn), nVersion(nVersionIn), vchData(vchDataIn), nPos(nPosIn) { if (nPos > vchData.size()) vchData.resize(nPos); } /** * (other params same as above) * @param[in] args A list of items to serialize starting at nPosIn. */ template CVectorWriter(int nTypeIn, int nVersionIn, std::vector &vchDataIn, size_t nPosIn, Args &&... args) : CVectorWriter(nTypeIn, nVersionIn, vchDataIn, nPosIn) { ::SerializeMany(*this, std::forward(args)...); } void write(const char *pch, size_t nSize) { assert(nPos <= vchData.size()); size_t nOverwrite = std::min(nSize, vchData.size() - nPos); if (nOverwrite) { memcpy(vchData.data() + nPos, reinterpret_cast(pch), nOverwrite); } if (nOverwrite < nSize) { vchData.insert(vchData.end(), reinterpret_cast(pch) + nOverwrite, reinterpret_cast(pch) + nSize); } nPos += nSize; } template CVectorWriter &operator<<(const T &obj) { // Serialize to this stream ::Serialize(*this, obj); return (*this); } int GetVersion() const { return nVersion; } int GetType() const { return nType; } void seek(size_t nSize) { nPos += nSize; if (nPos > vchData.size()) vchData.resize(nPos); } private: const int nType; const int nVersion; std::vector &vchData; size_t nPos; }; /** * Minimal stream for reading from an existing vector by reference */ class VectorReader { private: const int m_type; const int m_version; const std::vector &m_data; size_t m_pos = 0; public: /** * @param[in] type Serialization Type * @param[in] version Serialization Version (including any flags) * @param[in] data Referenced byte vector to overwrite/append * @param[in] pos Starting position. Vector index where reads should start. */ VectorReader(int type, int version, const std::vector &data, size_t pos) : m_type(type), m_version(version), m_data(data), m_pos(pos) { if (m_pos > m_data.size()) { throw std::ios_base::failure( "VectorReader(...): end of data (m_pos > m_data.size())"); } } /** * (other params same as above) * @param[in] args A list of items to deserialize starting at pos. */ template VectorReader(int type, int version, const std::vector &data, size_t pos, Args &&... args) : VectorReader(type, version, data, pos) { ::UnserializeMany(*this, std::forward(args)...); } template VectorReader &operator>>(T &obj) { // Unserialize from this stream ::Unserialize(*this, obj); return (*this); } int GetVersion() const { return m_version; } int GetType() const { return m_type; } size_t size() const { return m_data.size() - m_pos; } bool empty() const { return m_data.size() == m_pos; } void read(char *dst, size_t n) { if (n == 0) { return; } // Read from the beginning of the buffer size_t pos_next = m_pos + n; if (pos_next > m_data.size()) { throw std::ios_base::failure("VectorReader::read(): end of data"); } memcpy(dst, m_data.data() + m_pos, n); m_pos = pos_next; } }; /** * Double ended buffer combining vector and stream-like interfaces. * * >> and << read and write unformatted data using the above serialization * templates. Fills with data in linear time; some stringstream implementations * take N^2 time. */ class CDataStream { protected: typedef CSerializeData vector_type; vector_type vch; unsigned int nReadPos; int nType; int nVersion; public: typedef vector_type::allocator_type allocator_type; typedef vector_type::size_type size_type; typedef vector_type::difference_type difference_type; typedef vector_type::reference reference; typedef vector_type::const_reference const_reference; typedef vector_type::value_type value_type; typedef vector_type::iterator iterator; typedef vector_type::const_iterator const_iterator; typedef vector_type::reverse_iterator reverse_iterator; explicit CDataStream(int nTypeIn, int nVersionIn) { Init(nTypeIn, nVersionIn); } CDataStream(const_iterator pbegin, const_iterator pend, int nTypeIn, int nVersionIn) : vch(pbegin, pend) { Init(nTypeIn, nVersionIn); } CDataStream(const char *pbegin, const char *pend, int nTypeIn, int nVersionIn) : vch(pbegin, pend) { Init(nTypeIn, nVersionIn); } CDataStream(const vector_type &vchIn, int nTypeIn, int nVersionIn) : vch(vchIn.begin(), vchIn.end()) { Init(nTypeIn, nVersionIn); } CDataStream(const std::vector &vchIn, int nTypeIn, int nVersionIn) : vch(vchIn.begin(), vchIn.end()) { Init(nTypeIn, nVersionIn); } CDataStream(const std::vector &vchIn, int nTypeIn, int nVersionIn) : vch(vchIn.begin(), vchIn.end()) { Init(nTypeIn, nVersionIn); } template CDataStream(int nTypeIn, int nVersionIn, Args &&... args) { Init(nTypeIn, nVersionIn); ::SerializeMany(*this, std::forward(args)...); } void Init(int nTypeIn, int nVersionIn) { nReadPos = 0; nType = nTypeIn; nVersion = nVersionIn; } CDataStream &operator+=(const CDataStream &b) { vch.insert(vch.end(), b.begin(), b.end()); return *this; } friend CDataStream operator+(const CDataStream &a, const CDataStream &b) { CDataStream ret = a; ret += b; return (ret); } std::string str() const { return (std::string(begin(), end())); } // // Vector subset // const_iterator begin() const { return vch.begin() + nReadPos; } iterator begin() { return vch.begin() + nReadPos; } const_iterator end() const { return vch.end(); } iterator end() { return vch.end(); } size_type size() const { return vch.size() - nReadPos; } bool empty() const { return vch.size() == nReadPos; } void resize(size_type n, value_type c = 0) { vch.resize(n + nReadPos, c); } void reserve(size_type n) { vch.reserve(n + nReadPos); } const_reference operator[](size_type pos) const { return vch[pos + nReadPos]; } reference operator[](size_type pos) { return vch[pos + nReadPos]; } void clear() { vch.clear(); nReadPos = 0; } iterator insert(iterator it, const char x = char()) { return vch.insert(it, x); } void insert(iterator it, size_type n, const char x) { vch.insert(it, n, x); } value_type *data() { return vch.data() + nReadPos; } const value_type *data() const { return vch.data() + nReadPos; } void insert(iterator it, std::vector::const_iterator first, std::vector::const_iterator last) { if (last == first) { return; } assert(last - first > 0); if (it == vch.begin() + nReadPos && (unsigned int)(last - first) <= nReadPos) { // special case for inserting at the front when there's room nReadPos -= (last - first); memcpy(&vch[nReadPos], &first[0], last - first); } else { vch.insert(it, first, last); } } void insert(iterator it, const char *first, const char *last) { if (last == first) { return; } assert(last - first > 0); if (it == vch.begin() + nReadPos && (unsigned int)(last - first) <= nReadPos) { // special case for inserting at the front when there's room nReadPos -= (last - first); memcpy(&vch[nReadPos], &first[0], last - first); } else { vch.insert(it, first, last); } } iterator erase(iterator it) { if (it == vch.begin() + nReadPos) { // special case for erasing from the front if (++nReadPos >= vch.size()) { // whenever we reach the end, we take the opportunity to clear // the buffer nReadPos = 0; return vch.erase(vch.begin(), vch.end()); } return vch.begin() + nReadPos; } else { return vch.erase(it); } } iterator erase(iterator first, iterator last) { if (first == vch.begin() + nReadPos) { // special case for erasing from the front if (last == vch.end()) { nReadPos = 0; return vch.erase(vch.begin(), vch.end()); } else { nReadPos = (last - vch.begin()); return last; } } else return vch.erase(first, last); } inline void Compact() { vch.erase(vch.begin(), vch.begin() + nReadPos); nReadPos = 0; } bool Rewind(size_type n) { // Rewind by n characters if the buffer hasn't been compacted yet if (n > nReadPos) return false; nReadPos -= n; return true; } // // Stream subset // bool eof() const { return size() == 0; } CDataStream *rdbuf() { return this; } int in_avail() const { return size(); } void SetType(int n) { nType = n; } int GetType() const { return nType; } void SetVersion(int n) { nVersion = n; } int GetVersion() const { return nVersion; } void read(char *pch, size_t nSize) { if (nSize == 0) { return; } // Read from the beginning of the buffer unsigned int nReadPosNext = nReadPos + nSize; if (nReadPosNext > vch.size()) { throw std::ios_base::failure("CDataStream::read(): end of data"); } memcpy(pch, &vch[nReadPos], nSize); if (nReadPosNext == vch.size()) { nReadPos = 0; vch.clear(); return; } nReadPos = nReadPosNext; } void ignore(int nSize) { // Ignore from the beginning of the buffer if (nSize < 0) { throw std::ios_base::failure( "CDataStream::ignore(): nSize negative"); } unsigned int nReadPosNext = nReadPos + nSize; if (nReadPosNext >= vch.size()) { if (nReadPosNext > vch.size()) throw std::ios_base::failure( "CDataStream::ignore(): end of data"); nReadPos = 0; vch.clear(); return; } nReadPos = nReadPosNext; } void write(const char *pch, size_t nSize) { // Write to the end of the buffer vch.insert(vch.end(), pch, pch + nSize); } template void Serialize(Stream &s) const { // Special case: stream << stream concatenates like stream += stream if (!vch.empty()) s.write((char *)&vch[0], vch.size() * sizeof(vch[0])); } template CDataStream &operator<<(const T &obj) { // Serialize to this stream ::Serialize(*this, obj); return (*this); } template CDataStream &operator>>(T &&obj) { // Unserialize from this stream ::Unserialize(*this, obj); return (*this); } void GetAndClear(CSerializeData &d) { d.insert(d.end(), begin(), end()); clear(); } /** * XOR the contents of this stream with a certain key. * * @param[in] key The key used to XOR the data in this stream. */ void Xor(const std::vector &key) { if (key.size() == 0) { return; } for (size_type i = 0, j = 0; i != size(); i++) { vch[i] ^= key[j++]; // This potentially acts on very many bytes of data, so it's // important that we calculate `j`, i.e. the `key` index in this way // instead of doing a %, which would effectively be a division for // each byte Xor'd -- much slower than need be. if (j == key.size()) j = 0; } } }; template class BitStreamReader { private: IStream &m_istream; /// Buffered byte read in from the input stream. A new byte is read into the /// buffer when m_offset reaches 8. uint8_t m_buffer{0}; /// Number of high order bits in m_buffer already returned by previous /// Read() calls. The next bit to be returned is at this offset from the /// most significant bit position. int m_offset{8}; public: explicit BitStreamReader(IStream &istream) : m_istream(istream) {} /** * Read the specified number of bits from the stream. The data is returned * in the nbits least significant bits of a 64-bit uint. */ uint64_t Read(int nbits) { if (nbits < 0 || nbits > 64) { throw std::out_of_range("nbits must be between 0 and 64"); } uint64_t data = 0; while (nbits > 0) { if (m_offset == 8) { m_istream >> m_buffer; m_offset = 0; } int bits = std::min(8 - m_offset, nbits); data <<= bits; data |= static_cast(m_buffer << m_offset) >> (8 - bits); m_offset += bits; nbits -= bits; } return data; } }; template class BitStreamWriter { private: OStream &m_ostream; /// Buffered byte waiting to be written to the output stream. The byte is /// written buffer when m_offset reaches 8 or Flush() is called. uint8_t m_buffer{0}; /// Number of high order bits in m_buffer already written by previous /// Write() calls and not yet flushed to the stream. The next bit to be /// written to is at this offset from the most significant bit position. int m_offset{0}; public: explicit BitStreamWriter(OStream &ostream) : m_ostream(ostream) {} ~BitStreamWriter() { Flush(); } /** * Write the nbits least significant bits of a 64-bit int to the output * stream. Data is buffered until it completes an octet. */ void Write(uint64_t data, int nbits) { if (nbits < 0 || nbits > 64) { throw std::out_of_range("nbits must be between 0 and 64"); } while (nbits > 0) { int bits = std::min(8 - m_offset, nbits); m_buffer |= (data << (64 - nbits)) >> (64 - 8 + m_offset); m_offset += bits; nbits -= bits; if (m_offset == 8) { Flush(); } } } /** * Flush any unwritten bits to the output stream, padding with 0's to the * next byte boundary. */ void Flush() { if (m_offset == 0) { return; } m_ostream << m_buffer; m_buffer = 0; m_offset = 0; } }; /** * Non-refcounted RAII wrapper for FILE* * * Will automatically close the file when it goes out of scope if not null. If * you're returning the file pointer, return file.release(). If you need to * close the file early, use file.fclose() instead of fclose(file). */ class CAutoFile { private: - // Disallow copies - CAutoFile(const CAutoFile &); - CAutoFile &operator=(const CAutoFile &); - const int nType; const int nVersion; FILE *file; public: CAutoFile(FILE *filenew, int nTypeIn, int nVersionIn) : nType(nTypeIn), nVersion(nVersionIn) { file = filenew; } ~CAutoFile() { fclose(); } + // Disallow copies + CAutoFile(const CAutoFile &) = delete; + CAutoFile &operator=(const CAutoFile &) = delete; + void fclose() { if (file) { ::fclose(file); file = nullptr; } } /** * Get wrapped FILE* with transfer of ownership. * @note This will invalidate the CAutoFile object, and makes it the * responsibility of the caller of this function to clean up the returned * FILE*. */ FILE *release() { FILE *ret = file; file = nullptr; return ret; } /** * Get wrapped FILE* without transfer of ownership. * @note Ownership of the FILE* will remain with this class. Use this only * if the scope of the CAutoFile outlives use of the passed pointer. */ FILE *Get() const { return file; } /** Return true if the wrapped FILE* is nullptr, false otherwise. */ bool IsNull() const { return (file == nullptr); } // // Stream subset // int GetType() const { return nType; } int GetVersion() const { return nVersion; } void read(char *pch, size_t nSize) { if (!file) throw std::ios_base::failure( "CAutoFile::read: file handle is nullptr"); if (fread(pch, 1, nSize, file) != nSize) throw std::ios_base::failure(feof(file) ? "CAutoFile::read: end of file" : "CAutoFile::read: fread failed"); } void ignore(size_t nSize) { if (!file) throw std::ios_base::failure( "CAutoFile::ignore: file handle is nullptr"); uint8_t data[4096]; while (nSize > 0) { size_t nNow = std::min(nSize, sizeof(data)); if (fread(data, 1, nNow, file) != nNow) throw std::ios_base::failure( feof(file) ? "CAutoFile::ignore: end of file" : "CAutoFile::read: fread failed"); nSize -= nNow; } } void write(const char *pch, size_t nSize) { if (!file) throw std::ios_base::failure( "CAutoFile::write: file handle is nullptr"); if (fwrite(pch, 1, nSize, file) != nSize) throw std::ios_base::failure("CAutoFile::write: write failed"); } template CAutoFile &operator<<(const T &obj) { // Serialize to this stream if (!file) throw std::ios_base::failure( "CAutoFile::operator<<: file handle is nullptr"); ::Serialize(*this, obj); return (*this); } template CAutoFile &operator>>(T &&obj) { // Unserialize from this stream if (!file) throw std::ios_base::failure( "CAutoFile::operator>>: file handle is nullptr"); ::Unserialize(*this, obj); return (*this); } }; /** * Non-refcounted RAII wrapper around a FILE* that implements a ring buffer to * deserialize from. It guarantees the ability to rewind a given number of * bytes. * * Will automatically close the file when it goes out of scope if not null. If * you need to close the file early, use file.fclose() instead of fclose(file). */ class CBufferedFile { private: - // Disallow copies - CBufferedFile(const CBufferedFile &); - CBufferedFile &operator=(const CBufferedFile &); - const int nType; const int nVersion; // source file FILE *src; // how many bytes have been read from source uint64_t nSrcPos; // how many bytes have been read from this uint64_t nReadPos; // up to which position we're allowed to read uint64_t nReadLimit; // how many bytes we guarantee to rewind uint64_t nRewind; // the buffer std::vector vchBuf; protected: // read data from the source to fill the buffer bool Fill() { unsigned int pos = nSrcPos % vchBuf.size(); unsigned int readNow = vchBuf.size() - pos; unsigned int nAvail = vchBuf.size() - (nSrcPos - nReadPos) - nRewind; if (nAvail < readNow) readNow = nAvail; if (readNow == 0) return false; size_t nBytes = fread((void *)&vchBuf[pos], 1, readNow, src); if (nBytes == 0) { throw std::ios_base::failure( feof(src) ? "CBufferedFile::Fill: end of file" : "CBufferedFile::Fill: fread failed"); } else { nSrcPos += nBytes; return true; } } public: CBufferedFile(FILE *fileIn, uint64_t nBufSize, uint64_t nRewindIn, int nTypeIn, int nVersionIn) : nType(nTypeIn), nVersion(nVersionIn), nSrcPos(0), nReadPos(0), nReadLimit((uint64_t)(-1)), nRewind(nRewindIn), vchBuf(nBufSize, 0) { src = fileIn; } ~CBufferedFile() { fclose(); } + // Disallow copies + CBufferedFile(const CBufferedFile &) = delete; + CBufferedFile &operator=(const CBufferedFile &) = delete; + int GetVersion() const { return nVersion; } int GetType() const { return nType; } void fclose() { if (src) { ::fclose(src); src = nullptr; } } // check whether we're at the end of the source file bool eof() const { return nReadPos == nSrcPos && feof(src); } // read a number of bytes void read(char *pch, size_t nSize) { if (nSize + nReadPos > nReadLimit) throw std::ios_base::failure("Read attempted past buffer limit"); if (nSize + nRewind > vchBuf.size()) throw std::ios_base::failure("Read larger than buffer size"); while (nSize > 0) { if (nReadPos == nSrcPos) Fill(); unsigned int pos = nReadPos % vchBuf.size(); size_t nNow = nSize; if (nNow + pos > vchBuf.size()) nNow = vchBuf.size() - pos; if (nNow + nReadPos > nSrcPos) nNow = nSrcPos - nReadPos; memcpy(pch, &vchBuf[pos], nNow); nReadPos += nNow; pch += nNow; nSize -= nNow; } } // return the current reading position uint64_t GetPos() const { return nReadPos; } // rewind to a given reading position bool SetPos(uint64_t nPos) { nReadPos = nPos; if (nReadPos + nRewind < nSrcPos) { nReadPos = nSrcPos - nRewind; return false; } else if (nReadPos > nSrcPos) { nReadPos = nSrcPos; return false; } else { return true; } } bool Seek(uint64_t nPos) { long nLongPos = nPos; if (nPos != (uint64_t)nLongPos) return false; if (fseek(src, nLongPos, SEEK_SET)) return false; nLongPos = ftell(src); nSrcPos = nLongPos; nReadPos = nLongPos; return true; } // Prevent reading beyond a certain position. No argument removes the limit. bool SetLimit(uint64_t nPos = (uint64_t)(-1)) { if (nPos < nReadPos) return false; nReadLimit = nPos; return true; } template CBufferedFile &operator>>(T &&obj) { // Unserialize from this stream ::Unserialize(*this, obj); return (*this); } // search for a given byte in the stream, and remain positioned on it void FindByte(char ch) { while (true) { if (nReadPos == nSrcPos) Fill(); if (vchBuf[nReadPos % vchBuf.size()] == ch) break; nReadPos++; } } }; #endif // BITCOIN_STREAMS_H diff --git a/src/support/lockedpool.h b/src/support/lockedpool.h index 34fc87cc7..89220866f 100644 --- a/src/support/lockedpool.h +++ b/src/support/lockedpool.h @@ -1,249 +1,245 @@ // 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. #ifndef BITCOIN_SUPPORT_LOCKEDPOOL_H #define BITCOIN_SUPPORT_LOCKEDPOOL_H #include #include #include #include #include /** * OS-dependent allocation and deallocation of locked/pinned memory pages. * Abstract base class. */ class LockedPageAllocator { public: virtual ~LockedPageAllocator() {} /** * Allocate and lock memory pages. * If len is not a multiple of the system page size, it is rounded up. * Returns 0 in case of allocation failure. * * If locking the memory pages could not be accomplished it will still * return the memory, however the lockingSuccess flag will be false. * lockingSuccess is undefined if the allocation fails. */ virtual void *AllocateLocked(size_t len, bool *lockingSuccess) = 0; /** * Unlock and free memory pages. * Clear the memory before unlocking. */ virtual void FreeLocked(void *addr, size_t len) = 0; /** * Get the total limit on the amount of memory that may be locked by this * process, in bytes. Return size_t max if there is no limit or the limit is * unknown. Return 0 if no memory can be locked at all. */ virtual size_t GetLimit() = 0; }; /** * An arena manages a contiguous region of memory by dividing it into chunks. */ class Arena { public: Arena(void *base, size_t size, size_t alignment); virtual ~Arena(); + Arena(const Arena &other) = delete; // non construction-copyable + Arena &operator=(const Arena &) = delete; // non copyable + /** Memory statistics. */ struct Stats { size_t used; size_t free; size_t total; size_t chunks_used; size_t chunks_free; }; /** * Allocate size bytes from this arena. * Returns pointer on success, or 0 if memory is full or the application * tried to allocate 0 bytes. */ void *alloc(size_t size); /** * Free a previously allocated chunk of memory. * Freeing the zero pointer has no effect. * Raises std::runtime_error in case of error. */ void free(void *ptr); /** Get arena usage statistics */ Stats stats() const; #ifdef ARENA_DEBUG void walk() const; #endif /** * Return whether a pointer points inside this arena. * This returns base <= ptr < (base+size) so only use it for (inclusive) * chunk starting addresses. */ bool addressInArena(void *ptr) const { return ptr >= base && ptr < end; } private: - // non construction-copyable - Arena(const Arena &other) = delete; - // non copyable - Arena &operator=(const Arena &) = delete; - /** * Map of chunk address to chunk information. This class makes use of the * sorted order to merge previous and next chunks during deallocation. */ std::map chunks_free; std::map chunks_used; /** Base address of arena */ char *base; /** End address of arena */ char *end; /** Minimum chunk alignment */ size_t alignment; }; /** * Pool for locked memory chunks. * * To avoid sensitive key data from being swapped to disk, the memory in this * pool is locked/pinned. * * An arena manages a contiguous region of memory. The pool starts out with one * arena but can grow to multiple arenas if the need arises. * * Unlike a normal C heap, the administrative structures are separate from the * managed memory. This has been done as the sizes and bases of objects are not * in themselves sensitive information, as to conserve precious locked memory. * In some operating systems the amount of memory that can be locked is small. */ class LockedPool { public: /** * Size of one arena of locked memory. This is a compromise. * Do not set this too low, as managing many arenas will increase allocation * and deallocation overhead. Setting it too high allocates more locked * memory from the OS than strictly necessary. */ static const size_t ARENA_SIZE = 256 * 1024; /** * Chunk alignment. Another compromise. Setting this too high will waste * memory, setting it too low will facilitate fragmentation. */ static const size_t ARENA_ALIGN = 16; /** * Callback when allocation succeeds but locking fails. */ typedef bool (*LockingFailed_Callback)(); /** Memory statistics. */ struct Stats { size_t used; size_t free; size_t total; size_t locked; size_t chunks_used; size_t chunks_free; }; /** * Create a new LockedPool. This takes ownership of the MemoryPageLocker, * you can only instantiate this with LockedPool(std::move(...)). * * The second argument is an optional callback when locking a newly * allocated arena failed. If this callback is provided and returns false, * the allocation fails (hard fail), if it returns true the allocation * proceeds, but it could warn. */ explicit LockedPool(std::unique_ptr allocator, LockingFailed_Callback lf_cb_in = nullptr); ~LockedPool(); + LockedPool(const LockedPool &other) = delete; // non construction-copyable + LockedPool &operator=(const LockedPool &) = delete; // non copyable + /** * Allocate size bytes from this arena. * Returns pointer on success, or 0 if memory is full or the application * tried to allocate 0 bytes. */ void *alloc(size_t size); /** * Free a previously allocated chunk of memory. * Freeing the zero pointer has no effect. * Raises std::runtime_error in case of error. */ void free(void *ptr); /** Get pool usage statistics */ Stats stats() const; private: - // non construction-copyable - LockedPool(const LockedPool &other) = delete; - // non copyable - LockedPool &operator=(const LockedPool &) = delete; - std::unique_ptr allocator; /** Create an arena from locked pages */ class LockedPageArena : public Arena { public: LockedPageArena(LockedPageAllocator *alloc_in, void *base_in, size_t size, size_t align); ~LockedPageArena(); private: void *base; size_t size; LockedPageAllocator *allocator; }; bool new_arena(size_t size, size_t align); std::list arenas; LockingFailed_Callback lf_cb; size_t cumulative_bytes_locked; /** * Mutex protects access to this pool's data structures, including arenas. */ mutable std::mutex mutex; }; /** * Singleton class to keep track of locked (ie, non-swappable) memory, for use * in std::allocator templates. * * Some implementations of the STL allocate memory in some constructors (i.e., * see MSVC's vector implementation where it allocates 1 byte of memory in * the allocator). Due to the unpredictable order of static initializers, we * have to make sure the LockedPoolManager instance exists before any other * STL-based objects that use secure_allocator are created. So instead of having * LockedPoolManager also be static-initialized, it is created on demand. */ class LockedPoolManager : public LockedPool { public: /** Return the current instance, or create it once */ static LockedPoolManager &Instance() { std::call_once(LockedPoolManager::init_flag, LockedPoolManager::CreateInstance); return *LockedPoolManager::_instance; } private: explicit LockedPoolManager(std::unique_ptr allocator); /** Create a new LockedPoolManager specialized to the OS */ static void CreateInstance(); /** Called when locking fails, warn the user here */ static bool LockingFailed(); static LockedPoolManager *_instance; static std::once_flag init_flag; }; #endif // BITCOIN_SUPPORT_LOCKEDPOOL_H diff --git a/src/wallet/db.h b/src/wallet/db.h index ad9618e5c..826aa7b3c 100644 --- a/src/wallet/db.h +++ b/src/wallet/db.h @@ -1,389 +1,388 @@ // 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_WALLET_DB_H #define BITCOIN_WALLET_DB_H #include #include #include #include #include #include #include #include #include #include #include static const unsigned int DEFAULT_WALLET_DBLOGSIZE = 100; static const bool DEFAULT_WALLET_PRIVDB = true; class CDBEnv { private: bool fDbEnvInit; bool fMockDb; // Don't change into fs::path, as that can result in // shutdown problems/crashes caused by a static initialized internal // pointer. std::string strPath; void EnvShutdown(); public: mutable CCriticalSection cs_db; std::unique_ptr dbenv; std::map mapFileUseCount; std::map mapDb; CDBEnv(); ~CDBEnv(); void Reset(); void MakeMock(); bool IsMock() const { return fMockDb; } /** * Verify that database file strFile is OK. If it is not, call the callback * to try to recover. * This must be called BEFORE strFile is opened. * Returns true if strFile is OK. */ enum class VerifyResult { VERIFY_OK, RECOVER_OK, RECOVER_FAIL }; typedef bool (*recoverFunc_type)(const std::string &strFile, std::string &out_backup_filename); VerifyResult Verify(const std::string &strFile, recoverFunc_type recoverFunc, std::string &out_backup_filename); /** * Salvage data from a file that Verify says is bad. * fAggressive sets the DB_AGGRESSIVE flag (see berkeley DB->verify() method * documentation). * Appends binary key/value pairs to vResult, returns true if successful. * NOTE: reads the entire database into memory, so cannot be used * for huge databases. */ typedef std::pair, std::vector> KeyValPair; bool Salvage(const std::string &strFile, bool fAggressive, std::vector &vResult); bool Open(const fs::path &path, bool retry = 0); void Close(); void Flush(bool fShutdown); void CheckpointLSN(const std::string &strFile); void CloseDb(const std::string &strFile); DbTxn *TxnBegin(int flags = DB_TXN_WRITE_NOSYNC) { DbTxn *ptxn = nullptr; int ret = dbenv->txn_begin(nullptr, &ptxn, flags); if (!ptxn || ret != 0) return nullptr; return ptxn; } }; extern CDBEnv bitdb; /** * An instance of this class represents one database. * For BerkeleyDB this is just a (env, strFile) tuple. */ class CWalletDBWrapper { friend class CDB; public: /** Create dummy DB handle */ CWalletDBWrapper() : nLastSeen(0), nLastFlushed(0), nLastWalletUpdate(0), env(nullptr) {} /** Create DB handle to real database */ CWalletDBWrapper(CDBEnv *env_in, const std::string &strFile_in) : nLastSeen(0), nLastFlushed(0), nLastWalletUpdate(0), env(env_in), strFile(strFile_in) {} /** Rewrite the entire database on disk, with the exception of key pszSkip * if non-zero */ bool Rewrite(const char *pszSkip = nullptr); /** Back up the entire database to a file. */ bool Backup(const std::string &strDest); /** Get a name for this database, for debugging etc. */ std::string GetName() const { return strFile; } /** Make sure all changes are flushed to disk. */ void Flush(bool shutdown); void IncrementUpdateCounter(); std::atomic nUpdateCounter; unsigned int nLastSeen; unsigned int nLastFlushed; int64_t nLastWalletUpdate; private: /** BerkeleyDB specific */ CDBEnv *env; std::string strFile; /** * Return whether this database handle is a dummy for testing. * Only to be used at a low level, application should ideally not care * about this. */ bool IsDummy() { return env == nullptr; } }; /** RAII class that provides access to a Berkeley database */ class CDB { protected: Db *pdb; std::string strFile; DbTxn *activeTxn; bool fReadOnly; bool fFlushOnClose; CDBEnv *env; public: explicit CDB(CWalletDBWrapper &dbw, const char *pszMode = "r+", bool fFlushOnCloseIn = true); ~CDB() { Close(); } + CDB(const CDB &) = delete; + CDB &operator=(const CDB &) = delete; + void Flush(); void Close(); static bool Recover(const std::string &filename, void *callbackDataIn, bool (*recoverKVcallback)(void *callbackData, CDataStream ssKey, CDataStream ssValue), std::string &out_backup_filename); /* flush the wallet passively (TRY_LOCK) ideal to be called periodically */ static bool PeriodicFlush(CWalletDBWrapper &dbw); /* verifies the database environment */ static bool VerifyEnvironment(const std::string &walletFile, const fs::path &walletDir, std::string &errorStr); /* verifies the database file */ static bool VerifyDatabaseFile(const std::string &walletFile, const fs::path &walletDir, std::string &warningStr, std::string &errorStr, CDBEnv::recoverFunc_type recoverFunc); -private: - CDB(const CDB &); - void operator=(const CDB &); - public: template bool Read(const K &key, T &value) { if (!pdb) { return false; } // Key CDataStream ssKey(SER_DISK, CLIENT_VERSION); ssKey.reserve(1000); ssKey << key; Dbt datKey(ssKey.data(), ssKey.size()); // Read Dbt datValue; datValue.set_flags(DB_DBT_MALLOC); int ret = pdb->get(activeTxn, &datKey, &datValue, 0); memset(datKey.get_data(), 0, datKey.get_size()); if (datValue.get_data() == nullptr) { return false; } // Unserialize value try { CDataStream ssValue((char *)datValue.get_data(), (char *)datValue.get_data() + datValue.get_size(), SER_DISK, CLIENT_VERSION); ssValue >> value; } catch (const std::exception &) { return false; } // Clear and free memory memset(datValue.get_data(), 0, datValue.get_size()); free(datValue.get_data()); return (ret == 0); } template bool Write(const K &key, const T &value, bool fOverwrite = true) { if (!pdb) { return true; } if (fReadOnly) { assert(!"Write called on database in read-only mode"); } // Key CDataStream ssKey(SER_DISK, CLIENT_VERSION); ssKey.reserve(1000); ssKey << key; Dbt datKey(ssKey.data(), ssKey.size()); // Value CDataStream ssValue(SER_DISK, CLIENT_VERSION); ssValue.reserve(10000); ssValue << value; Dbt datValue(ssValue.data(), ssValue.size()); // Write int ret = pdb->put(activeTxn, &datKey, &datValue, (fOverwrite ? 0 : DB_NOOVERWRITE)); // Clear memory in case it was a private key memset(datKey.get_data(), 0, datKey.get_size()); memset(datValue.get_data(), 0, datValue.get_size()); return (ret == 0); } template bool Erase(const K &key) { if (!pdb) { return false; } if (fReadOnly) { assert(!"Erase called on database in read-only mode"); } // Key CDataStream ssKey(SER_DISK, CLIENT_VERSION); ssKey.reserve(1000); ssKey << key; Dbt datKey(ssKey.data(), ssKey.size()); // Erase int ret = pdb->del(activeTxn, &datKey, 0); // Clear memory memset(datKey.get_data(), 0, datKey.get_size()); return (ret == 0 || ret == DB_NOTFOUND); } template bool Exists(const K &key) { if (!pdb) { return false; } // Key CDataStream ssKey(SER_DISK, CLIENT_VERSION); ssKey.reserve(1000); ssKey << key; Dbt datKey(ssKey.data(), ssKey.size()); // Exists int ret = pdb->exists(activeTxn, &datKey, 0); // Clear memory memset(datKey.get_data(), 0, datKey.get_size()); return (ret == 0); } Dbc *GetCursor() { if (!pdb) { return nullptr; } Dbc *pcursor = nullptr; int ret = pdb->cursor(nullptr, &pcursor, 0); if (ret != 0) { return nullptr; } return pcursor; } int ReadAtCursor(Dbc *pcursor, CDataStream &ssKey, CDataStream &ssValue, bool setRange = false) { // Read at cursor Dbt datKey; unsigned int fFlags = DB_NEXT; if (setRange) { datKey.set_data(ssKey.data()); datKey.set_size(ssKey.size()); fFlags = DB_SET_RANGE; } Dbt datValue; datKey.set_flags(DB_DBT_MALLOC); datValue.set_flags(DB_DBT_MALLOC); int ret = pcursor->get(&datKey, &datValue, fFlags); if (ret != 0) { return ret; } else if (datKey.get_data() == nullptr || datValue.get_data() == nullptr) { return 99999; } // Convert to streams ssKey.SetType(SER_DISK); ssKey.clear(); ssKey.write((char *)datKey.get_data(), datKey.get_size()); ssValue.SetType(SER_DISK); ssValue.clear(); ssValue.write((char *)datValue.get_data(), datValue.get_size()); // Clear and free memory memset(datKey.get_data(), 0, datKey.get_size()); memset(datValue.get_data(), 0, datValue.get_size()); free(datKey.get_data()); free(datValue.get_data()); return 0; } public: bool TxnBegin() { if (!pdb || activeTxn) { return false; } DbTxn *ptxn = bitdb.TxnBegin(); if (!ptxn) { return false; } activeTxn = ptxn; return true; } bool TxnCommit() { if (!pdb || !activeTxn) { return false; } int ret = activeTxn->commit(0); activeTxn = nullptr; return (ret == 0); } bool TxnAbort() { if (!pdb || !activeTxn) { return false; } int ret = activeTxn->abort(); activeTxn = nullptr; return (ret == 0); } bool ReadVersion(int &nVersion) { nVersion = 0; return Read(std::string("version"), nVersion); } bool WriteVersion(int nVersion) { return Write(std::string("version"), nVersion); } static bool Rewrite(CWalletDBWrapper &dbw, const char *pszSkip = nullptr); }; #endif // BITCOIN_WALLET_DB_H diff --git a/src/wallet/walletdb.h b/src/wallet/walletdb.h index 68fe703aa..b93947d1d 100644 --- a/src/wallet/walletdb.h +++ b/src/wallet/walletdb.h @@ -1,277 +1,276 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2009-2016 The Bitcoin Core developers // Copyright (c) 2017 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_WALLET_WALLETDB_H #define BITCOIN_WALLET_WALLETDB_H #include #include #include #include