diff --git a/src/addrdb.cpp b/src/addrdb.cpp index 0c8b908da..96fb1fe27 100644 --- a/src/addrdb.cpp +++ b/src/addrdb.cpp @@ -1,147 +1,147 @@ // 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. #include <addrdb.h> #include <addrman.h> #include <chainparams.h> #include <clientversion.h> #include <fs.h> #include <hash.h> #include <random.h> #include <streams.h> #include <tinyformat.h> #include <util/system.h> namespace { template <typename Stream, typename Data> bool SerializeDB(const CChainParams &chainParams, Stream &stream, const Data &data) { // Write and commit header, data try { CHashWriter hasher(SER_DISK, CLIENT_VERSION); - stream << FLATDATA(chainParams.DiskMagic()) << data; - hasher << FLATDATA(chainParams.DiskMagic()) << data; + stream << chainParams.DiskMagic() << data; + hasher << chainParams.DiskMagic() << data; stream << hasher.GetHash(); } catch (const std::exception &e) { return error("%s: Serialize or I/O error - %s", __func__, e.what()); } return true; } template <typename Data> bool SerializeFileDB(const CChainParams &chainParams, const std::string &prefix, const fs::path &path, const Data &data) { // Generate random temporary filename unsigned short randv = 0; GetRandBytes((uint8_t *)&randv, sizeof(randv)); std::string tmpfn = strprintf("%s.%04x", prefix, randv); // open temp output file, and associate with CAutoFile fs::path pathTmp = GetDataDir() / tmpfn; FILE *file = fsbridge::fopen(pathTmp, "wb"); CAutoFile fileout(file, SER_DISK, CLIENT_VERSION); if (fileout.IsNull()) { return error("%s: Failed to open file %s", __func__, pathTmp.string()); } // Serialize if (!SerializeDB(chainParams, fileout, data)) { return false; } if (!FileCommit(fileout.Get())) { return error("%s: Failed to flush file %s", __func__, pathTmp.string()); } fileout.fclose(); // replace existing file, if any, with new file if (!RenameOver(pathTmp, path)) { return error("%s: Rename-into-place failed", __func__); } return true; } template <typename Stream, typename Data> bool DeserializeDB(const CChainParams &chainParams, Stream &stream, Data &data, bool fCheckSum = true) { try { CHashVerifier<Stream> verifier(&stream); // de-serialize file header (network specific magic number) and .. uint8_t pchMsgTmp[4]; - verifier >> FLATDATA(pchMsgTmp); + verifier >> pchMsgTmp; // ... verify the network matches ours if (memcmp(pchMsgTmp, std::begin(chainParams.DiskMagic()), sizeof(pchMsgTmp))) { return error("%s: Invalid network magic number", __func__); } // de-serialize data verifier >> data; // verify checksum if (fCheckSum) { uint256 hashTmp; stream >> hashTmp; if (hashTmp != verifier.GetHash()) { return error("%s: Checksum mismatch, data corrupted", __func__); } } } catch (const std::exception &e) { return error("%s: Deserialize or I/O error - %s", __func__, e.what()); } return true; } template <typename Data> bool DeserializeFileDB(const CChainParams &chainParams, const fs::path &path, Data &data) { // open input file, and associate with CAutoFile FILE *file = fsbridge::fopen(path, "rb"); CAutoFile filein(file, SER_DISK, CLIENT_VERSION); if (filein.IsNull()) { return error("%s: Failed to open file %s", __func__, path.string()); } return DeserializeDB(chainParams, filein, data); } } // namespace CBanDB::CBanDB(fs::path ban_list_path, const CChainParams &_chainParams) : m_ban_list_path(std::move(ban_list_path)), chainParams(_chainParams) {} bool CBanDB::Write(const banmap_t &banSet) { return SerializeFileDB(chainParams, "banlist", m_ban_list_path, banSet); } bool CBanDB::Read(banmap_t &banSet) { return DeserializeFileDB(chainParams, m_ban_list_path, banSet); } CAddrDB::CAddrDB(const CChainParams &chainParamsIn) : chainParams(chainParamsIn) { pathAddr = GetDataDir() / "peers.dat"; } bool CAddrDB::Write(const CAddrMan &addr) { return SerializeFileDB(chainParams, "peers", pathAddr, addr); } bool CAddrDB::Read(CAddrMan &addr) { return DeserializeFileDB(chainParams, pathAddr, addr); } bool CAddrDB::Read(CAddrMan &addr, CDataStream &ssPeers) { bool ret = DeserializeDB(chainParams, ssPeers, addr, false); if (!ret) { // Ensure addrman is left in a clean state addr.Clear(); } return ret; } diff --git a/src/netaddress.h b/src/netaddress.h index 51880c372..3c3d32aef 100644 --- a/src/netaddress.h +++ b/src/netaddress.h @@ -1,201 +1,201 @@ // 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_NETADDRESS_H #define BITCOIN_NETADDRESS_H #if defined(HAVE_CONFIG_H) #include <config/bitcoin-config.h> #endif #include <compat.h> #include <serialize.h> #include <cstdint> #include <string> #include <vector> enum Network { NET_UNROUTABLE = 0, NET_IPV4, NET_IPV6, NET_ONION, NET_INTERNAL, NET_MAX, }; /** IP address (IPv6, or IPv4 using mapped IPv6 range (::FFFF:0:0/96)) */ class CNetAddr { protected: // in network byte order uint8_t ip[16]; // for scoped/link-local ipv6 addresses uint32_t scopeId; public: CNetAddr(); explicit CNetAddr(const struct in_addr &ipv4Addr); void SetIP(const CNetAddr &ip); private: /** * Set raw IPv4 or IPv6 address (in network byte order) * @note Only NET_IPV4 and NET_IPV6 are allowed for network. */ void SetRaw(Network network, const uint8_t *data); public: /** * Transform an arbitrary string into a non-routable ipv6 address. * Useful for mapping resolved addresses back to their source. */ bool SetInternal(const std::string &name); // for Tor addresses bool SetSpecial(const std::string &strName); // IPv4 mapped address (::FFFF:0:0/96, 0.0.0.0/0) bool IsIPv4() const; // IPv6 address (not mapped IPv4, not Tor) bool IsIPv6() const; // IPv4 private networks (10.0.0.0/8, 192.168.0.0/16, 172.16.0.0/12) bool IsRFC1918() const; // IPv4 inter-network communications (192.18.0.0/15) bool IsRFC2544() const; // IPv4 ISP-level NAT (100.64.0.0/10) bool IsRFC6598() const; // IPv4 documentation addresses (192.0.2.0/24, 198.51.100.0/24, // 203.0.113.0/24) bool IsRFC5737() const; // IPv6 documentation address (2001:0DB8::/32) bool IsRFC3849() const; // IPv4 autoconfig (169.254.0.0/16) bool IsRFC3927() const; // IPv6 6to4 tunnelling (2002::/16) bool IsRFC3964() const; // IPv6 unique local (FC00::/7) bool IsRFC4193() const; // IPv6 Teredo tunnelling (2001::/32) bool IsRFC4380() const; // IPv6 ORCHID (2001:10::/28) bool IsRFC4843() const; // IPv6 autoconfig (FE80::/64) bool IsRFC4862() const; // IPv6 well-known prefix (64:FF9B::/96) bool IsRFC6052() const; // IPv6 IPv4-translated address (::FFFF:0:0:0/96) bool IsRFC6145() const; bool IsTor() const; bool IsLocal() const; bool IsRoutable() const; bool IsInternal() const; bool IsValid() const; enum Network GetNetwork() const; std::string ToString() const; std::string ToStringIP() const; unsigned int GetByte(int n) const; uint64_t GetHash() const; bool GetInAddr(struct in_addr *pipv4Addr) const; std::vector<uint8_t> GetGroup() const; int GetReachabilityFrom(const CNetAddr *paddrPartner = nullptr) const; explicit CNetAddr(const struct in6_addr &pipv6Addr, const uint32_t scope = 0); bool GetIn6Addr(struct in6_addr *pipv6Addr) const; friend bool operator==(const CNetAddr &a, const CNetAddr &b); friend bool operator!=(const CNetAddr &a, const CNetAddr &b) { return !(a == b); } friend bool operator<(const CNetAddr &a, const CNetAddr &b); ADD_SERIALIZE_METHODS; template <typename Stream, typename Operation> inline void SerializationOp(Stream &s, Operation ser_action) { - READWRITE(FLATDATA(ip)); + READWRITE(ip); } friend class CSubNet; }; class CSubNet { protected: /// Network (base) address CNetAddr network; /// Netmask, in network byte order uint8_t netmask[16]; /// Is this value valid? (only used to signal parse errors) bool valid; public: CSubNet(); CSubNet(const CNetAddr &addr, int32_t mask); CSubNet(const CNetAddr &addr, const CNetAddr &mask); // constructor for single ip subnet (<ipv4>/32 or <ipv6>/128) explicit CSubNet(const CNetAddr &addr); bool Match(const CNetAddr &addr) const; std::string ToString() const; bool IsValid() const; friend bool operator==(const CSubNet &a, const CSubNet &b); friend bool operator!=(const CSubNet &a, const CSubNet &b) { return !(a == b); } friend bool operator<(const CSubNet &a, const CSubNet &b); ADD_SERIALIZE_METHODS; template <typename Stream, typename Operation> inline void SerializationOp(Stream &s, Operation ser_action) { READWRITE(network); - READWRITE(FLATDATA(netmask)); - READWRITE(FLATDATA(valid)); + READWRITE(netmask); + READWRITE(valid); } }; /** A combination of a network address (CNetAddr) and a (TCP) port */ class CService : public CNetAddr { protected: // host order unsigned short port; public: CService(); CService(const CNetAddr &ip, unsigned short port); CService(const struct in_addr &ipv4Addr, unsigned short port); explicit CService(const struct sockaddr_in &addr); unsigned short GetPort() const; bool GetSockAddr(struct sockaddr *paddr, socklen_t *addrlen) const; bool SetSockAddr(const struct sockaddr *paddr); friend bool operator==(const CService &a, const CService &b); friend bool operator!=(const CService &a, const CService &b) { return !(a == b); } friend bool operator<(const CService &a, const CService &b); std::vector<uint8_t> GetKey() const; std::string ToString() const; std::string ToStringPort() const; std::string ToStringIPPort() const; CService(const struct in6_addr &ipv6Addr, unsigned short port); explicit CService(const struct sockaddr_in6 &addr); ADD_SERIALIZE_METHODS; template <typename Stream, typename Operation> inline void SerializationOp(Stream &s, Operation ser_action) { - READWRITE(FLATDATA(ip)); + READWRITE(ip); unsigned short portN = htons(port); READWRITE(FLATDATA(portN)); if (ser_action.ForRead()) { port = ntohs(portN); } } }; #endif // BITCOIN_NETADDRESS_H diff --git a/src/protocol.h b/src/protocol.h index 3c70ce883..272573794 100644 --- a/src/protocol.h +++ b/src/protocol.h @@ -1,472 +1,472 @@ // 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 __cplusplus #error This header can only be compiled as C++. #endif #ifndef BITCOIN_PROTOCOL_H #define BITCOIN_PROTOCOL_H #include <netaddress.h> #include <serialize.h> #include <uint256.h> #include <version.h> #include <array> #include <cstdint> #include <string> class Config; /** * Maximum length of incoming protocol messages (Currently 2MB). * NB: Messages propagating block content are not subject to this limit. */ static const unsigned int MAX_PROTOCOL_MESSAGE_LENGTH = 2 * 1024 * 1024; /** * Message header. * (4) message start. * (12) command. * (4) size. * (4) checksum. */ class CMessageHeader { public: static constexpr size_t MESSAGE_START_SIZE = 4; static constexpr size_t COMMAND_SIZE = 12; static constexpr size_t MESSAGE_SIZE_SIZE = 4; static constexpr size_t CHECKSUM_SIZE = 4; static constexpr size_t MESSAGE_SIZE_OFFSET = MESSAGE_START_SIZE + COMMAND_SIZE; static constexpr size_t CHECKSUM_OFFSET = MESSAGE_SIZE_OFFSET + MESSAGE_SIZE_SIZE; static constexpr size_t HEADER_SIZE = MESSAGE_START_SIZE + COMMAND_SIZE + MESSAGE_SIZE_SIZE + CHECKSUM_SIZE; typedef std::array<uint8_t, MESSAGE_START_SIZE> MessageMagic; explicit CMessageHeader(const MessageMagic &pchMessageStartIn); CMessageHeader(const MessageMagic &pchMessageStartIn, const char *pszCommand, unsigned int nMessageSizeIn); std::string GetCommand() const; bool IsValid(const Config &config) const; bool IsValidWithoutConfig(const MessageMagic &magic) const; bool IsOversized(const Config &config) const; ADD_SERIALIZE_METHODS; template <typename Stream, typename Operation> inline void SerializationOp(Stream &s, Operation ser_action) { - READWRITE(FLATDATA(pchMessageStart)); - READWRITE(FLATDATA(pchCommand)); + READWRITE(pchMessageStart); + READWRITE(pchCommand); READWRITE(nMessageSize); - READWRITE(FLATDATA(pchChecksum)); + READWRITE(pchChecksum); } MessageMagic pchMessageStart; std::array<char, COMMAND_SIZE> pchCommand; uint32_t nMessageSize; uint8_t pchChecksum[CHECKSUM_SIZE]; }; /** * Bitcoin protocol message types. When adding new message types, don't forget * to update allNetMessageTypes in protocol.cpp. */ namespace NetMsgType { /** * The version message provides information about the transmitting node to the * receiving node at the beginning of a connection. * @see https://bitcoin.org/en/developer-reference#version */ extern const char *VERSION; /** * The verack message acknowledges a previously-received version message, * informing the connecting node that it can begin to send other messages. * @see https://bitcoin.org/en/developer-reference#verack */ extern const char *VERACK; /** * The addr (IP address) message relays connection information for peers on the * network. * @see https://bitcoin.org/en/developer-reference#addr */ extern const char *ADDR; /** * The inv message (inventory message) transmits one or more inventories of * objects known to the transmitting peer. * @see https://bitcoin.org/en/developer-reference#inv */ extern const char *INV; /** * The getdata message requests one or more data objects from another node. * @see https://bitcoin.org/en/developer-reference#getdata */ extern const char *GETDATA; /** * The merkleblock message is a reply to a getdata message which requested a * block using the inventory type MSG_MERKLEBLOCK. * @since protocol version 70001 as described by BIP37. * @see https://bitcoin.org/en/developer-reference#merkleblock */ extern const char *MERKLEBLOCK; /** * The getblocks message requests an inv message that provides block header * hashes starting from a particular point in the block chain. * @see https://bitcoin.org/en/developer-reference#getblocks */ extern const char *GETBLOCKS; /** * The getheaders message requests a headers message that provides block * headers starting from a particular point in the block chain. * @since protocol version 31800. * @see https://bitcoin.org/en/developer-reference#getheaders */ extern const char *GETHEADERS; /** * The tx message transmits a single transaction. * @see https://bitcoin.org/en/developer-reference#tx */ extern const char *TX; /** * The headers message sends one or more block headers to a node which * previously requested certain headers with a getheaders message. * @since protocol version 31800. * @see https://bitcoin.org/en/developer-reference#headers */ extern const char *HEADERS; /** * The block message transmits a single serialized block. * @see https://bitcoin.org/en/developer-reference#block */ extern const char *BLOCK; /** * The getaddr message requests an addr message from the receiving node, * preferably one with lots of IP addresses of other receiving nodes. * @see https://bitcoin.org/en/developer-reference#getaddr */ extern const char *GETADDR; /** * The mempool message requests the TXIDs of transactions that the receiving * node has verified as valid but which have not yet appeared in a block. * @since protocol version 60002. * @see https://bitcoin.org/en/developer-reference#mempool */ extern const char *MEMPOOL; /** * The ping message is sent periodically to help confirm that the receiving * peer is still connected. * @see https://bitcoin.org/en/developer-reference#ping */ extern const char *PING; /** * The pong message replies to a ping message, proving to the pinging node that * the ponging node is still alive. * @since protocol version 60001 as described by BIP31. * @see https://bitcoin.org/en/developer-reference#pong */ extern const char *PONG; /** * The notfound message is a reply to a getdata message which requested an * object the receiving node does not have available for relay. * @ince protocol version 70001. * @see https://bitcoin.org/en/developer-reference#notfound */ extern const char *NOTFOUND; /** * The filterload message tells the receiving peer to filter all relayed * transactions and requested merkle blocks through the provided filter. * @since protocol version 70001 as described by BIP37. * Only available with service bit NODE_BLOOM since protocol version * 70011 as described by BIP111. * @see https://bitcoin.org/en/developer-reference#filterload */ extern const char *FILTERLOAD; /** * The filteradd message tells the receiving peer to add a single element to a * previously-set bloom filter, such as a new public key. * @since protocol version 70001 as described by BIP37. * Only available with service bit NODE_BLOOM since protocol version * 70011 as described by BIP111. * @see https://bitcoin.org/en/developer-reference#filteradd */ extern const char *FILTERADD; /** * The filterclear message tells the receiving peer to remove a previously-set * bloom filter. * @since protocol version 70001 as described by BIP37. * Only available with service bit NODE_BLOOM since protocol version * 70011 as described by BIP111. * @see https://bitcoin.org/en/developer-reference#filterclear */ extern const char *FILTERCLEAR; /** * The reject message informs the receiving node that one of its previous * messages has been rejected. * @since protocol version 70002 as described by BIP61. * @see https://bitcoin.org/en/developer-reference#reject */ extern const char *REJECT; /** * Indicates that a node prefers to receive new block announcements via a * "headers" message rather than an "inv". * @since protocol version 70012 as described by BIP130. * @see https://bitcoin.org/en/developer-reference#sendheaders */ extern const char *SENDHEADERS; /** * The feefilter message tells the receiving peer not to inv us any txs * which do not meet the specified min fee rate. * @since protocol version 70013 as described by BIP133 */ extern const char *FEEFILTER; /** * Contains a 1-byte bool and 8-byte LE version number. * Indicates that a node is willing to provide blocks via "cmpctblock" messages. * May indicate that a node prefers to receive new block announcements via a * "cmpctblock" message rather than an "inv", depending on message contents. * @since protocol version 70014 as described by BIP 152 */ extern const char *SENDCMPCT; /** * Contains a CBlockHeaderAndShortTxIDs object - providing a header and * list of "short txids". * @since protocol version 70014 as described by BIP 152 */ extern const char *CMPCTBLOCK; /** * Contains a BlockTransactionsRequest * Peer should respond with "blocktxn" message. * @since protocol version 70014 as described by BIP 152 */ extern const char *GETBLOCKTXN; /** * Contains a BlockTransactions. * Sent in response to a "getblocktxn" message. * @since protocol version 70014 as described by BIP 152 */ extern const char *BLOCKTXN; /** * Contains an AvalanchePoll. * Peer should respond with "avaresponse" message. */ extern const char *AVAPOLL; /** * Contains an AvalancheResponse. * Sent in response to a "avapoll" message. */ extern const char *AVARESPONSE; /** * Indicate if the message is used to transmit the content of a block. * These messages can be significantly larger than usual messages and therefore * may need to be processed differently. */ bool IsBlockLike(const std::string &strCommand); }; // namespace NetMsgType /* Get a vector of all valid message types (see above) */ const std::vector<std::string> &getAllNetMessageTypes(); /** * nServices flags. */ enum ServiceFlags : uint64_t { // Nothing NODE_NONE = 0, // NODE_NETWORK means that the node is capable of serving the complete block // chain. It is currently set by all Bitcoin ABC non pruned nodes, and is // unset by SPV clients or other light clients. NODE_NETWORK = (1 << 0), // NODE_GETUTXO means the node is capable of responding to the getutxo // protocol request. Bitcoin ABC does not support this but a patch set // called Bitcoin XT does. See BIP 64 for details on how this is // implemented. NODE_GETUTXO = (1 << 1), // NODE_BLOOM means the node is capable and willing to handle bloom-filtered // connections. Bitcoin ABC nodes used to support this by default, without // advertising this bit, but no longer do as of protocol version 70011 (= // NO_BLOOM_VERSION) NODE_BLOOM = (1 << 2), // NODE_XTHIN means the node supports Xtreme Thinblocks. If this is turned // off then the node will not service nor make xthin requests. NODE_XTHIN = (1 << 4), // NODE_BITCOIN_CASH means the node supports Bitcoin Cash and the // associated consensus rule changes. // This service bit is intended to be used prior until some time after the // UAHF activation when the Bitcoin Cash network has adequately separated. // TODO: remove (free up) the NODE_BITCOIN_CASH service bit once no longer // needed. NODE_BITCOIN_CASH = (1 << 5), // NODE_NETWORK_LIMITED means the same as NODE_NETWORK with the limitation // of only serving the last 288 (2 day) blocks // See BIP159 for details on how this is implemented. NODE_NETWORK_LIMITED = (1 << 10), // The last non experimental service bit, helper for looping over the flags NODE_LAST_NON_EXPERIMENTAL_SERVICE_BIT = (1 << 23), // Bits 24-31 are reserved for temporary experiments. Just pick a bit that // isn't getting used, or one not being used much, and notify the // bitcoin-development mailing list. Remember that service bits are just // unauthenticated advertisements, so your code must be robust against // collisions and other cases where nodes may be advertising a service they // do not actually support. Other service bits should be allocated via the // BIP process. // NODE_AVALANCHE means the node supports Bitcoin Cash's avalanche // preconsensus mechanism. NODE_AVALANCHE = (1 << 24), }; /** * Gets the set of service flags which are "desirable" for a given peer. * * These are the flags which are required for a peer to support for them * to be "interesting" to us, ie for us to wish to use one of our few * outbound connection slots for or for us to wish to prioritize keeping * their connection around. * * Relevant service flags may be peer- and state-specific in that the * version of the peer may determine which flags are required (eg in the * case of NODE_NETWORK_LIMITED where we seek out NODE_NETWORK peers * unless they set NODE_NETWORK_LIMITED and we are out of IBD, in which * case NODE_NETWORK_LIMITED suffices). * * Thus, generally, avoid calling with peerServices == NODE_NONE, unless * state-specific flags must absolutely be avoided. When called with * peerServices == NODE_NONE, the returned desirable service flags are * guaranteed to not change dependent on state - ie they are suitable for * use when describing peers which we know to be desirable, but for which * we do not have a confirmed set of service flags. * * If the NODE_NONE return value is changed, contrib/seeds/makeseeds.py * should be updated appropriately to filter for the same nodes. */ ServiceFlags GetDesirableServiceFlags(ServiceFlags services); /** * Set the current IBD status in order to figure out the desirable service * flags */ void SetServiceFlagsIBDCache(bool status); /** * A shortcut for (services & GetDesirableServiceFlags(services)) * == GetDesirableServiceFlags(services), ie determines whether the given * set of service flags are sufficient for a peer to be "relevant". */ static inline bool HasAllDesirableServiceFlags(ServiceFlags services) { return !(GetDesirableServiceFlags(services) & (~services)); } /** * Checks if a peer with the given service flags may be capable of having a * robust address-storage DB. */ static inline bool MayHaveUsefulAddressDB(ServiceFlags services) { return (services & NODE_NETWORK) || (services & NODE_NETWORK_LIMITED); } /** * A CService with information about it as peer. */ class CAddress : public CService { public: CAddress(); explicit CAddress(CService ipIn, ServiceFlags nServicesIn); void Init(); ADD_SERIALIZE_METHODS; template <typename Stream, typename Operation> inline void SerializationOp(Stream &s, Operation ser_action) { if (ser_action.ForRead()) Init(); int nVersion = s.GetVersion(); if (s.GetType() & SER_DISK) READWRITE(nVersion); if ((s.GetType() & SER_DISK) || (nVersion >= CADDR_TIME_VERSION && !(s.GetType() & SER_GETHASH))) READWRITE(nTime); uint64_t nServicesInt = nServices; READWRITE(nServicesInt); nServices = static_cast<ServiceFlags>(nServicesInt); READWRITEAS(CService, *this); } // TODO: make private (improves encapsulation) public: ServiceFlags nServices; // disk and network only unsigned int nTime; }; /** getdata message type flags */ const uint32_t MSG_TYPE_MASK = 0xffffffff >> 3; /** getdata / inv message types. * These numbers are defined by the protocol. When adding a new value, be sure * to mention it in the respective BIP. */ enum GetDataMsg { UNDEFINED = 0, MSG_TX = 1, MSG_BLOCK = 2, // The following can only occur in getdata. Invs always use TX or BLOCK. //!< Defined in BIP37 MSG_FILTERED_BLOCK = 3, //!< Defined in BIP152 MSG_CMPCT_BLOCK = 4, }; /** * Inv(ventory) message data. * Intended as non-ambiguous identifier of objects (eg. transactions, blocks) * held by peers. */ class CInv { public: // TODO: make private (improves encapsulation) uint32_t type; uint256 hash; public: CInv() : type(0), hash() {} CInv(uint32_t typeIn, const uint256 &hashIn) : type(typeIn), hash(hashIn) {} ADD_SERIALIZE_METHODS; template <typename Stream, typename Operation> inline void SerializationOp(Stream &s, Operation ser_action) { READWRITE(type); READWRITE(hash); } friend bool operator<(const CInv &a, const CInv &b) { return a.type < b.type || (a.type == b.type && a.hash < b.hash); } std::string GetCommand() const; std::string ToString() const; uint32_t GetKind() const { return type & MSG_TYPE_MASK; } bool IsTx() const { auto k = GetKind(); return k == MSG_TX; } bool IsSomeBlock() const { auto k = GetKind(); return k == MSG_BLOCK || k == MSG_FILTERED_BLOCK || k == MSG_CMPCT_BLOCK; } }; #endif // BITCOIN_PROTOCOL_H diff --git a/src/serialize.h b/src/serialize.h index 52133f94d..a523ff1fe 100644 --- a/src/serialize.h +++ b/src/serialize.h @@ -1,957 +1,1022 @@ // 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_SERIALIZE_H #define BITCOIN_SERIALIZE_H #include <compat/endian.h> #include <prevector.h> #include <algorithm> +#include <array> #include <cassert> #include <cstdint> #include <cstring> #include <ios> #include <limits> #include <map> #include <memory> #include <set> #include <string> #include <utility> #include <vector> static const uint64_t MAX_SIZE = 0x02000000; /** * Dummy data type to identify deserializing constructors. * * By convention, a constructor of a type T with signature * * template <typename Stream> T::T(deserialize_type, Stream& s) * * is a deserializing constructor, which builds the type by deserializing it * from s. If T contains const fields, this is likely the only way to do so. */ struct deserialize_type {}; constexpr deserialize_type deserialize{}; /** * Used to bypass the rule against non-const reference to temporary where it * makes sense with wrappers such as CFlatData or CTxDB */ template <typename T> inline T &REF(const T &val) { return const_cast<T &>(val); } /** * Used to acquire a non-const pointer "this" to generate bodies of const * serialization operations from a template */ template <typename T> inline T *NCONST_PTR(const T *val) { return const_cast<T *>(val); } +//! Safely convert odd char pointer types to standard ones. +inline char *CharCast(char *c) { + return c; +} +inline char *CharCast(uint8_t *c) { + return (char *)c; +} +inline const char *CharCast(const char *c) { + return c; +} +inline const char *CharCast(const uint8_t *c) { + return (const char *)c; +} + /** * Lowest-level serialization and conversion. * @note Sizes of these types are verified in the tests */ template <typename Stream> inline void ser_writedata8(Stream &s, uint8_t obj) { s.write((char *)&obj, 1); } template <typename Stream> inline void ser_writedata16(Stream &s, uint16_t obj) { obj = htole16(obj); s.write((char *)&obj, 2); } template <typename Stream> inline void ser_writedata32(Stream &s, uint32_t obj) { obj = htole32(obj); s.write((char *)&obj, 4); } template <typename Stream> inline void ser_writedata32be(Stream &s, uint32_t obj) { obj = htobe32(obj); s.write((char *)&obj, 4); } template <typename Stream> inline void ser_writedata64(Stream &s, uint64_t obj) { obj = htole64(obj); s.write((char *)&obj, 8); } template <typename Stream> inline uint8_t ser_readdata8(Stream &s) { uint8_t obj; s.read((char *)&obj, 1); return obj; } template <typename Stream> inline uint16_t ser_readdata16(Stream &s) { uint16_t obj; s.read((char *)&obj, 2); return le16toh(obj); } template <typename Stream> inline uint32_t ser_readdata32(Stream &s) { uint32_t obj; s.read((char *)&obj, 4); return le32toh(obj); } template <typename Stream> inline uint32_t ser_readdata32be(Stream &s) { uint32_t obj; s.read((char *)&obj, 4); return be32toh(obj); } template <typename Stream> inline uint64_t ser_readdata64(Stream &s) { uint64_t obj; s.read((char *)&obj, 8); return le64toh(obj); } inline uint64_t ser_double_to_uint64(double x) { union { double x; uint64_t y; } tmp; tmp.x = x; return tmp.y; } inline uint32_t ser_float_to_uint32(float x) { union { float x; uint32_t y; } tmp; tmp.x = x; return tmp.y; } inline double ser_uint64_to_double(uint64_t y) { union { double x; uint64_t y; } tmp; tmp.y = y; return tmp.x; } inline float ser_uint32_to_float(uint32_t y) { union { float x; uint32_t y; } tmp; tmp.y = y; return tmp.x; } ///////////////////////////////////////////////////////////////// // // Templates for serializing to anything that looks like a stream, // i.e. anything that supports .read(char*, size_t) and .write(char*, size_t) // class CSizeComputer; enum { // primary actions SER_NETWORK = (1 << 0), SER_DISK = (1 << 1), SER_GETHASH = (1 << 2), }; //! Convert the reference base type to X, without changing constness or //! reference type. template <typename X> X &ReadWriteAsHelper(X &x) { return x; } template <typename X> const X &ReadWriteAsHelper(const X &x) { return x; } #define READWRITE(...) (::SerReadWriteMany(s, ser_action, __VA_ARGS__)) #define READWRITEAS(type, obj) \ (::SerReadWriteMany(s, ser_action, ReadWriteAsHelper<type>(obj))) /** * Implement three methods for serializable objects. These are actually wrappers * over "SerializationOp" template, which implements the body of each class' * serialization code. Adding "ADD_SERIALIZE_METHODS" in the body of the class * causes these wrappers to be added as members. */ #define ADD_SERIALIZE_METHODS \ template <typename Stream> void Serialize(Stream &s) const { \ NCONST_PTR(this)->SerializationOp(s, CSerActionSerialize()); \ } \ template <typename Stream> void Unserialize(Stream &s) { \ SerializationOp(s, CSerActionUnserialize()); \ } #ifndef CHAR_EQUALS_INT8 // TODO Get rid of bare char template <typename Stream> inline void Serialize(Stream &s, char a) { ser_writedata8(s, a); } #endif template <typename Stream> inline void Serialize(Stream &s, int8_t a) { ser_writedata8(s, a); } template <typename Stream> inline void Serialize(Stream &s, uint8_t a) { ser_writedata8(s, a); } template <typename Stream> inline void Serialize(Stream &s, int16_t a) { ser_writedata16(s, a); } template <typename Stream> inline void Serialize(Stream &s, uint16_t a) { ser_writedata16(s, a); } template <typename Stream> inline void Serialize(Stream &s, int32_t a) { ser_writedata32(s, a); } template <typename Stream> inline void Serialize(Stream &s, uint32_t a) { ser_writedata32(s, a); } template <typename Stream> inline void Serialize(Stream &s, int64_t a) { ser_writedata64(s, a); } template <typename Stream> inline void Serialize(Stream &s, uint64_t a) { ser_writedata64(s, a); } template <typename Stream> inline void Serialize(Stream &s, float a) { ser_writedata32(s, ser_float_to_uint32(a)); } template <typename Stream> inline void Serialize(Stream &s, double a) { ser_writedata64(s, ser_double_to_uint64(a)); } +template <typename Stream, size_t N> +inline void Serialize(Stream &s, const int8_t (&a)[N]) { + s.write(a, N); +} +template <typename Stream, size_t N> +inline void Serialize(Stream &s, const uint8_t (&a)[N]) { + s.write(CharCast(a), N); +} +template <typename Stream, size_t N> +inline void Serialize(Stream &s, const std::array<int8_t, N> &a) { + s.write(a.data(), N); +} +template <typename Stream, size_t N> +inline void Serialize(Stream &s, const std::array<uint8_t, N> &a) { + s.write(CharCast(a.data()), N); +} #ifndef CHAR_EQUALS_INT8 // TODO Get rid of bare char template <typename Stream> inline void Unserialize(Stream &s, char &a) { a = ser_readdata8(s); } +template <typename Stream, size_t N> +inline void Serialize(Stream &s, const char (&a)[N]) { + s.write(a, N); +} +template <typename Stream, size_t N> +inline void Serialize(Stream &s, const std::array<char, N> &a) { + s.write(a.data(), N); +} #endif template <typename Stream> inline void Unserialize(Stream &s, int8_t &a) { a = ser_readdata8(s); } template <typename Stream> inline void Unserialize(Stream &s, uint8_t &a) { a = ser_readdata8(s); } template <typename Stream> inline void Unserialize(Stream &s, int16_t &a) { a = ser_readdata16(s); } template <typename Stream> inline void Unserialize(Stream &s, uint16_t &a) { a = ser_readdata16(s); } template <typename Stream> inline void Unserialize(Stream &s, int32_t &a) { a = ser_readdata32(s); } template <typename Stream> inline void Unserialize(Stream &s, uint32_t &a) { a = ser_readdata32(s); } template <typename Stream> inline void Unserialize(Stream &s, int64_t &a) { a = ser_readdata64(s); } template <typename Stream> inline void Unserialize(Stream &s, uint64_t &a) { a = ser_readdata64(s); } template <typename Stream> inline void Unserialize(Stream &s, float &a) { a = ser_uint32_to_float(ser_readdata32(s)); } template <typename Stream> inline void Unserialize(Stream &s, double &a) { a = ser_uint64_to_double(ser_readdata64(s)); } +template <typename Stream, size_t N> +inline void Unserialize(Stream &s, int8_t (&a)[N]) { + s.read(a, N); +} +template <typename Stream, size_t N> +inline void Unserialize(Stream &s, uint8_t (&a)[N]) { + s.read(CharCast(a), N); +} +template <typename Stream, size_t N> +inline void Unserialize(Stream &s, std::array<int8_t, N> &a) { + s.read(a.data(), N); +} +template <typename Stream, size_t N> +inline void Unserialize(Stream &s, std::array<uint8_t, N> &a) { + s.read(CharCast(a.data()), N); +} +#ifndef CHAR_EQUALS_INT8 +template <typename Stream, size_t N> +inline void Unserialize(Stream &s, char (&a)[N]) { + s.read(CharCast(a), N); +} +template <typename Stream, size_t N> +inline void Unserialize(Stream &s, std::array<char, N> &a) { + s.read(CharCast(a.data()), N); +} +#endif template <typename Stream> inline void Serialize(Stream &s, bool a) { char f = a; ser_writedata8(s, f); } template <typename Stream> inline void Unserialize(Stream &s, bool &a) { char f = ser_readdata8(s); a = f; } /** * Compact Size * size < 253 -- 1 byte * size <= USHRT_MAX -- 3 bytes (253 + 2 bytes) * size <= UINT_MAX -- 5 bytes (254 + 4 bytes) * size > UINT_MAX -- 9 bytes (255 + 8 bytes) */ inline uint32_t GetSizeOfCompactSize(uint64_t nSize) { if (nSize < 253) { return sizeof(uint8_t); } if (nSize <= std::numeric_limits<uint16_t>::max()) { return sizeof(uint8_t) + sizeof(uint16_t); } if (nSize <= std::numeric_limits<uint32_t>::max()) { return sizeof(uint8_t) + sizeof(uint32_t); } return sizeof(uint8_t) + sizeof(uint64_t); } inline void WriteCompactSize(CSizeComputer &os, uint64_t nSize); template <typename Stream> void WriteCompactSize(Stream &os, uint64_t nSize) { if (nSize < 253) { ser_writedata8(os, nSize); } else if (nSize <= std::numeric_limits<uint16_t>::max()) { ser_writedata8(os, 253); ser_writedata16(os, nSize); } else if (nSize <= std::numeric_limits<uint32_t>::max()) { ser_writedata8(os, 254); ser_writedata32(os, nSize); } else { ser_writedata8(os, 255); ser_writedata64(os, nSize); } return; } template <typename Stream> uint64_t ReadCompactSize(Stream &is) { uint8_t chSize = ser_readdata8(is); uint64_t nSizeRet = 0; if (chSize < 253) { nSizeRet = chSize; } else if (chSize == 253) { nSizeRet = ser_readdata16(is); if (nSizeRet < 253) { throw std::ios_base::failure("non-canonical ReadCompactSize()"); } } else if (chSize == 254) { nSizeRet = ser_readdata32(is); if (nSizeRet < 0x10000u) { throw std::ios_base::failure("non-canonical ReadCompactSize()"); } } else { nSizeRet = ser_readdata64(is); if (nSizeRet < 0x100000000ULL) { throw std::ios_base::failure("non-canonical ReadCompactSize()"); } } if (nSizeRet > MAX_SIZE) { throw std::ios_base::failure("ReadCompactSize(): size too large"); } return nSizeRet; } /** * Variable-length integers: bytes are a MSB base-128 encoding of the number. * The high bit in each byte signifies whether another digit follows. To make * sure the encoding is one-to-one, one is subtracted from all but the last * digit. Thus, the byte sequence a[] with length len, where all but the last * byte has bit 128 set, encodes the number: * * (a[len-1] & 0x7F) + sum(i=1..len-1, 128^i*((a[len-i-1] & 0x7F)+1)) * * Properties: * * Very small (0-127: 1 byte, 128-16511: 2 bytes, 16512-2113663: 3 bytes) * * Every integer has exactly one encoding * * Encoding does not depend on size of original integer type * * No redundancy: every (infinite) byte sequence corresponds to a list * of encoded integers. * * 0: [0x00] 256: [0x81 0x00] * 1: [0x01] 16383: [0xFE 0x7F] * 127: [0x7F] 16384: [0xFF 0x00] * 128: [0x80 0x00] 16511: [0xFF 0x7F] * 255: [0x80 0x7F] 65535: [0x82 0xFE 0x7F] * 2^32: [0x8E 0xFE 0xFE 0xFF 0x00] */ /** * Mode for encoding VarInts. * * Currently there is no support for signed encodings. The default mode will not * compile with signed values, and the legacy "nonnegative signed" mode will * accept signed values, but improperly encode and decode them if they are * negative. In the future, the DEFAULT mode could be extended to support * negative numbers in a backwards compatible way, and additional modes could be * added to support different varint formats (e.g. zigzag encoding). */ enum class VarIntMode { DEFAULT, NONNEGATIVE_SIGNED }; template <VarIntMode Mode, typename I> struct CheckVarIntMode { constexpr CheckVarIntMode() { static_assert(Mode != VarIntMode::DEFAULT || std::is_unsigned<I>::value, "Unsigned type required with mode DEFAULT."); static_assert(Mode != VarIntMode::NONNEGATIVE_SIGNED || std::is_signed<I>::value, "Signed type required with mode NONNEGATIVE_SIGNED."); } }; template <VarIntMode Mode, typename I> inline unsigned int GetSizeOfVarInt(I n) { CheckVarIntMode<Mode, I>(); int nRet = 0; while (true) { nRet++; if (n <= 0x7F) { return nRet; } n = (n >> 7) - 1; } } template <typename I> inline void WriteVarInt(CSizeComputer &os, I n); template <typename Stream, VarIntMode Mode, typename I> void WriteVarInt(Stream &os, I n) { CheckVarIntMode<Mode, I>(); uint8_t tmp[(sizeof(n) * 8 + 6) / 7]; int len = 0; while (true) { tmp[len] = (n & 0x7F) | (len ? 0x80 : 0x00); if (n <= 0x7F) { break; } n = (n >> 7) - 1; len++; } do { ser_writedata8(os, tmp[len]); } while (len--); } template <typename Stream, VarIntMode Mode, typename I> I ReadVarInt(Stream &is) { CheckVarIntMode<Mode, I>(); I n = 0; while (true) { uint8_t chData = ser_readdata8(is); if (n > (std::numeric_limits<I>::max() >> 7)) { throw std::ios_base::failure("ReadVarInt(): size too large"); } n = (n << 7) | (chData & 0x7F); if ((chData & 0x80) == 0) { return n; } if (n == std::numeric_limits<I>::max()) { throw std::ios_base::failure("ReadVarInt(): size too large"); } n++; } } #define FLATDATA(obj) CFlatData((char *)&(obj), (char *)&(obj) + sizeof(obj)) #define VARINT(obj, ...) WrapVarInt<__VA_ARGS__>(REF(obj)) #define COMPACTSIZE(obj) CCompactSize(REF(obj)) #define LIMITED_STRING(obj, n) LimitedString<n>(REF(obj)) /** * Wrapper for serializing arrays and POD. */ class CFlatData { protected: char *pbegin; char *pend; public: CFlatData(void *pbeginIn, void *pendIn) : pbegin((char *)pbeginIn), pend((char *)pendIn) {} template <class T, class TAl> explicit CFlatData(std::vector<T, TAl> &v) { pbegin = (char *)v.data(); pend = (char *)(v.data() + v.size()); } template <unsigned int N, typename T, typename S, typename D> explicit CFlatData(prevector<N, T, S, D> &v) { pbegin = (char *)v.data(); pend = (char *)(v.data() + v.size()); } char *begin() { return pbegin; } const char *begin() const { return pbegin; } char *end() { return pend; } const char *end() const { return pend; } template <typename Stream> void Serialize(Stream &s) const { s.write(pbegin, pend - pbegin); } template <typename Stream> void Unserialize(Stream &s) { s.read(pbegin, pend - pbegin); } }; template <VarIntMode Mode, typename I> class CVarInt { protected: I &n; public: explicit CVarInt(I &nIn) : n(nIn) {} template <typename Stream> void Serialize(Stream &s) const { WriteVarInt<Stream, Mode, I>(s, n); } template <typename Stream> void Unserialize(Stream &s) { n = ReadVarInt<Stream, Mode, I>(s); } }; class CCompactSize { protected: uint64_t &n; public: explicit CCompactSize(uint64_t &nIn) : n(nIn) {} template <typename Stream> void Serialize(Stream &s) const { WriteCompactSize<Stream>(s, n); } template <typename Stream> void Unserialize(Stream &s) { n = ReadCompactSize<Stream>(s); } }; template <size_t Limit> class LimitedString { protected: std::string &string; public: explicit LimitedString(std::string &_string) : string(_string) {} template <typename Stream> void Unserialize(Stream &s) { size_t size = ReadCompactSize(s); if (size > Limit) { throw std::ios_base::failure("String length limit exceeded"); } string.resize(size); if (size != 0) { s.read((char *)string.data(), size); } } template <typename Stream> void Serialize(Stream &s) const { WriteCompactSize(s, string.size()); if (!string.empty()) { s.write((char *)string.data(), string.size()); } } }; template <VarIntMode Mode = VarIntMode::DEFAULT, typename I> CVarInt<Mode, I> WrapVarInt(I &n) { return CVarInt<Mode, I>{n}; } /** * Forward declarations */ /** * string */ template <typename Stream, typename C> void Serialize(Stream &os, const std::basic_string<C> &str); template <typename Stream, typename C> void Unserialize(Stream &is, std::basic_string<C> &str); /** * prevector * prevectors of uint8_t are a special case and are intended to be serialized as * a single opaque blob. */ template <typename Stream, unsigned int N, typename T> void Serialize_impl(Stream &os, const prevector<N, T> &v, const uint8_t &); template <typename Stream, unsigned int N, typename T, typename V> void Serialize_impl(Stream &os, const prevector<N, T> &v, const V &); template <typename Stream, unsigned int N, typename T> inline void Serialize(Stream &os, const prevector<N, T> &v); template <typename Stream, unsigned int N, typename T> void Unserialize_impl(Stream &is, prevector<N, T> &v, const uint8_t &); template <typename Stream, unsigned int N, typename T, typename V> void Unserialize_impl(Stream &is, prevector<N, T> &v, const V &); template <typename Stream, unsigned int N, typename T> inline void Unserialize(Stream &is, prevector<N, T> &v); /** * vector * vectors of uint8_t are a special case and are intended to be serialized as a * single opaque blob. */ template <typename Stream, typename T, typename A> void Serialize_impl(Stream &os, const std::vector<T, A> &v, const uint8_t &); template <typename Stream, typename T, typename A, typename V> void Serialize_impl(Stream &os, const std::vector<T, A> &v, const V &); template <typename Stream, typename T, typename A> inline void Serialize(Stream &os, const std::vector<T, A> &v); template <typename Stream, typename T, typename A> void Unserialize_impl(Stream &is, std::vector<T, A> &v, const uint8_t &); template <typename Stream, typename T, typename A, typename V> void Unserialize_impl(Stream &is, std::vector<T, A> &v, const V &); template <typename Stream, typename T, typename A> inline void Unserialize(Stream &is, std::vector<T, A> &v); /** * pair */ template <typename Stream, typename K, typename T> void Serialize(Stream &os, const std::pair<K, T> &item); template <typename Stream, typename K, typename T> void Unserialize(Stream &is, std::pair<K, T> &item); /** * map */ template <typename Stream, typename K, typename T, typename Pred, typename A> void Serialize(Stream &os, const std::map<K, T, Pred, A> &m); template <typename Stream, typename K, typename T, typename Pred, typename A> void Unserialize(Stream &is, std::map<K, T, Pred, A> &m); /** * set */ template <typename Stream, typename K, typename Pred, typename A> void Serialize(Stream &os, const std::set<K, Pred, A> &m); template <typename Stream, typename K, typename Pred, typename A> void Unserialize(Stream &is, std::set<K, Pred, A> &m); /** * shared_ptr */ template <typename Stream, typename T> void Serialize(Stream &os, const std::shared_ptr<const T> &p); template <typename Stream, typename T> void Unserialize(Stream &os, std::shared_ptr<const T> &p); /** * unique_ptr */ template <typename Stream, typename T> void Serialize(Stream &os, const std::unique_ptr<const T> &p); template <typename Stream, typename T> void Unserialize(Stream &os, std::unique_ptr<const T> &p); /** * If none of the specialized versions above matched, default to calling member * function. */ template <typename Stream, typename T> inline void Serialize(Stream &os, const T &a) { a.Serialize(os); } template <typename Stream, typename T> inline void Unserialize(Stream &is, T &&a) { a.Unserialize(is); } /** * string */ template <typename Stream, typename C> void Serialize(Stream &os, const std::basic_string<C> &str) { WriteCompactSize(os, str.size()); if (!str.empty()) { os.write((char *)str.data(), str.size() * sizeof(C)); } } template <typename Stream, typename C> void Unserialize(Stream &is, std::basic_string<C> &str) { size_t nSize = ReadCompactSize(is); str.resize(nSize); if (nSize != 0) { is.read((char *)str.data(), nSize * sizeof(C)); } } /** * prevector */ template <typename Stream, unsigned int N, typename T> void Serialize_impl(Stream &os, const prevector<N, T> &v, const uint8_t &) { WriteCompactSize(os, v.size()); if (!v.empty()) { os.write((char *)v.data(), v.size() * sizeof(T)); } } template <typename Stream, unsigned int N, typename T, typename V> void Serialize_impl(Stream &os, const prevector<N, T> &v, const V &) { WriteCompactSize(os, v.size()); for (const T &i : v) { ::Serialize(os, i); } } template <typename Stream, unsigned int N, typename T> inline void Serialize(Stream &os, const prevector<N, T> &v) { Serialize_impl(os, v, T()); } template <typename Stream, unsigned int N, typename T> void Unserialize_impl(Stream &is, prevector<N, T> &v, const uint8_t &) { // Limit size per read so bogus size value won't cause out of memory v.clear(); size_t nSize = ReadCompactSize(is); size_t i = 0; while (i < nSize) { size_t blk = std::min(nSize - i, size_t(1 + 4999999 / sizeof(T))); v.resize(i + blk); is.read((char *)&v[i], blk * sizeof(T)); i += blk; } } template <typename Stream, unsigned int N, typename T, typename V> void Unserialize_impl(Stream &is, prevector<N, T> &v, const V &) { v.clear(); size_t nSize = ReadCompactSize(is); size_t i = 0; size_t nMid = 0; while (nMid < nSize) { nMid += 5000000 / sizeof(T); if (nMid > nSize) { nMid = nSize; } v.resize(nMid); for (; i < nMid; i++) { Unserialize(is, v[i]); } } } template <typename Stream, unsigned int N, typename T> inline void Unserialize(Stream &is, prevector<N, T> &v) { Unserialize_impl(is, v, T()); } /** * vector */ template <typename Stream, typename T, typename A> void Serialize_impl(Stream &os, const std::vector<T, A> &v, const uint8_t &) { WriteCompactSize(os, v.size()); if (!v.empty()) { os.write((char *)v.data(), v.size() * sizeof(T)); } } template <typename Stream, typename T, typename A, typename V> void Serialize_impl(Stream &os, const std::vector<T, A> &v, const V &) { WriteCompactSize(os, v.size()); for (const T &i : v) { ::Serialize(os, i); } } template <typename Stream, typename T, typename A> inline void Serialize(Stream &os, const std::vector<T, A> &v) { Serialize_impl(os, v, T()); } template <typename Stream, typename T, typename A> void Unserialize_impl(Stream &is, std::vector<T, A> &v, const uint8_t &) { // Limit size per read so bogus size value won't cause out of memory v.clear(); size_t nSize = ReadCompactSize(is); size_t i = 0; while (i < nSize) { size_t blk = std::min(nSize - i, size_t(1 + 4999999 / sizeof(T))); v.resize(i + blk); is.read((char *)&v[i], blk * sizeof(T)); i += blk; } } template <typename Stream, typename T, typename A, typename V> void Unserialize_impl(Stream &is, std::vector<T, A> &v, const V &) { v.clear(); size_t nSize = ReadCompactSize(is); size_t i = 0; size_t nMid = 0; while (nMid < nSize) { nMid += 5000000 / sizeof(T); if (nMid > nSize) { nMid = nSize; } v.resize(nMid); for (; i < nMid; i++) { Unserialize(is, v[i]); } } } template <typename Stream, typename T, typename A> inline void Unserialize(Stream &is, std::vector<T, A> &v) { Unserialize_impl(is, v, T()); } /** * pair */ template <typename Stream, typename K, typename T> void Serialize(Stream &os, const std::pair<K, T> &item) { Serialize(os, item.first); Serialize(os, item.second); } template <typename Stream, typename K, typename T> void Unserialize(Stream &is, std::pair<K, T> &item) { Unserialize(is, item.first); Unserialize(is, item.second); } /** * map */ template <typename Stream, typename K, typename T, typename Pred, typename A> void Serialize(Stream &os, const std::map<K, T, Pred, A> &m) { WriteCompactSize(os, m.size()); for (const auto &entry : m) { Serialize(os, entry); } } template <typename Stream, typename K, typename T, typename Pred, typename A> void Unserialize(Stream &is, std::map<K, T, Pred, A> &m) { m.clear(); size_t nSize = ReadCompactSize(is); typename std::map<K, T, Pred, A>::iterator mi = m.begin(); for (size_t i = 0; i < nSize; i++) { std::pair<K, T> item; Unserialize(is, item); mi = m.insert(mi, item); } } /** * set */ template <typename Stream, typename K, typename Pred, typename A> void Serialize(Stream &os, const std::set<K, Pred, A> &m) { WriteCompactSize(os, m.size()); for (const K &i : m) { Serialize(os, i); } } template <typename Stream, typename K, typename Pred, typename A> void Unserialize(Stream &is, std::set<K, Pred, A> &m) { m.clear(); size_t nSize = ReadCompactSize(is); typename std::set<K, Pred, A>::iterator it = m.begin(); for (size_t i = 0; i < nSize; i++) { K key; Unserialize(is, key); it = m.insert(it, key); } } /** * unique_ptr */ template <typename Stream, typename T> void Serialize(Stream &os, const std::unique_ptr<const T> &p) { Serialize(os, *p); } template <typename Stream, typename T> void Unserialize(Stream &is, std::unique_ptr<const T> &p) { p.reset(new T(deserialize, is)); } /** * shared_ptr */ template <typename Stream, typename T> void Serialize(Stream &os, const std::shared_ptr<const T> &p) { Serialize(os, *p); } template <typename Stream, typename T> void Unserialize(Stream &is, std::shared_ptr<const T> &p) { p = std::make_shared<const T>(deserialize, is); } /** * Support for ADD_SERIALIZE_METHODS and READWRITE macro */ struct CSerActionSerialize { constexpr bool ForRead() const { return false; } }; struct CSerActionUnserialize { constexpr bool ForRead() const { return true; } }; /** * ::GetSerializeSize implementations * * Computing the serialized size of objects is done through a special stream * object of type CSizeComputer, which only records the number of bytes written * to it. * * If your Serialize or SerializationOp method has non-trivial overhead for * serialization, it may be worthwhile to implement a specialized version for * CSizeComputer, which uses the s.seek() method to record bytes that would * be written instead. */ class CSizeComputer { protected: size_t nSize; const int nType; const int nVersion; public: CSizeComputer(int nTypeIn, int nVersionIn) : nSize(0), nType(nTypeIn), nVersion(nVersionIn) {} void write(const char *psz, size_t _nSize) { this->nSize += _nSize; } /** Pretend _nSize bytes are written, without specifying them. */ void seek(size_t _nSize) { this->nSize += _nSize; } template <typename T> CSizeComputer &operator<<(const T &obj) { ::Serialize(*this, obj); return (*this); } size_t size() const { return nSize; } int GetVersion() const { return nVersion; } int GetType() const { return nType; } }; template <typename Stream> void SerializeMany(Stream &s) {} template <typename Stream, typename Arg, typename... Args> void SerializeMany(Stream &s, const Arg &arg, const Args &... args) { ::Serialize(s, arg); ::SerializeMany(s, args...); } template <typename Stream> inline void UnserializeMany(Stream &s) {} template <typename Stream, typename Arg, typename... Args> inline void UnserializeMany(Stream &s, Arg &&arg, Args &&... args) { ::Unserialize(s, arg); ::UnserializeMany(s, args...); } template <typename Stream, typename... Args> inline void SerReadWriteMany(Stream &s, CSerActionSerialize ser_action, const Args &... args) { ::SerializeMany(s, args...); } template <typename Stream, typename... Args> inline void SerReadWriteMany(Stream &s, CSerActionUnserialize ser_action, Args &&... args) { ::UnserializeMany(s, args...); } template <typename I> inline void WriteVarInt(CSizeComputer &s, I n) { s.seek(GetSizeOfVarInt<I>(n)); } inline void WriteCompactSize(CSizeComputer &s, uint64_t nSize) { s.seek(GetSizeOfCompactSize(nSize)); } template <typename T> size_t GetSerializeSize(const T &t, int nType, int nVersion) { return (CSizeComputer(nType, nVersion) << t).size(); } template <typename S, typename T> size_t GetSerializeSize(const S &s, const T &t) { return (CSizeComputer(s.GetType(), s.GetVersion()) << t).size(); } #endif // BITCOIN_SERIALIZE_H diff --git a/src/test/net_tests.cpp b/src/test/net_tests.cpp index e1d722c9c..018c68b0a 100644 --- a/src/test/net_tests.cpp +++ b/src/test/net_tests.cpp @@ -1,226 +1,226 @@ // Copyright (c) 2012-2016 The Bitcoin Core developers // Copyright (c) 2017-2018 The Bitcoin developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include <net.h> #include <addrman.h> #include <chainparams.h> #include <clientversion.h> #include <config.h> #include <hash.h> #include <netbase.h> #include <serialize.h> #include <streams.h> #include <test/test_bitcoin.h> #include <boost/test/unit_test.hpp> #include <string> class CAddrManSerializationMock : public CAddrMan { public: virtual void Serialize(CDataStream &s) const = 0; //! Ensure that bucket placement is always the same for testing purposes. void MakeDeterministic() { nKey.SetNull(); insecure_rand = FastRandomContext(true); } }; class CAddrManUncorrupted : public CAddrManSerializationMock { public: void Serialize(CDataStream &s) const override { CAddrMan::Serialize(s); } }; class CAddrManCorrupted : public CAddrManSerializationMock { public: void Serialize(CDataStream &s) const override { // Produces corrupt output that claims addrman has 20 addrs when it only // has one addr. uint8_t nVersion = 1; s << nVersion; s << uint8_t(32); s << nKey; s << 10; // nNew s << 10; // nTried int nUBuckets = ADDRMAN_NEW_BUCKET_COUNT ^ (1 << 30); s << nUBuckets; CService serv; Lookup("252.1.1.1", serv, 7777, false); CAddress addr = CAddress(serv, NODE_NONE); CNetAddr resolved; LookupHost("252.2.2.2", resolved, false); CAddrInfo info = CAddrInfo(addr, resolved); s << info; } }; class NetTestConfig : public DummyConfig { public: bool SetMaxBlockSize(uint64_t maxBlockSize) override { nMaxBlockSize = maxBlockSize; return true; } uint64_t GetMaxBlockSize() const override { return nMaxBlockSize; } private: uint64_t nMaxBlockSize; }; static CDataStream AddrmanToStream(CAddrManSerializationMock &_addrman) { CDataStream ssPeersIn(SER_DISK, CLIENT_VERSION); - ssPeersIn << FLATDATA(Params().DiskMagic()); + ssPeersIn << Params().DiskMagic(); ssPeersIn << _addrman; std::string str = ssPeersIn.str(); std::vector<uint8_t> vchData(str.begin(), str.end()); return CDataStream(vchData, SER_DISK, CLIENT_VERSION); } BOOST_FIXTURE_TEST_SUITE(net_tests, BasicTestingSetup) BOOST_AUTO_TEST_CASE(cnode_listen_port) { // test default unsigned short port = GetListenPort(); BOOST_CHECK(port == Params().GetDefaultPort()); // test set port unsigned short altPort = 12345; gArgs.SoftSetArg("-port", std::to_string(altPort)); port = GetListenPort(); BOOST_CHECK(port == altPort); } BOOST_AUTO_TEST_CASE(caddrdb_read) { CAddrManUncorrupted addrmanUncorrupted; addrmanUncorrupted.MakeDeterministic(); CService addr1, addr2, addr3; Lookup("250.7.1.1", addr1, 8333, false); Lookup("250.7.2.2", addr2, 9999, false); Lookup("250.7.3.3", addr3, 9999, false); // Add three addresses to new table. CService source; Lookup("252.5.1.1", source, 8333, false); addrmanUncorrupted.Add(CAddress(addr1, NODE_NONE), source); addrmanUncorrupted.Add(CAddress(addr2, NODE_NONE), source); addrmanUncorrupted.Add(CAddress(addr3, NODE_NONE), source); // Test that the de-serialization does not throw an exception. CDataStream ssPeers1 = AddrmanToStream(addrmanUncorrupted); bool exceptionThrown = false; CAddrMan addrman1; BOOST_CHECK(addrman1.size() == 0); try { uint8_t pchMsgTmp[4]; - ssPeers1 >> FLATDATA(pchMsgTmp); + ssPeers1 >> pchMsgTmp; ssPeers1 >> addrman1; } catch (const std::exception &e) { exceptionThrown = true; } BOOST_CHECK(addrman1.size() == 3); BOOST_CHECK(exceptionThrown == false); // Test that CAddrDB::Read creates an addrman with the correct number of // addrs. CDataStream ssPeers2 = AddrmanToStream(addrmanUncorrupted); CAddrMan addrman2; CAddrDB adb(Params()); BOOST_CHECK(addrman2.size() == 0); adb.Read(addrman2, ssPeers2); BOOST_CHECK(addrman2.size() == 3); } BOOST_AUTO_TEST_CASE(caddrdb_read_corrupted) { CAddrManCorrupted addrmanCorrupted; addrmanCorrupted.MakeDeterministic(); // Test that the de-serialization of corrupted addrman throws an exception. CDataStream ssPeers1 = AddrmanToStream(addrmanCorrupted); bool exceptionThrown = false; CAddrMan addrman1; BOOST_CHECK(addrman1.size() == 0); try { uint8_t pchMsgTmp[4]; - ssPeers1 >> FLATDATA(pchMsgTmp); + ssPeers1 >> pchMsgTmp; ssPeers1 >> addrman1; } catch (const std::exception &e) { exceptionThrown = true; } // Even through de-serialization failed addrman is not left in a clean // state. BOOST_CHECK(addrman1.size() == 1); BOOST_CHECK(exceptionThrown); // Test that CAddrDB::Read leaves addrman in a clean state if // de-serialization fails. CDataStream ssPeers2 = AddrmanToStream(addrmanCorrupted); CAddrMan addrman2; CAddrDB adb(Params()); BOOST_CHECK(addrman2.size() == 0); adb.Read(addrman2, ssPeers2); BOOST_CHECK(addrman2.size() == 0); } BOOST_AUTO_TEST_CASE(cnode_simple_test) { SOCKET hSocket = INVALID_SOCKET; NodeId id = 0; int height = 0; in_addr ipv4Addr; ipv4Addr.s_addr = 0xa0b0c001; CAddress addr = CAddress(CService(ipv4Addr, 7777), NODE_NETWORK); std::string pszDest; bool fInboundIn = false; // Test that fFeeler is false by default. auto pnode1 = std::make_unique<CNode>(id++, NODE_NETWORK, height, hSocket, addr, 0, 0, CAddress(), pszDest, fInboundIn); BOOST_CHECK(pnode1->fInbound == false); BOOST_CHECK(pnode1->fFeeler == false); fInboundIn = true; auto pnode2 = std::make_unique<CNode>(id++, NODE_NETWORK, height, hSocket, addr, 1, 1, CAddress(), pszDest, fInboundIn); BOOST_CHECK(pnode2->fInbound == true); BOOST_CHECK(pnode2->fFeeler == false); } BOOST_AUTO_TEST_CASE(test_getSubVersionEB) { BOOST_CHECK_EQUAL(getSubVersionEB(13800000000), "13800.0"); BOOST_CHECK_EQUAL(getSubVersionEB(3800000000), "3800.0"); BOOST_CHECK_EQUAL(getSubVersionEB(14000000), "14.0"); BOOST_CHECK_EQUAL(getSubVersionEB(1540000), "1.5"); BOOST_CHECK_EQUAL(getSubVersionEB(1560000), "1.5"); BOOST_CHECK_EQUAL(getSubVersionEB(210000), "0.2"); BOOST_CHECK_EQUAL(getSubVersionEB(10000), "0.0"); BOOST_CHECK_EQUAL(getSubVersionEB(0), "0.0"); } BOOST_AUTO_TEST_CASE(test_userAgent) { NetTestConfig config; config.SetMaxBlockSize(8000000); const std::string uacomment = "A very nice comment"; gArgs.ForceSetMultiArg("-uacomment", uacomment); const std::string versionMessage = "/Bitcoin ABC:" + std::to_string(CLIENT_VERSION_MAJOR) + "." + std::to_string(CLIENT_VERSION_MINOR) + "." + std::to_string(CLIENT_VERSION_REVISION) + "(EB8.0; " + uacomment + ")/"; BOOST_CHECK_EQUAL(userAgent(config), versionMessage); } BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/serialize_tests.cpp b/src/test/serialize_tests.cpp index b231d12d7..1e7e921d1 100644 --- a/src/test/serialize_tests.cpp +++ b/src/test/serialize_tests.cpp @@ -1,438 +1,439 @@ // Copyright (c) 2012-2016 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include <serialize.h> #include <hash.h> #include <streams.h> #include <util/strencodings.h> #include <test/test_bitcoin.h> #include <boost/test/unit_test.hpp> #include <cstdint> #include <limits> BOOST_FIXTURE_TEST_SUITE(serialize_tests, BasicTestingSetup) class CSerializeMethodsTestSingle { protected: int intval; bool boolval; std::string stringval; - const char *charstrval; + char charstrval[16]; CTransactionRef txval; public: CSerializeMethodsTestSingle() = default; CSerializeMethodsTestSingle(int intvalin, bool boolvalin, std::string stringvalin, const char *charstrvalin, const CTransactionRef &txvalin) : intval(intvalin), boolval(boolvalin), - stringval(std::move(stringvalin)), charstrval(charstrvalin), - txval(txvalin) {} + stringval(std::move(stringvalin)), txval(txvalin) { + memcpy(charstrval, charstrvalin, sizeof(charstrval)); + } ADD_SERIALIZE_METHODS; template <typename Stream, typename Operation> inline void SerializationOp(Stream &s, Operation ser_action) { READWRITE(intval); READWRITE(boolval); READWRITE(stringval); - READWRITE(FLATDATA(charstrval)); + READWRITE(charstrval); READWRITE(txval); } bool operator==(const CSerializeMethodsTestSingle &rhs) { return intval == rhs.intval && boolval == rhs.boolval && stringval == rhs.stringval && strcmp(charstrval, rhs.charstrval) == 0 && *txval == *rhs.txval; } }; class CSerializeMethodsTestMany : public CSerializeMethodsTestSingle { public: using CSerializeMethodsTestSingle::CSerializeMethodsTestSingle; ADD_SERIALIZE_METHODS; template <typename Stream, typename Operation> inline void SerializationOp(Stream &s, Operation ser_action) { - READWRITE(intval, boolval, stringval, FLATDATA(charstrval), txval); + READWRITE(intval, boolval, stringval, charstrval, txval); } }; BOOST_AUTO_TEST_CASE(sizes) { BOOST_CHECK_EQUAL(sizeof(char), GetSerializeSize(char(0), 0, 0)); BOOST_CHECK_EQUAL(sizeof(int8_t), GetSerializeSize(int8_t(0), 0, 0)); BOOST_CHECK_EQUAL(sizeof(uint8_t), GetSerializeSize(uint8_t(0), 0, 0)); BOOST_CHECK_EQUAL(sizeof(int16_t), GetSerializeSize(int16_t(0), 0, 0)); BOOST_CHECK_EQUAL(sizeof(uint16_t), GetSerializeSize(uint16_t(0), 0, 0)); BOOST_CHECK_EQUAL(sizeof(int32_t), GetSerializeSize(int32_t(0), 0, 0)); BOOST_CHECK_EQUAL(sizeof(uint32_t), GetSerializeSize(uint32_t(0), 0, 0)); BOOST_CHECK_EQUAL(sizeof(int64_t), GetSerializeSize(int64_t(0), 0, 0)); BOOST_CHECK_EQUAL(sizeof(uint64_t), GetSerializeSize(uint64_t(0), 0, 0)); BOOST_CHECK_EQUAL(sizeof(float), GetSerializeSize(float(0), 0, 0)); BOOST_CHECK_EQUAL(sizeof(double), GetSerializeSize(double(0), 0, 0)); // Bool is serialized as char BOOST_CHECK_EQUAL(sizeof(char), GetSerializeSize(bool(0), 0, 0)); // Sanity-check GetSerializeSize and c++ type matching BOOST_CHECK_EQUAL(GetSerializeSize(char(0), 0, 0), 1U); BOOST_CHECK_EQUAL(GetSerializeSize(int8_t(0), 0, 0), 1U); BOOST_CHECK_EQUAL(GetSerializeSize(uint8_t(0), 0, 0), 1U); BOOST_CHECK_EQUAL(GetSerializeSize(int16_t(0), 0, 0), 2U); BOOST_CHECK_EQUAL(GetSerializeSize(uint16_t(0), 0, 0), 2U); BOOST_CHECK_EQUAL(GetSerializeSize(int32_t(0), 0, 0), 4U); BOOST_CHECK_EQUAL(GetSerializeSize(uint32_t(0), 0, 0), 4U); BOOST_CHECK_EQUAL(GetSerializeSize(int64_t(0), 0, 0), 8U); BOOST_CHECK_EQUAL(GetSerializeSize(uint64_t(0), 0, 0), 8U); BOOST_CHECK_EQUAL(GetSerializeSize(float(0), 0, 0), 4U); BOOST_CHECK_EQUAL(GetSerializeSize(double(0), 0, 0), 8U); BOOST_CHECK_EQUAL(GetSerializeSize(bool(0), 0, 0), 1U); } BOOST_AUTO_TEST_CASE(floats_conversion) { // Choose values that map unambiguously to binary floating point to avoid // rounding issues at the compiler side. BOOST_CHECK_EQUAL(ser_uint32_to_float(0x00000000), 0.0F); BOOST_CHECK_EQUAL(ser_uint32_to_float(0x3f000000), 0.5F); BOOST_CHECK_EQUAL(ser_uint32_to_float(0x3f800000), 1.0F); BOOST_CHECK_EQUAL(ser_uint32_to_float(0x40000000), 2.0F); BOOST_CHECK_EQUAL(ser_uint32_to_float(0x40800000), 4.0F); BOOST_CHECK_EQUAL(ser_uint32_to_float(0x44444444), 785.066650390625F); BOOST_CHECK_EQUAL(ser_float_to_uint32(0.0F), 0x00000000U); BOOST_CHECK_EQUAL(ser_float_to_uint32(0.5F), 0x3f000000U); BOOST_CHECK_EQUAL(ser_float_to_uint32(1.0F), 0x3f800000U); BOOST_CHECK_EQUAL(ser_float_to_uint32(2.0F), 0x40000000U); BOOST_CHECK_EQUAL(ser_float_to_uint32(4.0F), 0x40800000U); BOOST_CHECK_EQUAL(ser_float_to_uint32(785.066650390625F), 0x44444444U); } BOOST_AUTO_TEST_CASE(doubles_conversion) { // Choose values that map unambiguously to binary floating point to avoid // rounding issues at the compiler side. BOOST_CHECK_EQUAL(ser_uint64_to_double(0x0000000000000000ULL), 0.0); BOOST_CHECK_EQUAL(ser_uint64_to_double(0x3fe0000000000000ULL), 0.5); BOOST_CHECK_EQUAL(ser_uint64_to_double(0x3ff0000000000000ULL), 1.0); BOOST_CHECK_EQUAL(ser_uint64_to_double(0x4000000000000000ULL), 2.0); BOOST_CHECK_EQUAL(ser_uint64_to_double(0x4010000000000000ULL), 4.0); BOOST_CHECK_EQUAL(ser_uint64_to_double(0x4088888880000000ULL), 785.066650390625); BOOST_CHECK_EQUAL(ser_double_to_uint64(0.0), 0x0000000000000000ULL); BOOST_CHECK_EQUAL(ser_double_to_uint64(0.5), 0x3fe0000000000000ULL); BOOST_CHECK_EQUAL(ser_double_to_uint64(1.0), 0x3ff0000000000000ULL); BOOST_CHECK_EQUAL(ser_double_to_uint64(2.0), 0x4000000000000000ULL); BOOST_CHECK_EQUAL(ser_double_to_uint64(4.0), 0x4010000000000000ULL); BOOST_CHECK_EQUAL(ser_double_to_uint64(785.066650390625), 0x4088888880000000ULL); } /* Python code to generate the below hashes: def reversed_hex(x): return binascii.hexlify(''.join(reversed(x))) def dsha256(x): return hashlib.sha256(hashlib.sha256(x).digest()).digest() reversed_hex(dsha256(''.join(struct.pack('<f', x) for x in range(0,1000)))) == '8e8b4cf3e4df8b332057e3e23af42ebc663b61e0495d5e7e32d85099d7f3fe0c' reversed_hex(dsha256(''.join(struct.pack('<d', x) for x in range(0,1000)))) == '43d0c82591953c4eafe114590d392676a01585d25b25d433557f0d7878b23f96' */ BOOST_AUTO_TEST_CASE(floats) { CDataStream ss(SER_DISK, 0); // encode for (int i = 0; i < 1000; i++) { ss << float(i); } BOOST_CHECK(Hash(ss.begin(), ss.end()) == uint256S("8e8b4cf3e4df8b332057e3e23af42ebc663b61e0495d5e7e32d85" "099d7f3fe0c")); // decode for (int i = 0; i < 1000; i++) { float j; ss >> j; BOOST_CHECK_MESSAGE(i == j, "decoded:" << j << " expected:" << i); } } BOOST_AUTO_TEST_CASE(doubles) { CDataStream ss(SER_DISK, 0); // encode for (int i = 0; i < 1000; i++) { ss << double(i); } BOOST_CHECK(Hash(ss.begin(), ss.end()) == uint256S("43d0c82591953c4eafe114590d392676a01585d25b25d433557f0" "d7878b23f96")); // decode for (int i = 0; i < 1000; i++) { double j; ss >> j; BOOST_CHECK_MESSAGE(i == j, "decoded:" << j << " expected:" << i); } } BOOST_AUTO_TEST_CASE(varints) { // encode CDataStream ss(SER_DISK, 0); CDataStream::size_type size = 0; for (int i = 0; i < 100000; i++) { ss << VARINT(i, VarIntMode::NONNEGATIVE_SIGNED); size += ::GetSerializeSize(VARINT(i, VarIntMode::NONNEGATIVE_SIGNED), 0, 0); BOOST_CHECK(size == ss.size()); } for (uint64_t i = 0; i < 100000000000ULL; i += 999999937) { ss << VARINT(i); size += ::GetSerializeSize(VARINT(i), 0, 0); BOOST_CHECK(size == ss.size()); } // decode for (int i = 0; i < 100000; i++) { int j = -1; ss >> VARINT(j, VarIntMode::NONNEGATIVE_SIGNED); BOOST_CHECK_MESSAGE(i == j, "decoded:" << j << " expected:" << i); } for (uint64_t i = 0; i < 100000000000ULL; i += 999999937) { uint64_t j = -1; ss >> VARINT(j); BOOST_CHECK_MESSAGE(i == j, "decoded:" << j << " expected:" << i); } } BOOST_AUTO_TEST_CASE(varints_bitpatterns) { CDataStream ss(SER_DISK, 0); ss << VARINT(0, VarIntMode::NONNEGATIVE_SIGNED); BOOST_CHECK_EQUAL(HexStr(ss), "00"); ss.clear(); ss << VARINT(0x7f, VarIntMode::NONNEGATIVE_SIGNED); BOOST_CHECK_EQUAL(HexStr(ss), "7f"); ss.clear(); ss << VARINT((int8_t)0x7f, VarIntMode::NONNEGATIVE_SIGNED); BOOST_CHECK_EQUAL(HexStr(ss), "7f"); ss.clear(); ss << VARINT(0x80, VarIntMode::NONNEGATIVE_SIGNED); BOOST_CHECK_EQUAL(HexStr(ss), "8000"); ss.clear(); ss << VARINT((uint8_t)0x80); BOOST_CHECK_EQUAL(HexStr(ss), "8000"); ss.clear(); ss << VARINT(0x1234, VarIntMode::NONNEGATIVE_SIGNED); BOOST_CHECK_EQUAL(HexStr(ss), "a334"); ss.clear(); ss << VARINT((int16_t)0x1234, VarIntMode::NONNEGATIVE_SIGNED); BOOST_CHECK_EQUAL(HexStr(ss), "a334"); ss.clear(); ss << VARINT(0xffff, VarIntMode::NONNEGATIVE_SIGNED); BOOST_CHECK_EQUAL(HexStr(ss), "82fe7f"); ss.clear(); ss << VARINT((uint16_t)0xffff); BOOST_CHECK_EQUAL(HexStr(ss), "82fe7f"); ss.clear(); ss << VARINT(0x123456, VarIntMode::NONNEGATIVE_SIGNED); BOOST_CHECK_EQUAL(HexStr(ss), "c7e756"); ss.clear(); ss << VARINT((int32_t)0x123456, VarIntMode::NONNEGATIVE_SIGNED); BOOST_CHECK_EQUAL(HexStr(ss), "c7e756"); ss.clear(); ss << VARINT(0x80123456U); BOOST_CHECK_EQUAL(HexStr(ss), "86ffc7e756"); ss.clear(); ss << VARINT((uint32_t)0x80123456U); BOOST_CHECK_EQUAL(HexStr(ss), "86ffc7e756"); ss.clear(); ss << VARINT(0xffffffff); BOOST_CHECK_EQUAL(HexStr(ss), "8efefefe7f"); ss.clear(); ss << VARINT(0x7fffffffffffffffLL, VarIntMode::NONNEGATIVE_SIGNED); BOOST_CHECK_EQUAL(HexStr(ss), "fefefefefefefefe7f"); ss.clear(); ss << VARINT(0xffffffffffffffffULL); BOOST_CHECK_EQUAL(HexStr(ss), "80fefefefefefefefe7f"); ss.clear(); } static bool isTooLargeException(const std::ios_base::failure &ex) { std::ios_base::failure expectedException( "ReadCompactSize(): size too large"); // The string returned by what() can be different for different platforms. // Instead of directly comparing the ex.what() with an expected string, // create an instance of exception to see if ex.what() matches the expected // explanatory string returned by the exception instance. return strcmp(expectedException.what(), ex.what()) == 0; } BOOST_AUTO_TEST_CASE(compactsize) { CDataStream ss(SER_DISK, 0); std::vector<char>::size_type i, j; for (i = 1; i <= MAX_SIZE; i *= 2) { WriteCompactSize(ss, i - 1); WriteCompactSize(ss, i); } for (i = 1; i <= MAX_SIZE; i *= 2) { j = ReadCompactSize(ss); BOOST_CHECK_MESSAGE((i - 1) == j, "decoded:" << j << " expected:" << (i - 1)); j = ReadCompactSize(ss); BOOST_CHECK_MESSAGE(i == j, "decoded:" << j << " expected:" << i); } WriteCompactSize(ss, MAX_SIZE); BOOST_CHECK_EQUAL(ReadCompactSize(ss), MAX_SIZE); WriteCompactSize(ss, MAX_SIZE + 1); BOOST_CHECK_EXCEPTION(ReadCompactSize(ss), std::ios_base::failure, isTooLargeException); WriteCompactSize(ss, std::numeric_limits<int64_t>::max()); BOOST_CHECK_EXCEPTION(ReadCompactSize(ss), std::ios_base::failure, isTooLargeException); WriteCompactSize(ss, std::numeric_limits<uint64_t>::max()); BOOST_CHECK_EXCEPTION(ReadCompactSize(ss), std::ios_base::failure, isTooLargeException); } static bool isCanonicalException(const std::ios_base::failure &ex) { std::ios_base::failure expectedException("non-canonical ReadCompactSize()"); // The string returned by what() can be different for different platforms. // Instead of directly comparing the ex.what() with an expected string, // create an instance of exception to see if ex.what() matches the expected // explanatory string returned by the exception instance. return strcmp(expectedException.what(), ex.what()) == 0; } BOOST_AUTO_TEST_CASE(noncanonical) { // Write some non-canonical CompactSize encodings, and make sure an // exception is thrown when read back. CDataStream ss(SER_DISK, 0); std::vector<char>::size_type n; // zero encoded with three bytes: ss.write("\xfd\x00\x00", 3); BOOST_CHECK_EXCEPTION(ReadCompactSize(ss), std::ios_base::failure, isCanonicalException); // 0xfc encoded with three bytes: ss.write("\xfd\xfc\x00", 3); BOOST_CHECK_EXCEPTION(ReadCompactSize(ss), std::ios_base::failure, isCanonicalException); // 0xfd encoded with three bytes is OK: ss.write("\xfd\xfd\x00", 3); n = ReadCompactSize(ss); BOOST_CHECK(n == 0xfd); // zero encoded with five bytes: ss.write("\xfe\x00\x00\x00\x00", 5); BOOST_CHECK_EXCEPTION(ReadCompactSize(ss), std::ios_base::failure, isCanonicalException); // 0xffff encoded with five bytes: ss.write("\xfe\xff\xff\x00\x00", 5); BOOST_CHECK_EXCEPTION(ReadCompactSize(ss), std::ios_base::failure, isCanonicalException); // zero encoded with nine bytes: ss.write("\xff\x00\x00\x00\x00\x00\x00\x00\x00", 9); BOOST_CHECK_EXCEPTION(ReadCompactSize(ss), std::ios_base::failure, isCanonicalException); // 0x01ffffff encoded with nine bytes: ss.write("\xff\xff\xff\xff\x01\x00\x00\x00\x00", 9); BOOST_CHECK_EXCEPTION(ReadCompactSize(ss), std::ios_base::failure, isCanonicalException); } BOOST_AUTO_TEST_CASE(insert_delete) { // Test inserting/deleting bytes. CDataStream ss(SER_DISK, 0); BOOST_CHECK_EQUAL(ss.size(), 0U); ss.write("\x00\x01\x02\xff", 4); BOOST_CHECK_EQUAL(ss.size(), 4U); char c = (char)11; // Inserting at beginning/end/middle: ss.insert(ss.begin(), c); BOOST_CHECK_EQUAL(ss.size(), 5U); BOOST_CHECK_EQUAL(ss[0], c); BOOST_CHECK_EQUAL(ss[1], 0); ss.insert(ss.end(), c); BOOST_CHECK_EQUAL(ss.size(), 6U); BOOST_CHECK_EQUAL(ss[4], (char)0xff); BOOST_CHECK_EQUAL(ss[5], c); ss.insert(ss.begin() + 2, c); BOOST_CHECK_EQUAL(ss.size(), 7U); BOOST_CHECK_EQUAL(ss[2], c); // Delete at beginning/end/middle ss.erase(ss.begin()); BOOST_CHECK_EQUAL(ss.size(), 6U); BOOST_CHECK_EQUAL(ss[0], 0); ss.erase(ss.begin() + ss.size() - 1); BOOST_CHECK_EQUAL(ss.size(), 5U); BOOST_CHECK_EQUAL(ss[4], (char)0xff); ss.erase(ss.begin() + 1); BOOST_CHECK_EQUAL(ss.size(), 4U); BOOST_CHECK_EQUAL(ss[0], 0); BOOST_CHECK_EQUAL(ss[1], 1); BOOST_CHECK_EQUAL(ss[2], 2); BOOST_CHECK_EQUAL(ss[3], (char)0xff); // Make sure GetAndClear does the right thing: CSerializeData d; ss.GetAndClear(d); BOOST_CHECK_EQUAL(ss.size(), 0U); } BOOST_AUTO_TEST_CASE(class_methods) { int intval(100); bool boolval(true); std::string stringval("testing"); - const char *charstrval("testing charstr"); + const char charstrval[16] = "testing charstr"; CMutableTransaction txval; CTransactionRef tx_ref{MakeTransactionRef(txval)}; CSerializeMethodsTestSingle methodtest1(intval, boolval, stringval, charstrval, tx_ref); CSerializeMethodsTestMany methodtest2(intval, boolval, stringval, charstrval, tx_ref); CSerializeMethodsTestSingle methodtest3; CSerializeMethodsTestMany methodtest4; CDataStream ss(SER_DISK, PROTOCOL_VERSION); BOOST_CHECK(methodtest1 == methodtest2); ss << methodtest1; ss >> methodtest4; ss << methodtest2; ss >> methodtest3; BOOST_CHECK(methodtest1 == methodtest2); BOOST_CHECK(methodtest2 == methodtest3); BOOST_CHECK(methodtest3 == methodtest4); CDataStream ss2(SER_DISK, PROTOCOL_VERSION, intval, boolval, stringval, - FLATDATA(charstrval), txval); + charstrval, txval); ss2 >> methodtest3; BOOST_CHECK(methodtest3 == methodtest4); } BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/streams_tests.cpp b/src/test/streams_tests.cpp index 9bfcede91..baf00c69f 100644 --- a/src/test/streams_tests.cpp +++ b/src/test/streams_tests.cpp @@ -1,222 +1,220 @@ // Copyright (c) 2012-2016 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include <streams.h> #include <support/allocators/zeroafterfree.h> #include <test/test_bitcoin.h> #include <boost/assign/std/vector.hpp> // for 'operator+=()' #include <boost/test/unit_test.hpp> using namespace boost::assign; // bring 'operator+=()' into scope BOOST_FIXTURE_TEST_SUITE(streams_tests, BasicTestingSetup) BOOST_AUTO_TEST_CASE(streams_vector_writer) { uint8_t a(1); uint8_t b(2); uint8_t bytes[] = {3, 4, 5, 6}; std::vector<uint8_t> vch; // Each test runs twice. Serializing a second time at the same starting // point should yield the same results, even if the first test grew the // vector. CVectorWriter(SER_NETWORK, INIT_PROTO_VERSION, vch, 0, a, b); BOOST_CHECK((vch == std::vector<uint8_t>{{1, 2}})); CVectorWriter(SER_NETWORK, INIT_PROTO_VERSION, vch, 0, a, b); BOOST_CHECK((vch == std::vector<uint8_t>{{1, 2}})); vch.clear(); CVectorWriter(SER_NETWORK, INIT_PROTO_VERSION, vch, 2, a, b); BOOST_CHECK((vch == std::vector<uint8_t>{{0, 0, 1, 2}})); CVectorWriter(SER_NETWORK, INIT_PROTO_VERSION, vch, 2, a, b); BOOST_CHECK((vch == std::vector<uint8_t>{{0, 0, 1, 2}})); vch.clear(); vch.resize(5, 0); CVectorWriter(SER_NETWORK, INIT_PROTO_VERSION, vch, 2, a, b); BOOST_CHECK((vch == std::vector<uint8_t>{{0, 0, 1, 2, 0}})); CVectorWriter(SER_NETWORK, INIT_PROTO_VERSION, vch, 2, a, b); BOOST_CHECK((vch == std::vector<uint8_t>{{0, 0, 1, 2, 0}})); vch.clear(); vch.resize(4, 0); CVectorWriter(SER_NETWORK, INIT_PROTO_VERSION, vch, 3, a, b); BOOST_CHECK((vch == std::vector<uint8_t>{{0, 0, 0, 1, 2}})); CVectorWriter(SER_NETWORK, INIT_PROTO_VERSION, vch, 3, a, b); BOOST_CHECK((vch == std::vector<uint8_t>{{0, 0, 0, 1, 2}})); vch.clear(); vch.resize(4, 0); CVectorWriter(SER_NETWORK, INIT_PROTO_VERSION, vch, 4, a, b); BOOST_CHECK((vch == std::vector<uint8_t>{{0, 0, 0, 0, 1, 2}})); CVectorWriter(SER_NETWORK, INIT_PROTO_VERSION, vch, 4, a, b); BOOST_CHECK((vch == std::vector<uint8_t>{{0, 0, 0, 0, 1, 2}})); vch.clear(); - CVectorWriter(SER_NETWORK, INIT_PROTO_VERSION, vch, 0, FLATDATA(bytes)); + CVectorWriter(SER_NETWORK, INIT_PROTO_VERSION, vch, 0, bytes); BOOST_CHECK((vch == std::vector<uint8_t>{{3, 4, 5, 6}})); - CVectorWriter(SER_NETWORK, INIT_PROTO_VERSION, vch, 0, FLATDATA(bytes)); + CVectorWriter(SER_NETWORK, INIT_PROTO_VERSION, vch, 0, bytes); BOOST_CHECK((vch == std::vector<uint8_t>{{3, 4, 5, 6}})); vch.clear(); vch.resize(4, 8); - CVectorWriter(SER_NETWORK, INIT_PROTO_VERSION, vch, 2, a, FLATDATA(bytes), - b); + CVectorWriter(SER_NETWORK, INIT_PROTO_VERSION, vch, 2, a, bytes, b); BOOST_CHECK((vch == std::vector<uint8_t>{{8, 8, 1, 3, 4, 5, 6, 2}})); - CVectorWriter(SER_NETWORK, INIT_PROTO_VERSION, vch, 2, a, FLATDATA(bytes), - b); + CVectorWriter(SER_NETWORK, INIT_PROTO_VERSION, vch, 2, a, bytes, b); BOOST_CHECK((vch == std::vector<uint8_t>{{8, 8, 1, 3, 4, 5, 6, 2}})); vch.clear(); } BOOST_AUTO_TEST_CASE(streams_vector_reader) { std::vector<uint8_t> vch = {1, 255, 3, 4, 5, 6}; VectorReader reader(SER_NETWORK, INIT_PROTO_VERSION, vch, 0); BOOST_CHECK_EQUAL(reader.size(), 6); BOOST_CHECK(!reader.empty()); // Read a single byte as an uint8_t. uint8_t a; reader >> a; BOOST_CHECK_EQUAL(a, 1); BOOST_CHECK_EQUAL(reader.size(), 5); BOOST_CHECK(!reader.empty()); // Read a single byte as a (signed) int8_t. int8_t b; reader >> b; BOOST_CHECK_EQUAL(b, -1); BOOST_CHECK_EQUAL(reader.size(), 4); BOOST_CHECK(!reader.empty()); // Read a 4 bytes as an unsigned uint32_t. uint32_t c; reader >> c; // 100992003 = 3,4,5,6 in little-endian base-256 BOOST_CHECK_EQUAL(c, 100992003); BOOST_CHECK_EQUAL(reader.size(), 0); BOOST_CHECK(reader.empty()); // Reading after end of byte vector throws an error. int32_t d; BOOST_CHECK_THROW(reader >> d, std::ios_base::failure); // Read a 4 bytes as a (signed) int32_t from the beginning of the buffer. VectorReader new_reader(SER_NETWORK, INIT_PROTO_VERSION, vch, 0); new_reader >> d; // 67370753 = 1,255,3,4 in little-endian base-256 BOOST_CHECK_EQUAL(d, 67370753); BOOST_CHECK_EQUAL(new_reader.size(), 2); BOOST_CHECK(!new_reader.empty()); // Reading after end of byte vector throws an error even if the reader is // not totally empty. BOOST_CHECK_THROW(new_reader >> d, std::ios_base::failure); } BOOST_AUTO_TEST_CASE(bitstream_reader_writer) { CDataStream data(SER_NETWORK, INIT_PROTO_VERSION); BitStreamWriter<CDataStream> bit_writer(data); bit_writer.Write(0, 1); bit_writer.Write(2, 2); bit_writer.Write(6, 3); bit_writer.Write(11, 4); bit_writer.Write(1, 5); bit_writer.Write(32, 6); bit_writer.Write(7, 7); bit_writer.Write(30497, 16); bit_writer.Flush(); CDataStream data_copy(data); uint32_t serialized_int1; data >> serialized_int1; // NOTE: Serialized as LE BOOST_CHECK_EQUAL(serialized_int1, (uint32_t)0x7700C35A); uint16_t serialized_int2; data >> serialized_int2; // NOTE: Serialized as LE BOOST_CHECK_EQUAL(serialized_int2, (uint16_t)0x1072); BitStreamReader<CDataStream> bit_reader(data_copy); BOOST_CHECK_EQUAL(bit_reader.Read(1), 0); BOOST_CHECK_EQUAL(bit_reader.Read(2), 2); BOOST_CHECK_EQUAL(bit_reader.Read(3), 6); BOOST_CHECK_EQUAL(bit_reader.Read(4), 11); BOOST_CHECK_EQUAL(bit_reader.Read(5), 1); BOOST_CHECK_EQUAL(bit_reader.Read(6), 32); BOOST_CHECK_EQUAL(bit_reader.Read(7), 7); BOOST_CHECK_EQUAL(bit_reader.Read(16), 30497); BOOST_CHECK_THROW(bit_reader.Read(8), std::ios_base::failure); } BOOST_AUTO_TEST_CASE(streams_serializedata_xor) { std::vector<char> in; std::vector<char> expected_xor; std::vector<uint8_t> key; CDataStream ds(in, 0, 0); // Degenerate case key += '\x00', '\x00'; ds.Xor(key); BOOST_CHECK_EQUAL(std::string(expected_xor.begin(), expected_xor.end()), std::string(ds.begin(), ds.end())); in += '\x0f', '\xf0'; expected_xor += '\xf0', '\x0f'; // Single character key ds.clear(); ds.insert(ds.begin(), in.begin(), in.end()); key.clear(); key += '\xff'; ds.Xor(key); BOOST_CHECK_EQUAL(std::string(expected_xor.begin(), expected_xor.end()), std::string(ds.begin(), ds.end())); // Multi character key in.clear(); expected_xor.clear(); in += '\xf0', '\x0f'; expected_xor += '\x0f', '\x00'; ds.clear(); ds.insert(ds.begin(), in.begin(), in.end()); key.clear(); key += '\xff', '\x0f'; ds.Xor(key); BOOST_CHECK_EQUAL(std::string(expected_xor.begin(), expected_xor.end()), std::string(ds.begin(), ds.end())); } BOOST_AUTO_TEST_CASE(streams_empty_vector) { std::vector<char> in; CDataStream ds(in, 0, 0); // read 0 bytes used to cause a segfault on some older systems. ds.read(nullptr, 0); // Same goes for writing 0 bytes from a vector ... const std::vector<char> vdata{'f', 'o', 'o', 'b', 'a', 'r'}; ds.insert(ds.begin(), vdata.begin(), vdata.begin()); ds.insert(ds.begin(), vdata.begin(), vdata.end()); // ... or an array. const char adata[6] = {'f', 'o', 'o', 'b', 'a', 'r'}; ds.insert(ds.begin(), &adata[0], &adata[0]); ds.insert(ds.begin(), &adata[0], &adata[6]); } BOOST_AUTO_TEST_SUITE_END() diff --git a/src/validation.cpp b/src/validation.cpp index b16dff213..57fa03607 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -1,5757 +1,5757 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2009-2016 The Bitcoin Core developers // Copyright (c) 2017-2018 The Bitcoin developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include <validation.h> #include <arith_uint256.h> #include <blockindexworkcomparator.h> #include <blockvalidity.h> #include <chainparams.h> #include <checkpoints.h> #include <checkqueue.h> #include <config.h> #include <consensus/activation.h> #include <consensus/consensus.h> #include <consensus/merkle.h> #include <consensus/tx_verify.h> #include <consensus/validation.h> #include <flatfile.h> #include <fs.h> #include <hash.h> #include <index/txindex.h> #include <init.h> #include <policy/fees.h> #include <policy/policy.h> #include <pow.h> #include <primitives/block.h> #include <primitives/transaction.h> #include <random.h> #include <reverse_iterator.h> #include <script/script.h> #include <script/scriptcache.h> #include <script/sigcache.h> #include <script/standard.h> #include <timedata.h> #include <tinyformat.h> #include <txdb.h> #include <txmempool.h> #include <ui_interface.h> #include <undo.h> #include <util/moneystr.h> #include <util/strencodings.h> #include <util/system.h> #include <validationinterface.h> #include <warnings.h> #include <boost/algorithm/string/replace.hpp> #include <boost/thread.hpp> // boost::this_thread::interruption_point() (mingw) #include <atomic> #include <future> #include <sstream> #include <thread> #define MICRO 0.000001 #define MILLI 0.001 class ConnectTrace; /** * CChainState stores and provides an API to update our local knowledge of the * current best chain and header tree. * * It generally provides access to the current block tree, as well as functions * to provide new data, which it will appropriately validate and incorporate in * its state as necessary. * * Eventually, the API here is targeted at being exposed externally as a * consumable libconsensus library, so any functions added must only call * other class member functions, pure functions in other parts of the consensus * library, callbacks via the validation interface, or read/write-to-disk * functions (eventually this will also be via callbacks). */ class CChainState { private: /** * The set of all CBlockIndex entries with BLOCK_VALID_TRANSACTIONS (for * itself and all ancestors) and as good as our current tip or better. * Entries may be failed, though, and pruning nodes may be missing the data * for the block. */ std::set<CBlockIndex *, CBlockIndexWorkComparator> setBlockIndexCandidates; /** * the ChainState CriticalSection * A lock that must be held when modifying this ChainState - held in * ActivateBestChain() */ CCriticalSection m_cs_chainstate; /** * Every received block is assigned a unique and increasing identifier, so * we know which one to give priority in case of a fork. * Blocks loaded from disk are assigned id 0, so start the counter at 1. */ std::atomic<int32_t> nBlockSequenceId{1}; /** Decreasing counter (used by subsequent preciousblock calls). */ int32_t nBlockReverseSequenceId = -1; /** chainwork for the last block that preciousblock has been applied to. */ arith_uint256 nLastPreciousChainwork = 0; /** * In order to efficiently track invalidity of headers, we keep the set of * blocks which we tried to connect and found to be invalid here (ie which * were set to BLOCK_FAILED_VALID since the last restart). We can then * walk this set and check if a new header is a descendant of something in * this set, preventing us from having to walk mapBlockIndex when we try * to connect a bad block and fail. * * While this is more complicated than marking everything which descends * from an invalid block as invalid at the time we discover it to be * invalid, doing so would require walking all of mapBlockIndex to find all * descendants. Since this case should be very rare, keeping track of all * BLOCK_FAILED_VALID blocks in a set should be just fine and work just as * well. * * Because we already walk mapBlockIndex in height-order at startup, we go * ahead and mark descendants of invalid blocks as FAILED_CHILD at that * time, instead of putting things in this set. */ std::set<CBlockIndex *> m_failed_blocks; public: CChain chainActive; BlockMap mapBlockIndex; std::multimap<CBlockIndex *, CBlockIndex *> mapBlocksUnlinked; CBlockIndex *pindexBestInvalid = nullptr; CBlockIndex *pindexBestParked = nullptr; bool LoadBlockIndex(const Config &config, CBlockTreeDB &blocktree); bool ActivateBestChain( const Config &config, CValidationState &state, std::shared_ptr<const CBlock> pblock = std::shared_ptr<const CBlock>()); /** * If a block header hasn't already been seen, call CheckBlockHeader on it, * ensure that it doesn't descend from an invalid block, and then add it to * mapBlockIndex. */ bool AcceptBlockHeader(const Config &config, const CBlockHeader &block, CValidationState &state, CBlockIndex **ppindex); bool AcceptBlock(const Config &config, const std::shared_ptr<const CBlock> &pblock, CValidationState &state, bool fRequested, const FlatFilePos *dbp, bool *fNewBlock); // Block (dis)connection on a given view: DisconnectResult DisconnectBlock(const CBlock &block, const CBlockIndex *pindex, CCoinsViewCache &view); bool ConnectBlock(const CBlock &block, CValidationState &state, CBlockIndex *pindex, CCoinsViewCache &view, const CChainParams ¶ms, BlockValidationOptions options, bool fJustCheck = false) EXCLUSIVE_LOCKS_REQUIRED(cs_main); // Block disconnection on our pcoinsTip: bool DisconnectTip(const Config &config, CValidationState &state, DisconnectedBlockTransactions *disconnectpool) EXCLUSIVE_LOCKS_REQUIRED(cs_main); // Manual block validity manipulation: bool PreciousBlock(const Config &config, CValidationState &state, CBlockIndex *pindex); bool UnwindBlock(const Config &config, CValidationState &state, CBlockIndex *pindex, bool invalidate); void ResetBlockFailureFlags(CBlockIndex *pindex); template <typename F> void UpdateFlagsForBlock(CBlockIndex *pindexBase, CBlockIndex *pindex, F f); template <typename F, typename C> void UpdateFlags(CBlockIndex *pindex, F f, C fchild) EXCLUSIVE_LOCKS_REQUIRED(cs_main); template <typename F> void UpdateFlags(CBlockIndex *pindex, F f) EXCLUSIVE_LOCKS_REQUIRED(cs_main); /** Remove parked status from a block and its descendants. */ void UnparkBlockImpl(CBlockIndex *pindex, bool fClearChildren); bool ReplayBlocks(const Consensus::Params ¶ms, CCoinsView *view); bool RewindBlockIndex(const Config &config); bool LoadGenesisBlock(const CChainParams &chainparams); void PruneBlockIndexCandidates(); void UnloadBlockIndex(); private: bool ActivateBestChainStep(const Config &config, CValidationState &state, CBlockIndex *pindexMostWork, const std::shared_ptr<const CBlock> &pblock, bool &fInvalidFound, ConnectTrace &connectTrace) EXCLUSIVE_LOCKS_REQUIRED(cs_main); bool ConnectTip(const Config &config, CValidationState &state, CBlockIndex *pindexNew, const std::shared_ptr<const CBlock> &pblock, ConnectTrace &connectTrace, DisconnectedBlockTransactions &disconnectpool) EXCLUSIVE_LOCKS_REQUIRED(cs_main); CBlockIndex *AddToBlockIndex(const CBlockHeader &block) EXCLUSIVE_LOCKS_REQUIRED(cs_main); /** Create a new block index entry for a given block hash */ CBlockIndex *InsertBlockIndex(const uint256 &hash); /** * Make various assertions about the state of the block index. * * By default this only executes fully when using the Regtest chain; see: * fCheckBlockIndex. */ void CheckBlockIndex(const Consensus::Params &consensusParams); void InvalidBlockFound(CBlockIndex *pindex, const CValidationState &state) EXCLUSIVE_LOCKS_REQUIRED(cs_main); CBlockIndex *FindMostWorkChain() EXCLUSIVE_LOCKS_REQUIRED(cs_main); bool ReceivedBlockTransactions(const CBlock &block, CValidationState &state, CBlockIndex *pindexNew, const FlatFilePos &pos) EXCLUSIVE_LOCKS_REQUIRED(cs_main); bool RollforwardBlock(const CBlockIndex *pindex, CCoinsViewCache &inputs, const Consensus::Params ¶ms); } g_chainstate; /** * Global state */ CCriticalSection cs_main; BlockMap &mapBlockIndex = g_chainstate.mapBlockIndex; CChain &chainActive = g_chainstate.chainActive; CBlockIndex *pindexBestHeader = nullptr; Mutex g_best_block_mutex; std::condition_variable g_best_block_cv; uint256 g_best_block; int nScriptCheckThreads = 0; std::atomic_bool fImporting(false); std::atomic_bool fReindex(false); bool fHavePruned = false; bool fPruneMode = false; bool fIsBareMultisigStd = DEFAULT_PERMIT_BAREMULTISIG; bool fRequireStandard = true; bool fCheckBlockIndex = false; bool fCheckpointsEnabled = DEFAULT_CHECKPOINTS_ENABLED; size_t nCoinCacheUsage = 5000 * 300; uint64_t nPruneTarget = 0; int64_t nMaxTipAge = DEFAULT_MAX_TIP_AGE; uint256 hashAssumeValid; arith_uint256 nMinimumChainWork; CFeeRate minRelayTxFee = CFeeRate(DEFAULT_MIN_RELAY_TX_FEE_PER_KB); Amount maxTxFee = DEFAULT_TRANSACTION_MAXFEE; CTxMemPool g_mempool; std::atomic_bool g_is_mempool_loaded{false}; /** Constant stuff for coinbase transactions we create: */ CScript COINBASE_FLAGS; const std::string strMessageMagic = "Bitcoin Signed Message:\n"; // Internal stuff namespace { CBlockIndex *&pindexBestInvalid = g_chainstate.pindexBestInvalid; CBlockIndex *&pindexBestParked = g_chainstate.pindexBestParked; /** * The best finalized block. * This block cannot be reorged in any way, shape or form. */ CBlockIndex const *pindexFinalized; /** * All pairs A->B, where A (or one of its ancestors) misses transactions, but B * has transactions. Pruned nodes may have entries where B is missing data. */ std::multimap<CBlockIndex *, CBlockIndex *> &mapBlocksUnlinked = g_chainstate.mapBlocksUnlinked; CCriticalSection cs_LastBlockFile; std::vector<CBlockFileInfo> vinfoBlockFile; int nLastBlockFile = 0; /** * Global flag to indicate we should check to see if there are block/undo files * that should be deleted. Set on startup or if we allocate more file space when * we're in prune mode. */ bool fCheckForPruning = false; /** Dirty block index entries. */ std::set<const CBlockIndex *> setDirtyBlockIndex; /** Dirty block file entries. */ std::set<int> setDirtyFileInfo; } // namespace BlockValidationOptions::BlockValidationOptions(const Config &config) : excessiveBlockSize(config.GetMaxBlockSize()), checkPoW(true), checkMerkleRoot(true) {} CBlockIndex *FindForkInGlobalIndex(const CChain &chain, const CBlockLocator &locator) { AssertLockHeld(cs_main); // Find the first block the caller has in the main chain for (const uint256 &hash : locator.vHave) { CBlockIndex *pindex = LookupBlockIndex(hash); if (pindex) { if (chain.Contains(pindex)) { return pindex; } if (pindex->GetAncestor(chain.Height()) == chain.Tip()) { return chain.Tip(); } } } return chain.Genesis(); } std::unique_ptr<CCoinsViewDB> pcoinsdbview; std::unique_ptr<CCoinsViewCache> pcoinsTip; std::unique_ptr<CBlockTreeDB> pblocktree; enum class FlushStateMode { NONE, IF_NEEDED, PERIODIC, ALWAYS }; // See definition for documentation static bool FlushStateToDisk(const CChainParams &chainParams, CValidationState &state, FlushStateMode mode, int nManualPruneHeight = 0); static void FindFilesToPruneManual(std::set<int> &setFilesToPrune, int nManualPruneHeight); static void FindFilesToPrune(std::set<int> &setFilesToPrune, uint64_t nPruneAfterHeight); static FILE *OpenUndoFile(const FlatFilePos &pos, bool fReadOnly = false); static FlatFileSeq BlockFileSeq(); static FlatFileSeq UndoFileSeq(); static uint32_t GetNextBlockScriptFlags(const Consensus::Params ¶ms, const CBlockIndex *pindex); bool TestLockPointValidity(const LockPoints *lp) { AssertLockHeld(cs_main); assert(lp); // If there are relative lock times then the maxInputBlock will be set // If there are no relative lock times, the LockPoints don't depend on the // chain if (lp->maxInputBlock) { // Check whether chainActive is an extension of the block at which the // LockPoints calculation was valid. If not LockPoints are no longer // valid. if (!chainActive.Contains(lp->maxInputBlock)) { return false; } } // LockPoints still valid return true; } bool CheckSequenceLocks(const CTxMemPool &pool, const CTransaction &tx, int flags, LockPoints *lp, bool useExistingLockPoints) { AssertLockHeld(cs_main); AssertLockHeld(pool.cs); CBlockIndex *tip = chainActive.Tip(); assert(tip != nullptr); CBlockIndex index; index.pprev = tip; // CheckSequenceLocks() uses chainActive.Height()+1 to evaluate height based // locks because when SequenceLocks() is called within ConnectBlock(), the // height of the block *being* evaluated is what is used. Thus if we want to // know if a transaction can be part of the *next* block, we need to use one // more than chainActive.Height() index.nHeight = tip->nHeight + 1; std::pair<int, int64_t> lockPair; if (useExistingLockPoints) { assert(lp); lockPair.first = lp->height; lockPair.second = lp->time; } else { // pcoinsTip contains the UTXO set for chainActive.Tip() CCoinsViewMemPool viewMemPool(pcoinsTip.get(), pool); std::vector<int> prevheights; prevheights.resize(tx.vin.size()); for (size_t txinIndex = 0; txinIndex < tx.vin.size(); txinIndex++) { const CTxIn &txin = tx.vin[txinIndex]; Coin coin; if (!viewMemPool.GetCoin(txin.prevout, coin)) { return error("%s: Missing input", __func__); } if (coin.GetHeight() == MEMPOOL_HEIGHT) { // Assume all mempool transaction confirm in the next block prevheights[txinIndex] = tip->nHeight + 1; } else { prevheights[txinIndex] = coin.GetHeight(); } } lockPair = CalculateSequenceLocks(tx, flags, &prevheights, index); if (lp) { lp->height = lockPair.first; lp->time = lockPair.second; // Also store the hash of the block with the highest height of all // the blocks which have sequence locked prevouts. This hash needs // to still be on the chain for these LockPoint calculations to be // valid. // Note: It is impossible to correctly calculate a maxInputBlock if // any of the sequence locked inputs depend on unconfirmed txs, // except in the special case where the relative lock time/height is // 0, which is equivalent to no sequence lock. Since we assume input // height of tip+1 for mempool txs and test the resulting lockPair // from CalculateSequenceLocks against tip+1. We know // EvaluateSequenceLocks will fail if there was a non-zero sequence // lock on a mempool input, so we can use the return value of // CheckSequenceLocks to indicate the LockPoints validity. int maxInputHeight = 0; for (int height : prevheights) { // Can ignore mempool inputs since we'll fail if they had // non-zero locks. if (height != tip->nHeight + 1) { maxInputHeight = std::max(maxInputHeight, height); } } lp->maxInputBlock = tip->GetAncestor(maxInputHeight); } } return EvaluateSequenceLocks(index, lockPair); } /** Convert CValidationState to a human-readable message for logging */ std::string FormatStateMessage(const CValidationState &state) { return strprintf( "%s%s (code %i)", state.GetRejectReason(), state.GetDebugMessage().empty() ? "" : ", " + state.GetDebugMessage(), state.GetRejectCode()); } static bool IsMagneticAnomalyEnabledForCurrentBlock(const Consensus::Params ¶ms) EXCLUSIVE_LOCKS_REQUIRED(cs_main) { AssertLockHeld(cs_main); return IsMagneticAnomalyEnabled(params, chainActive.Tip()); } static bool IsGravitonEnabledForCurrentBlock(const Consensus::Params ¶ms) EXCLUSIVE_LOCKS_REQUIRED(cs_main) { AssertLockHeld(cs_main); return IsGravitonEnabled(params, chainActive.Tip()); } // Command-line argument "-replayprotectionactivationtime=<timestamp>" will // cause the node to switch to replay protected SigHash ForkID value when the // median timestamp of the previous 11 blocks is greater than or equal to // <timestamp>. Defaults to the pre-defined timestamp when not set. static bool IsReplayProtectionEnabled(const Consensus::Params ¶ms, int64_t nMedianTimePast) { return nMedianTimePast >= gArgs.GetArg("-replayprotectionactivationtime", params.phononActivationTime); } static bool IsReplayProtectionEnabled(const Consensus::Params ¶ms, const CBlockIndex *pindexPrev) { if (pindexPrev == nullptr) { return false; } return IsReplayProtectionEnabled(params, pindexPrev->GetMedianTimePast()); } static bool IsReplayProtectionEnabledForCurrentBlock(const Consensus::Params ¶ms) EXCLUSIVE_LOCKS_REQUIRED(cs_main) { AssertLockHeld(cs_main); return IsReplayProtectionEnabled(params, chainActive.Tip()); } // Used to avoid mempool polluting consensus critical paths if CCoinsViewMempool // were somehow broken and returning the wrong scriptPubKeys static bool CheckInputsFromMempoolAndCache( const CTransaction &tx, CValidationState &state, const CCoinsViewCache &view, const CTxMemPool &pool, const uint32_t flags, bool cacheSigStore, PrecomputedTransactionData &txdata) EXCLUSIVE_LOCKS_REQUIRED(cs_main) { AssertLockHeld(cs_main); // pool.cs should be locked already, but go ahead and re-take the lock here // to enforce that mempool doesn't change between when we check the view and // when we actually call through to CheckInputs LOCK(pool.cs); assert(!tx.IsCoinBase()); for (const CTxIn &txin : tx.vin) { const Coin &coin = view.AccessCoin(txin.prevout); // At this point we haven't actually checked if the coins are all // available (or shouldn't assume we have, since CheckInputs does). So // we just return failure if the inputs are not available here, and then // only have to check equivalence for available inputs. if (coin.IsSpent()) { return false; } const CTransactionRef &txFrom = pool.get(txin.prevout.GetTxId()); if (txFrom) { assert(txFrom->GetId() == txin.prevout.GetTxId()); assert(txFrom->vout.size() > txin.prevout.GetN()); assert(txFrom->vout[txin.prevout.GetN()] == coin.GetTxOut()); } else { const Coin &coinFromDisk = pcoinsTip->AccessCoin(txin.prevout); assert(!coinFromDisk.IsSpent()); assert(coinFromDisk.GetTxOut() == coin.GetTxOut()); } } return CheckInputs(tx, state, view, true, flags, cacheSigStore, true, txdata); } static bool AcceptToMemoryPoolWorker( const Config &config, CTxMemPool &pool, CValidationState &state, const CTransactionRef &ptx, bool fLimitFree, bool *pfMissingInputs, int64_t nAcceptTime, bool fOverrideMempoolLimit, const Amount nAbsurdFee, std::vector<COutPoint> &coins_to_uncache, bool test_accept) EXCLUSIVE_LOCKS_REQUIRED(cs_main) { AssertLockHeld(cs_main); const Consensus::Params &consensusParams = config.GetChainParams().GetConsensus(); const CTransaction &tx = *ptx; const TxId txid = tx.GetId(); // mempool "read lock" (held through // GetMainSignals().TransactionAddedToMempool()) LOCK(pool.cs); if (pfMissingInputs) { *pfMissingInputs = false; } // Coinbase is only valid in a block, not as a loose transaction. if (!CheckRegularTransaction(tx, state)) { // state filled in by CheckRegularTransaction. return false; } // Rather not work on nonstandard transactions (unless -testnet/-regtest) std::string reason; if (fRequireStandard && !IsStandardTx(tx, reason)) { return state.DoS(0, false, REJECT_NONSTANDARD, reason); } // Only accept nLockTime-using transactions that can be mined in the next // block; we don't want our mempool filled up with transactions that can't // be mined yet. CValidationState ctxState; if (!ContextualCheckTransactionForCurrentBlock( consensusParams, tx, ctxState, STANDARD_LOCKTIME_VERIFY_FLAGS)) { // We copy the state from a dummy to ensure we don't increase the // ban score of peer for transaction that could be valid in the future. return state.DoS( 0, false, REJECT_NONSTANDARD, ctxState.GetRejectReason(), ctxState.CorruptionPossible(), ctxState.GetDebugMessage()); } // Is it already in the memory pool? if (pool.exists(txid)) { return state.Invalid(false, REJECT_DUPLICATE, "txn-already-in-mempool"); } // Check for conflicts with in-memory transactions for (const CTxIn &txin : tx.vin) { auto itConflicting = pool.mapNextTx.find(txin.prevout); if (itConflicting != pool.mapNextTx.end()) { // Disable replacement feature for good return state.Invalid(false, REJECT_DUPLICATE, "txn-mempool-conflict"); } } { CCoinsView dummy; CCoinsViewCache view(&dummy); LockPoints lp; CCoinsViewMemPool viewMemPool(pcoinsTip.get(), pool); view.SetBackend(viewMemPool); // Do all inputs exist? for (const CTxIn &txin : tx.vin) { if (!pcoinsTip->HaveCoinInCache(txin.prevout)) { coins_to_uncache.push_back(txin.prevout); } if (!view.HaveCoin(txin.prevout)) { // Are inputs missing because we already have the tx? for (size_t out = 0; out < tx.vout.size(); out++) { // Optimistically just do efficient check of cache for // outputs. if (pcoinsTip->HaveCoinInCache(COutPoint(txid, out))) { return state.Invalid(false, REJECT_DUPLICATE, "txn-already-known"); } } // Otherwise assume this might be an orphan tx for which we just // haven't seen parents yet. if (pfMissingInputs) { *pfMissingInputs = true; } // fMissingInputs and !state.IsInvalid() is used to detect this // condition, don't set state.Invalid() return false; } } // Are the actual inputs available? if (!view.HaveInputs(tx)) { return state.Invalid(false, REJECT_DUPLICATE, "bad-txns-inputs-spent"); } // Bring the best block into scope. view.GetBestBlock(); // We have all inputs cached now, so switch back to dummy, so we don't // need to keep lock on mempool. view.SetBackend(dummy); // Only accept BIP68 sequence locked transactions that can be mined in // the next block; we don't want our mempool filled up with transactions // that can't be mined yet. Must keep pool.cs for this unless we change // CheckSequenceLocks to take a CoinsViewCache instead of create its // own. if (!CheckSequenceLocks(pool, tx, STANDARD_LOCKTIME_VERIFY_FLAGS, &lp)) { return state.DoS(0, false, REJECT_NONSTANDARD, "non-BIP68-final"); } Amount nFees = Amount::zero(); if (!Consensus::CheckTxInputs(tx, state, view, GetSpendHeight(view), nFees)) { return error("%s: Consensus::CheckTxInputs: %s, %s", __func__, tx.GetId().ToString(), FormatStateMessage(state)); } // Check for non-standard pay-to-script-hash in inputs if (fRequireStandard && !AreInputsStandard(tx, view)) { return state.Invalid(false, REJECT_NONSTANDARD, "bad-txns-nonstandard-inputs"); } int64_t nSigOpsCount = GetTransactionSigOpCount(tx, view, STANDARD_SCRIPT_VERIFY_FLAGS); // nModifiedFees includes any fee deltas from PrioritiseTransaction Amount nModifiedFees = nFees; double nPriorityDummy = 0; pool.ApplyDeltas(txid, nPriorityDummy, nModifiedFees); Amount inChainInputValue; double dPriority = view.GetPriority(tx, chainActive.Height(), inChainInputValue); // Keep track of transactions that spend a coinbase, which we re-scan // during reorgs to ensure COINBASE_MATURITY is still met. bool fSpendsCoinbase = false; for (const CTxIn &txin : tx.vin) { const Coin &coin = view.AccessCoin(txin.prevout); if (coin.IsCoinBase()) { fSpendsCoinbase = true; break; } } CTxMemPoolEntry entry(ptx, nFees, nAcceptTime, dPriority, chainActive.Height(), inChainInputValue, fSpendsCoinbase, nSigOpsCount, lp); unsigned int nSize = entry.GetTxSize(); // Check that the transaction doesn't have an excessive number of // sigops, making it impossible to mine. Since the coinbase transaction // itself can contain sigops MAX_STANDARD_TX_SIGOPS is less than // MAX_BLOCK_SIGOPS_PER_MB; we still consider this an invalid rather // than merely non-standard transaction. if (nSigOpsCount > MAX_STANDARD_TX_SIGOPS) { return state.DoS(0, false, REJECT_NONSTANDARD, "bad-txns-too-many-sigops", false, strprintf("%d", nSigOpsCount)); } Amount mempoolRejectFee = pool.GetMinFee( gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000) .GetFee(nSize); if (mempoolRejectFee > Amount::zero() && nModifiedFees < mempoolRejectFee) { return state.DoS( 0, false, REJECT_INSUFFICIENTFEE, "mempool min fee not met", false, strprintf("%d < %d", nModifiedFees, mempoolRejectFee)); } if (gArgs.GetBoolArg("-relaypriority", DEFAULT_RELAYPRIORITY) && nModifiedFees < minRelayTxFee.GetFee(nSize) && !AllowFree(entry.GetPriority(chainActive.Height() + 1))) { // Require that free transactions have sufficient priority to be // mined in the next block. return state.DoS(0, false, REJECT_INSUFFICIENTFEE, "insufficient priority"); } // Continuously rate-limit free (really, very-low-fee) transactions. // This mitigates 'penny-flooding' -- sending thousands of free // transactions just to be annoying or make others' transactions take // longer to confirm. if (fLimitFree && nModifiedFees < minRelayTxFee.GetFee(nSize)) { static CCriticalSection csFreeLimiter; static double dFreeCount; static int64_t nLastTime; int64_t nNow = GetTime(); LOCK(csFreeLimiter); // Use an exponentially decaying ~10-minute window: dFreeCount *= pow(1.0 - 1.0 / 600.0, double(nNow - nLastTime)); nLastTime = nNow; // -limitfreerelay unit is thousand-bytes-per-minute // At default rate it would take over a month to fill 1GB // NOTE: Use the actual size here, and not the fee size since this // is counting real size for the rate limiter. if (dFreeCount + nSize >= gArgs.GetArg("-limitfreerelay", DEFAULT_LIMITFREERELAY) * 10 * 1000) { return state.DoS(0, false, REJECT_INSUFFICIENTFEE, "rate limited free transaction"); } LogPrint(BCLog::MEMPOOL, "Rate limit dFreeCount: %g => %g\n", dFreeCount, dFreeCount + nSize); dFreeCount += nSize; } if (nAbsurdFee != Amount::zero() && nFees > nAbsurdFee) { return state.Invalid(false, REJECT_HIGHFEE, "absurdly-high-fee", strprintf("%d > %d", nFees, nAbsurdFee)); } // Calculate in-mempool ancestors, up to a limit. CTxMemPool::setEntries setAncestors; size_t nLimitAncestors = gArgs.GetArg("-limitancestorcount", DEFAULT_ANCESTOR_LIMIT); size_t nLimitAncestorSize = gArgs.GetArg("-limitancestorsize", DEFAULT_ANCESTOR_SIZE_LIMIT) * 1000; size_t nLimitDescendants = gArgs.GetArg("-limitdescendantcount", DEFAULT_DESCENDANT_LIMIT); size_t nLimitDescendantSize = gArgs.GetArg("-limitdescendantsize", DEFAULT_DESCENDANT_SIZE_LIMIT) * 1000; std::string errString; if (!pool.CalculateMemPoolAncestors( entry, setAncestors, nLimitAncestors, nLimitAncestorSize, nLimitDescendants, nLimitDescendantSize, errString)) { return state.DoS(0, false, REJECT_NONSTANDARD, "too-long-mempool-chain", false, errString); } // Set extraFlags as a set of flags that needs to be activated. uint32_t extraFlags = SCRIPT_VERIFY_NONE; if (IsReplayProtectionEnabledForCurrentBlock(consensusParams)) { extraFlags |= SCRIPT_ENABLE_REPLAY_PROTECTION; } if (IsMagneticAnomalyEnabledForCurrentBlock(consensusParams)) { extraFlags |= SCRIPT_VERIFY_CHECKDATASIG_SIGOPS; } if (IsGravitonEnabledForCurrentBlock(consensusParams)) { extraFlags |= SCRIPT_ENABLE_SCHNORR_MULTISIG; extraFlags |= SCRIPT_VERIFY_MINIMALDATA; } // Make sure whatever we need to activate is actually activated. const uint32_t scriptVerifyFlags = STANDARD_SCRIPT_VERIFY_FLAGS | extraFlags; // Check against previous transactions. This is done last to help // prevent CPU exhaustion denial-of-service attacks. PrecomputedTransactionData txdata(tx); if (!CheckInputs(tx, state, view, true, scriptVerifyFlags, true, false, txdata)) { // State filled in by CheckInputs. return false; } // Check again against the next block's script verification flags // to cache our script execution flags. // // This is also useful in case of bugs in the standard flags that cause // transactions to pass as valid when they're actually invalid. For // instance the STRICTENC flag was incorrectly allowing certain CHECKSIG // NOT scripts to pass, even though they were invalid. // // There is a similar check in CreateNewBlock() to prevent creating // invalid blocks (using TestBlockValidity), however allowing such // transactions into the mempool can be exploited as a DoS attack. uint32_t nextBlockScriptVerifyFlags = GetNextBlockScriptFlags(consensusParams, chainActive.Tip()); if (!CheckInputsFromMempoolAndCache(tx, state, view, pool, nextBlockScriptVerifyFlags, true, txdata)) { return error("%s: BUG! PLEASE REPORT THIS! CheckInputs failed " "against next-block but not STANDARD flags %s, %s", __func__, txid.ToString(), FormatStateMessage(state)); } if (test_accept) { // Tx was accepted, but not added return true; } // Store transaction in memory. pool.addUnchecked(txid, entry, setAncestors); // Trim mempool and check if tx was trimmed. if (!fOverrideMempoolLimit) { pool.LimitSize( gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000, gArgs.GetArg("-mempoolexpiry", DEFAULT_MEMPOOL_EXPIRY) * 60 * 60); if (!pool.exists(txid)) { return state.DoS(0, false, REJECT_INSUFFICIENTFEE, "mempool full"); } } } GetMainSignals().TransactionAddedToMempool(ptx); return true; } /** * (try to) add transaction to memory pool with a specified acceptance time. */ static bool AcceptToMemoryPoolWithTime( const Config &config, CTxMemPool &pool, CValidationState &state, const CTransactionRef &tx, bool fLimitFree, bool *pfMissingInputs, int64_t nAcceptTime, bool fOverrideMempoolLimit, const Amount nAbsurdFee, bool test_accept) EXCLUSIVE_LOCKS_REQUIRED(cs_main) { std::vector<COutPoint> coins_to_uncache; bool res = AcceptToMemoryPoolWorker( config, pool, state, tx, fLimitFree, pfMissingInputs, nAcceptTime, fOverrideMempoolLimit, nAbsurdFee, coins_to_uncache, test_accept); if (!res) { for (const COutPoint &outpoint : coins_to_uncache) { pcoinsTip->Uncache(outpoint); } } // After we've (potentially) uncached entries, ensure our coins cache is // still within its size limits CValidationState stateDummy; FlushStateToDisk(config.GetChainParams(), stateDummy, FlushStateMode::PERIODIC); return res; } bool AcceptToMemoryPool(const Config &config, CTxMemPool &pool, CValidationState &state, const CTransactionRef &tx, bool fLimitFree, bool *pfMissingInputs, bool fOverrideMempoolLimit, const Amount nAbsurdFee, bool test_accept) { return AcceptToMemoryPoolWithTime( config, pool, state, tx, fLimitFree, pfMissingInputs, GetTime(), fOverrideMempoolLimit, nAbsurdFee, test_accept); } /** * Return transaction in txOut, and if it was found inside a block, its hash is * placed in hashBlock. If blockIndex is provided, the transaction is fetched * from the corresponding block. */ bool GetTransaction(const Consensus::Params ¶ms, const TxId &txid, CTransactionRef &txOut, uint256 &hashBlock, bool fAllowSlow, CBlockIndex *blockIndex) { CBlockIndex *pindexSlow = blockIndex; LOCK(cs_main); if (!blockIndex) { CTransactionRef ptx = g_mempool.get(txid); if (ptx) { txOut = ptx; return true; } if (g_txindex) { return g_txindex->FindTx(txid, hashBlock, txOut); } // use coin database to locate block that contains transaction, and scan // it if (fAllowSlow) { const Coin &coin = AccessByTxid(*pcoinsTip, txid); if (!coin.IsSpent()) { pindexSlow = chainActive[coin.GetHeight()]; } } } if (pindexSlow) { CBlock block; if (ReadBlockFromDisk(block, pindexSlow, params)) { for (const auto &tx : block.vtx) { if (tx->GetId() == txid) { txOut = tx; hashBlock = pindexSlow->GetBlockHash(); return true; } } } } return false; } ////////////////////////////////////////////////////////////////////////////// // // CBlock and CBlockIndex // static bool WriteBlockToDisk(const CBlock &block, FlatFilePos &pos, const CMessageHeader::MessageMagic &messageStart) { // Open history file to append CAutoFile fileout(OpenBlockFile(pos), SER_DISK, CLIENT_VERSION); if (fileout.IsNull()) { return error("WriteBlockToDisk: OpenBlockFile failed"); } // Write index header unsigned int nSize = GetSerializeSize(fileout, block); - fileout << FLATDATA(messageStart) << nSize; + fileout << messageStart << nSize; // Write block long fileOutPos = ftell(fileout.Get()); if (fileOutPos < 0) { return error("WriteBlockToDisk: ftell failed"); } pos.nPos = (unsigned int)fileOutPos; fileout << block; return true; } bool ReadBlockFromDisk(CBlock &block, const FlatFilePos &pos, const Consensus::Params ¶ms) { block.SetNull(); // Open history file to read CAutoFile filein(OpenBlockFile(pos, true), SER_DISK, CLIENT_VERSION); if (filein.IsNull()) { return error("ReadBlockFromDisk: OpenBlockFile failed for %s", pos.ToString()); } // Read block try { filein >> block; } catch (const std::exception &e) { return error("%s: Deserialize or I/O error - %s at %s", __func__, e.what(), pos.ToString()); } // Check the header if (!CheckProofOfWork(block.GetHash(), block.nBits, params)) { return error("ReadBlockFromDisk: Errors in block header at %s", pos.ToString()); } return true; } bool ReadBlockFromDisk(CBlock &block, const CBlockIndex *pindex, const Consensus::Params ¶ms) { FlatFilePos blockPos; { LOCK(cs_main); blockPos = pindex->GetBlockPos(); } if (!ReadBlockFromDisk(block, blockPos, params)) { return false; } if (block.GetHash() != pindex->GetBlockHash()) { return error("ReadBlockFromDisk(CBlock&, CBlockIndex*): GetHash() " "doesn't match index for %s at %s", pindex->ToString(), pindex->GetBlockPos().ToString()); } return true; } Amount GetBlockSubsidy(int nHeight, const Consensus::Params &consensusParams) { int halvings = nHeight / consensusParams.nSubsidyHalvingInterval; // Force block reward to zero when right shift is undefined. if (halvings >= 64) { return Amount::zero(); } Amount nSubsidy = 50 * COIN; // Subsidy is cut in half every 210,000 blocks which will occur // approximately every 4 years. return ((nSubsidy / SATOSHI) >> halvings) * SATOSHI; } bool IsInitialBlockDownload() { // Once this function has returned false, it must remain false. static std::atomic<bool> latchToFalse{false}; // Optimization: pre-test latch before taking the lock. if (latchToFalse.load(std::memory_order_relaxed)) { return false; } LOCK(cs_main); if (latchToFalse.load(std::memory_order_relaxed)) { return false; } if (fImporting || fReindex) { return true; } if (chainActive.Tip() == nullptr) { return true; } if (chainActive.Tip()->nChainWork < nMinimumChainWork) { return true; } if (chainActive.Tip()->GetBlockTime() < (GetTime() - nMaxTipAge)) { return true; } LogPrintf("Leaving InitialBlockDownload (latching to false)\n"); latchToFalse.store(true, std::memory_order_relaxed); return false; } CBlockIndex const *pindexBestForkTip = nullptr; CBlockIndex const *pindexBestForkBase = nullptr; static void AlertNotify(const std::string &strMessage) { uiInterface.NotifyAlertChanged(); std::string strCmd = gArgs.GetArg("-alertnotify", ""); if (strCmd.empty()) { return; } // Alert text should be plain ascii coming from a trusted source, but to be // safe we first strip anything not in safeChars, then add single quotes // around the whole string before passing it to the shell: std::string singleQuote("'"); std::string safeStatus = SanitizeString(strMessage); safeStatus = singleQuote + safeStatus + singleQuote; boost::replace_all(strCmd, "%s", safeStatus); std::thread t(runCommand, strCmd); // thread runs free t.detach(); } static void CheckForkWarningConditions() EXCLUSIVE_LOCKS_REQUIRED(cs_main) { AssertLockHeld(cs_main); // Before we get past initial download, we cannot reliably alert about forks // (we assume we don't get stuck on a fork before finishing our initial // sync) if (IsInitialBlockDownload()) { return; } // If our best fork is no longer within 72 blocks (+/- 12 hours if no one // mines it) of our head, drop it if (pindexBestForkTip && chainActive.Height() - pindexBestForkTip->nHeight >= 72) { pindexBestForkTip = nullptr; } if (pindexBestForkTip || (pindexBestInvalid && pindexBestInvalid->nChainWork > chainActive.Tip()->nChainWork + (GetBlockProof(*chainActive.Tip()) * 6))) { if (!GetfLargeWorkForkFound() && pindexBestForkBase) { std::string warning = std::string("'Warning: Large-work fork detected, forking after " "block ") + pindexBestForkBase->phashBlock->ToString() + std::string("'"); AlertNotify(warning); } if (pindexBestForkTip && pindexBestForkBase) { LogPrintf("%s: Warning: Large fork found\n forking the " "chain at height %d (%s)\n lasting to height %d " "(%s).\nChain state database corruption likely.\n", __func__, pindexBestForkBase->nHeight, pindexBestForkBase->phashBlock->ToString(), pindexBestForkTip->nHeight, pindexBestForkTip->phashBlock->ToString()); SetfLargeWorkForkFound(true); } else { LogPrintf("%s: Warning: Found invalid chain at least ~6 blocks " "longer than our best chain.\nChain state database " "corruption likely.\n", __func__); SetfLargeWorkInvalidChainFound(true); } } else { SetfLargeWorkForkFound(false); SetfLargeWorkInvalidChainFound(false); } } static void CheckForkWarningConditionsOnNewFork(CBlockIndex *pindexNewForkTip) EXCLUSIVE_LOCKS_REQUIRED(cs_main) { AssertLockHeld(cs_main); // If we are on a fork that is sufficiently large, set a warning flag. const CBlockIndex *pfork = chainActive.FindFork(pindexNewForkTip); // We define a condition where we should warn the user about as a fork of at // least 7 blocks with a tip within 72 blocks (+/- 12 hours if no one mines // it) of ours. We use 7 blocks rather arbitrarily as it represents just // under 10% of sustained network hash rate operating on the fork, or a // chain that is entirely longer than ours and invalid (note that this // should be detected by both). We define it this way because it allows us // to only store the highest fork tip (+ base) which meets the 7-block // condition and from this always have the most-likely-to-cause-warning fork if (pfork && (!pindexBestForkTip || pindexNewForkTip->nHeight > pindexBestForkTip->nHeight) && pindexNewForkTip->nChainWork - pfork->nChainWork > (GetBlockProof(*pfork) * 7) && chainActive.Height() - pindexNewForkTip->nHeight < 72) { pindexBestForkTip = pindexNewForkTip; pindexBestForkBase = pfork; } CheckForkWarningConditions(); } void static InvalidChainFound(CBlockIndex *pindexNew) EXCLUSIVE_LOCKS_REQUIRED(cs_main) { if (!pindexBestInvalid || pindexNew->nChainWork > pindexBestInvalid->nChainWork) { pindexBestInvalid = pindexNew; } // If the invalid chain found is supposed to be finalized, we need to move // back the finalization point. if (IsBlockFinalized(pindexNew)) { pindexFinalized = pindexNew->pprev; } LogPrintf("%s: invalid block=%s height=%d log2_work=%.8g date=%s\n", __func__, pindexNew->GetBlockHash().ToString(), pindexNew->nHeight, log(pindexNew->nChainWork.getdouble()) / log(2.0), FormatISO8601DateTime(pindexNew->GetBlockTime())); CBlockIndex *tip = chainActive.Tip(); assert(tip); LogPrintf("%s: current best=%s height=%d log2_work=%.8g date=%s\n", __func__, tip->GetBlockHash().ToString(), chainActive.Height(), log(tip->nChainWork.getdouble()) / log(2.0), FormatISO8601DateTime(tip->GetBlockTime())); } void CChainState::InvalidBlockFound(CBlockIndex *pindex, const CValidationState &state) { if (!state.CorruptionPossible()) { pindex->nStatus = pindex->nStatus.withFailed(); m_failed_blocks.insert(pindex); setDirtyBlockIndex.insert(pindex); InvalidChainFound(pindex); } } void SpendCoins(CCoinsViewCache &view, const CTransaction &tx, CTxUndo &txundo, int nHeight) { // Mark inputs spent. if (tx.IsCoinBase()) { return; } txundo.vprevout.reserve(tx.vin.size()); for (const CTxIn &txin : tx.vin) { txundo.vprevout.emplace_back(); bool is_spent = view.SpendCoin(txin.prevout, &txundo.vprevout.back()); assert(is_spent); } } void UpdateCoins(CCoinsViewCache &view, const CTransaction &tx, CTxUndo &txundo, int nHeight) { SpendCoins(view, tx, txundo, nHeight); AddCoins(view, tx, nHeight); } void UpdateCoins(CCoinsViewCache &view, const CTransaction &tx, int nHeight) { // Mark inputs spent. if (!tx.IsCoinBase()) { for (const CTxIn &txin : tx.vin) { bool is_spent = view.SpendCoin(txin.prevout); assert(is_spent); } } // Add outputs. AddCoins(view, tx, nHeight); } bool CScriptCheck::operator()() { const CScript &scriptSig = ptxTo->vin[nIn].scriptSig; return VerifyScript(scriptSig, scriptPubKey, nFlags, CachingTransactionSignatureChecker(ptxTo, nIn, amount, cacheStore, txdata), &error); } int GetSpendHeight(const CCoinsViewCache &inputs) { LOCK(cs_main); CBlockIndex *pindexPrev = LookupBlockIndex(inputs.GetBestBlock()); return pindexPrev->nHeight + 1; } bool CheckInputs(const CTransaction &tx, CValidationState &state, const CCoinsViewCache &inputs, bool fScriptChecks, const uint32_t flags, bool sigCacheStore, bool scriptCacheStore, const PrecomputedTransactionData &txdata, std::vector<CScriptCheck> *pvChecks) EXCLUSIVE_LOCKS_REQUIRED(cs_main) { assert(!tx.IsCoinBase()); if (pvChecks) { pvChecks->reserve(tx.vin.size()); } // Skip script verification when connecting blocks under the assumevalid // block. Assuming the assumevalid block is valid this is safe because // block merkle hashes are still computed and checked, of course, if an // assumed valid block is invalid due to false scriptSigs this optimization // would allow an invalid chain to be accepted. if (!fScriptChecks) { return true; } // First check if script executions have been cached with the same flags. // Note that this assumes that the inputs provided are correct (ie that the // transaction hash which is in tx's prevouts properly commits to the // scriptPubKey in the inputs view of that transaction). uint256 hashCacheEntry = GetScriptCacheKey(tx, flags); if (IsKeyInScriptCache(hashCacheEntry, !scriptCacheStore)) { return true; } for (size_t i = 0; i < tx.vin.size(); i++) { const COutPoint &prevout = tx.vin[i].prevout; const Coin &coin = inputs.AccessCoin(prevout); assert(!coin.IsSpent()); // We very carefully only pass in things to CScriptCheck which are // clearly committed to by tx's hash. This provides a sanity // check that our caching is not introducing consensus failures through // additional data in, eg, the coins being spent being checked as a part // of CScriptCheck. const CScript &scriptPubKey = coin.GetTxOut().scriptPubKey; const Amount amount = coin.GetTxOut().nValue; // Verify signature CScriptCheck check(scriptPubKey, amount, tx, i, flags, sigCacheStore, txdata); if (pvChecks) { pvChecks->push_back(std::move(check)); } else if (!check()) { ScriptError scriptError = check.GetScriptError(); // Compute flags without the optional standardness flags. // This differs from MANDATORY_SCRIPT_VERIFY_FLAGS as it contains // additional upgrade flags (see AcceptToMemoryPoolWorker variable // extraFlags). uint32_t mandatoryFlags = flags & ~STANDARD_NOT_MANDATORY_VERIFY_FLAGS; if (flags != mandatoryFlags) { // Check whether the failure was caused by a non-mandatory // script verification check. If so, don't trigger DoS // protection to avoid splitting the network on the basis of // relay policy disagreements. CScriptCheck check2(scriptPubKey, amount, tx, i, mandatoryFlags, sigCacheStore, txdata); if (check2()) { return state.Invalid( false, REJECT_NONSTANDARD, strprintf("non-mandatory-script-verify-flag (%s)", ScriptErrorString(scriptError))); } // update the error message to reflect the mandatory violation. scriptError = check2.GetScriptError(); } // Before banning, we need to check whether the transaction would // be valid on the other side of the upgrade, so as to avoid // splitting the network between upgraded and non-upgraded nodes. CScriptCheck check3(scriptPubKey, amount, tx, i, mandatoryFlags ^ SCRIPT_ENABLE_SCHNORR_MULTISIG, sigCacheStore, txdata); if (check3()) { return state.Invalid( false, REJECT_INVALID, strprintf("upgrade-conditional-script-failure (%s)", ScriptErrorString(scriptError))); } // Failures of other flags indicate a transaction that is invalid in // new blocks, e.g. a invalid P2SH. We DoS ban such nodes as they // are not following the protocol. That said during an upgrade // careful thought should be taken as to the correct behavior - we // may want to continue peering with non-upgraded nodes even after // soft-fork super-majority signaling has occurred. return state.DoS( 100, false, REJECT_INVALID, strprintf("mandatory-script-verify-flag-failed (%s)", ScriptErrorString(scriptError))); } } if (scriptCacheStore && !pvChecks) { // We executed all of the provided scripts, and were told to cache the // result. Do so now. AddKeyInScriptCache(hashCacheEntry); } return true; } namespace { bool UndoWriteToDisk(const CBlockUndo &blockundo, FlatFilePos &pos, const uint256 &hashBlock, const CMessageHeader::MessageMagic &messageStart) { // Open history file to append CAutoFile fileout(OpenUndoFile(pos), SER_DISK, CLIENT_VERSION); if (fileout.IsNull()) { return error("%s: OpenUndoFile failed", __func__); } // Write index header unsigned int nSize = GetSerializeSize(fileout, blockundo); - fileout << FLATDATA(messageStart) << nSize; + fileout << messageStart << nSize; // Write undo data long fileOutPos = ftell(fileout.Get()); if (fileOutPos < 0) { return error("%s: ftell failed", __func__); } pos.nPos = (unsigned int)fileOutPos; fileout << blockundo; // calculate & write checksum CHashWriter hasher(SER_GETHASH, PROTOCOL_VERSION); hasher << hashBlock; hasher << blockundo; fileout << hasher.GetHash(); return true; } static bool UndoReadFromDisk(CBlockUndo &blockundo, const CBlockIndex *pindex) { FlatFilePos pos = pindex->GetUndoPos(); if (pos.IsNull()) { return error("%s: no undo data available", __func__); } // Open history file to read CAutoFile filein(OpenUndoFile(pos, true), SER_DISK, CLIENT_VERSION); if (filein.IsNull()) { return error("%s: OpenUndoFile failed", __func__); } // Read block uint256 hashChecksum; // We need a CHashVerifier as reserializing may lose data CHashVerifier<CAutoFile> verifier(&filein); try { verifier << pindex->pprev->GetBlockHash(); verifier >> blockundo; filein >> hashChecksum; } catch (const std::exception &e) { return error("%s: Deserialize or I/O error - %s", __func__, e.what()); } // Verify checksum if (hashChecksum != verifier.GetHash()) { return error("%s: Checksum mismatch", __func__); } return true; } /** Abort with a message */ static bool AbortNode(const std::string &strMessage, const std::string &userMessage = "") { SetMiscWarning(strMessage); LogPrintf("*** %s\n", strMessage); uiInterface.ThreadSafeMessageBox( userMessage.empty() ? _("Error: A fatal internal error occurred, see " "debug.log for details") : userMessage, "", CClientUIInterface::MSG_ERROR); StartShutdown(); return false; } static bool AbortNode(CValidationState &state, const std::string &strMessage, const std::string &userMessage = "") { AbortNode(strMessage, userMessage); return state.Error(strMessage); } } // namespace /** Restore the UTXO in a Coin at a given COutPoint. */ DisconnectResult UndoCoinSpend(const Coin &undo, CCoinsViewCache &view, const COutPoint &out) { bool fClean = true; if (view.HaveCoin(out)) { // Overwriting transaction output. fClean = false; } if (undo.GetHeight() == 0) { // Missing undo metadata (height and coinbase). Older versions included // this information only in undo records for the last spend of a // transactions' outputs. This implies that it must be present for some // other output of the same tx. const Coin &alternate = AccessByTxid(view, out.GetTxId()); if (alternate.IsSpent()) { // Adding output for transaction without known metadata return DISCONNECT_FAILED; } // This is somewhat ugly, but hopefully utility is limited. This is only // useful when working from legacy on disck data. In any case, putting // the correct information in there doesn't hurt. const_cast<Coin &>(undo) = Coin(undo.GetTxOut(), alternate.GetHeight(), alternate.IsCoinBase()); } // The potential_overwrite parameter to AddCoin is only allowed to be false // if we know for sure that the coin did not already exist in the cache. As // we have queried for that above using HaveCoin, we don't need to guess. // When fClean is false, a coin already existed and it is an overwrite. view.AddCoin(out, std::move(undo), !fClean); return fClean ? DISCONNECT_OK : DISCONNECT_UNCLEAN; } /** * Undo the effects of this block (with given index) on the UTXO set represented * by coins. When FAILED is returned, view is left in an indeterminate state. */ DisconnectResult CChainState::DisconnectBlock(const CBlock &block, const CBlockIndex *pindex, CCoinsViewCache &view) { CBlockUndo blockUndo; if (!UndoReadFromDisk(blockUndo, pindex)) { error("DisconnectBlock(): failure reading undo data"); return DISCONNECT_FAILED; } return ApplyBlockUndo(blockUndo, block, pindex, view); } DisconnectResult ApplyBlockUndo(const CBlockUndo &blockUndo, const CBlock &block, const CBlockIndex *pindex, CCoinsViewCache &view) { bool fClean = true; if (blockUndo.vtxundo.size() + 1 != block.vtx.size()) { error("DisconnectBlock(): block and undo data inconsistent"); return DISCONNECT_FAILED; } // First, restore inputs. for (size_t i = 1; i < block.vtx.size(); i++) { const CTransaction &tx = *(block.vtx[i]); const CTxUndo &txundo = blockUndo.vtxundo[i - 1]; if (txundo.vprevout.size() != tx.vin.size()) { error("DisconnectBlock(): transaction and undo data inconsistent"); return DISCONNECT_FAILED; } for (size_t j = 0; j < tx.vin.size(); j++) { const COutPoint &out = tx.vin[j].prevout; const Coin &undo = txundo.vprevout[j]; DisconnectResult res = UndoCoinSpend(undo, view, out); if (res == DISCONNECT_FAILED) { return DISCONNECT_FAILED; } fClean = fClean && res != DISCONNECT_UNCLEAN; } } // Second, revert created outputs. for (const auto &ptx : block.vtx) { const CTransaction &tx = *ptx; const TxId &txid = tx.GetId(); const bool is_coinbase = tx.IsCoinBase(); // Check that all outputs are available and match the outputs in the // block itself exactly. for (size_t o = 0; o < tx.vout.size(); o++) { if (tx.vout[o].scriptPubKey.IsUnspendable()) { continue; } COutPoint out(txid, o); Coin coin; bool is_spent = view.SpendCoin(out, &coin); if (!is_spent || tx.vout[o] != coin.GetTxOut() || uint32_t(pindex->nHeight) != coin.GetHeight() || is_coinbase != coin.IsCoinBase()) { // transaction output mismatch fClean = false; } } } // Move best block pointer to previous block. view.SetBestBlock(block.hashPrevBlock); return fClean ? DISCONNECT_OK : DISCONNECT_UNCLEAN; } static void FlushBlockFile(bool fFinalize = false) { LOCK(cs_LastBlockFile); FlatFilePos block_pos_old(nLastBlockFile, vinfoBlockFile[nLastBlockFile].nSize); FlatFilePos undo_pos_old(nLastBlockFile, vinfoBlockFile[nLastBlockFile].nUndoSize); bool status = true; status &= BlockFileSeq().Flush(block_pos_old, fFinalize); status &= UndoFileSeq().Flush(undo_pos_old, fFinalize); if (!status) { AbortNode("Flushing block file to disk failed. This is likely the " "result of an I/O error."); } } static bool FindUndoPos(CValidationState &state, int nFile, FlatFilePos &pos, unsigned int nAddSize); static bool WriteUndoDataForBlock(const CBlockUndo &blockundo, CValidationState &state, CBlockIndex *pindex, const CChainParams &chainparams) { // Write undo information to disk if (pindex->GetUndoPos().IsNull()) { FlatFilePos _pos; if (!FindUndoPos( state, pindex->nFile, _pos, ::GetSerializeSize(blockundo, SER_DISK, CLIENT_VERSION) + 40)) { return error("ConnectBlock(): FindUndoPos failed"); } if (!UndoWriteToDisk(blockundo, _pos, pindex->pprev->GetBlockHash(), chainparams.DiskMagic())) { return AbortNode(state, "Failed to write undo data"); } // update nUndoPos in block index pindex->nUndoPos = _pos.nPos; pindex->nStatus = pindex->nStatus.withUndo(); setDirtyBlockIndex.insert(pindex); } return true; } static CCheckQueue<CScriptCheck> scriptcheckqueue(128); void ThreadScriptCheck() { RenameThread("bitcoin-scriptch"); scriptcheckqueue.Thread(); } int32_t ComputeBlockVersion(const CBlockIndex *pindexPrev, const Consensus::Params ¶ms) { int32_t nVersion = VERSIONBITS_TOP_BITS; return nVersion; } // Returns the script flags which should be checked for the block after // the given block. static uint32_t GetNextBlockScriptFlags(const Consensus::Params ¶ms, const CBlockIndex *pindex) { uint32_t flags = SCRIPT_VERIFY_NONE; // Start enforcing P2SH (BIP16) if ((pindex->nHeight + 1) >= params.BIP16Height) { flags |= SCRIPT_VERIFY_P2SH; } // Start enforcing the DERSIG (BIP66) rule. if ((pindex->nHeight + 1) >= params.BIP66Height) { flags |= SCRIPT_VERIFY_DERSIG; } // Start enforcing CHECKLOCKTIMEVERIFY (BIP65) rule. if ((pindex->nHeight + 1) >= params.BIP65Height) { flags |= SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY; } // Start enforcing CSV (BIP68, BIP112 and BIP113) rule. if ((pindex->nHeight + 1) >= params.CSVHeight) { flags |= SCRIPT_VERIFY_CHECKSEQUENCEVERIFY; } // If the UAHF is enabled, we start accepting replay protected txns if (IsUAHFenabled(params, pindex)) { flags |= SCRIPT_VERIFY_STRICTENC; flags |= SCRIPT_ENABLE_SIGHASH_FORKID; } // If the DAA HF is enabled, we start rejecting transaction that use a high // s in their signature. We also make sure that signature that are supposed // to fail (for instance in multisig or other forms of smart contracts) are // null. if (IsDAAEnabled(params, pindex)) { flags |= SCRIPT_VERIFY_LOW_S; flags |= SCRIPT_VERIFY_NULLFAIL; } // When the magnetic anomaly fork is enabled, we start accepting // transactions using the OP_CHECKDATASIG opcode and it's verify // alternative. We also start enforcing push only signatures and // clean stack. if (IsMagneticAnomalyEnabled(params, pindex)) { flags |= SCRIPT_VERIFY_CHECKDATASIG_SIGOPS; flags |= SCRIPT_VERIFY_SIGPUSHONLY; flags |= SCRIPT_VERIFY_CLEANSTACK; } if (IsGravitonEnabled(params, pindex)) { flags |= SCRIPT_ENABLE_SCHNORR_MULTISIG; flags |= SCRIPT_VERIFY_MINIMALDATA; } // We make sure this node will have replay protection during the next hard // fork. if (IsReplayProtectionEnabled(params, pindex)) { flags |= SCRIPT_ENABLE_REPLAY_PROTECTION; } return flags; } static int64_t nTimeCheck = 0; static int64_t nTimeForks = 0; static int64_t nTimeVerify = 0; static int64_t nTimeConnect = 0; static int64_t nTimeIndex = 0; static int64_t nTimeCallbacks = 0; static int64_t nTimeTotal = 0; static int64_t nBlocksTotal = 0; /** * Apply the effects of this block (with given index) on the UTXO set * represented by coins. Validity checks that depend on the UTXO set are also * done; ConnectBlock() can fail if those validity checks fail (among other * reasons). */ bool CChainState::ConnectBlock(const CBlock &block, CValidationState &state, CBlockIndex *pindex, CCoinsViewCache &view, const CChainParams ¶ms, BlockValidationOptions options, bool fJustCheck) EXCLUSIVE_LOCKS_REQUIRED(cs_main) { AssertLockHeld(cs_main); assert(pindex); assert(*pindex->phashBlock == block.GetHash()); int64_t nTimeStart = GetTimeMicros(); const Consensus::Params &consensusParams = params.GetConsensus(); // Check it again in case a previous version let a bad block in // NOTE: We don't currently (re-)invoke ContextualCheckBlock() or // ContextualCheckBlockHeader() here. This means that if we add a new // consensus rule that is enforced in one of those two functions, then we // may have let in a block that violates the rule prior to updating the // software, and we would NOT be enforcing the rule here. Fully solving // upgrade from one software version to the next after a consensus rule // change is potentially tricky and issue-specific (see RewindBlockIndex() // for one general approach that was used for BIP 141 deployment). // Also, currently the rule against blocks more than 2 hours in the future // is enforced in ContextualCheckBlockHeader(); we wouldn't want to // re-enforce that rule here (at least until we make it impossible for // GetAdjustedTime() to go backward). if (!CheckBlock(block, state, consensusParams, options.withCheckPoW(!fJustCheck) .withCheckMerkleRoot(!fJustCheck))) { if (state.CorruptionPossible()) { // We don't write down blocks to disk if they may have been // corrupted, so this should be impossible unless we're having // hardware problems. return AbortNode(state, "Corrupt block found indicating potential " "hardware failure; shutting down"); } return error("%s: Consensus::CheckBlock: %s", __func__, FormatStateMessage(state)); } // Verify that the view's current state corresponds to the previous block uint256 hashPrevBlock = pindex->pprev == nullptr ? uint256() : pindex->pprev->GetBlockHash(); assert(hashPrevBlock == view.GetBestBlock()); // Special case for the genesis block, skipping connection of its // transactions (its coinbase is unspendable) if (block.GetHash() == consensusParams.hashGenesisBlock) { if (!fJustCheck) { view.SetBestBlock(pindex->GetBlockHash()); } return true; } nBlocksTotal++; bool fScriptChecks = true; if (!hashAssumeValid.IsNull()) { // We've been configured with the hash of a block which has been // externally verified to have a valid history. A suitable default value // is included with the software and updated from time to time. Because // validity relative to a piece of software is an objective fact these // defaults can be easily reviewed. This setting doesn't force the // selection of any particular chain but makes validating some faster by // effectively caching the result of part of the verification. BlockMap::const_iterator it = mapBlockIndex.find(hashAssumeValid); if (it != mapBlockIndex.end()) { if (it->second->GetAncestor(pindex->nHeight) == pindex && pindexBestHeader->GetAncestor(pindex->nHeight) == pindex && pindexBestHeader->nChainWork >= nMinimumChainWork) { // This block is a member of the assumed verified chain and an // ancestor of the best header. The equivalent time check // discourages hash power from extorting the network via DOS // attack into accepting an invalid block through telling users // they must manually set assumevalid. Requiring a software // change or burying the invalid block, regardless of the // setting, makes it hard to hide the implication of the demand. // This also avoids having release candidates that are hardly // doing any signature verification at all in testing without // having to artificially set the default assumed verified block // further back. The test against nMinimumChainWork prevents the // skipping when denied access to any chain at least as good as // the expected chain. fScriptChecks = (GetBlockProofEquivalentTime( *pindexBestHeader, *pindex, *pindexBestHeader, consensusParams) <= 60 * 60 * 24 * 7 * 2); } } } int64_t nTime1 = GetTimeMicros(); nTimeCheck += nTime1 - nTimeStart; LogPrint(BCLog::BENCH, " - Sanity checks: %.2fms [%.2fs (%.2fms/blk)]\n", MILLI * (nTime1 - nTimeStart), nTimeCheck * MICRO, nTimeCheck * MILLI / nBlocksTotal); // Do not allow blocks that contain transactions which 'overwrite' older // transactions, unless those are already completely spent. If such // overwrites are allowed, coinbases and transactions depending upon those // can be duplicated to remove the ability to spend the first instance -- // even after being sent to another address. See BIP30 and // http://r6.ca/blog/20120206T005236Z.html for more information. This logic // is not necessary for memory pool transactions, as AcceptToMemoryPool // already refuses previously-known transaction ids entirely. This rule was // originally applied to all blocks with a timestamp after March 15, 2012, // 0:00 UTC. Now that the whole chain is irreversibly beyond that time it is // applied to all blocks except the two in the chain that violate it. This // prevents exploiting the issue against nodes during their initial block // download. bool fEnforceBIP30 = !((pindex->nHeight == 91842 && pindex->GetBlockHash() == uint256S("0x00000000000a4d0a398161ffc163c503763" "b1f4360639393e0e4c8e300e0caec")) || (pindex->nHeight == 91880 && pindex->GetBlockHash() == uint256S("0x00000000000743f190a18c5577a3c2d2a1f" "610ae9601ac046a38084ccb7cd721"))); // Once BIP34 activated it was not possible to create new duplicate // coinbases and thus other than starting with the 2 existing duplicate // coinbase pairs, not possible to create overwriting txs. But by the time // BIP34 activated, in each of the existing pairs the duplicate coinbase had // overwritten the first before the first had been spent. Since those // coinbases are sufficiently buried it's no longer possible to create // further duplicate transactions descending from the known pairs either. If // we're on the known chain at height greater than where BIP34 activated, we // can save the db accesses needed for the BIP30 check. assert(pindex->pprev); CBlockIndex *pindexBIP34height = pindex->pprev->GetAncestor(consensusParams.BIP34Height); // Only continue to enforce if we're below BIP34 activation height or the // block hash at that height doesn't correspond. fEnforceBIP30 = fEnforceBIP30 && (!pindexBIP34height || !(pindexBIP34height->GetBlockHash() == consensusParams.BIP34Hash)); if (fEnforceBIP30) { for (const auto &tx : block.vtx) { for (size_t o = 0; o < tx->vout.size(); o++) { if (view.HaveCoin(COutPoint(tx->GetId(), o))) { return state.DoS( 100, error("ConnectBlock(): tried to overwrite transaction"), REJECT_INVALID, "bad-txns-BIP30"); } } } } // Start enforcing BIP68 (sequence locks). int nLockTimeFlags = 0; if (pindex->nHeight >= consensusParams.CSVHeight) { nLockTimeFlags |= LOCKTIME_VERIFY_SEQUENCE; } const uint32_t flags = GetNextBlockScriptFlags(consensusParams, pindex->pprev); int64_t nTime2 = GetTimeMicros(); nTimeForks += nTime2 - nTime1; LogPrint(BCLog::BENCH, " - Fork checks: %.2fms [%.2fs (%.2fms/blk)]\n", MILLI * (nTime2 - nTime1), nTimeForks * MICRO, nTimeForks * MILLI / nBlocksTotal); CBlockUndo blockundo; CCheckQueueControl<CScriptCheck> control(fScriptChecks ? &scriptcheckqueue : nullptr); std::vector<int> prevheights; Amount nFees = Amount::zero(); int nInputs = 0; // Sigops counting. We need to do it again because of P2SH. uint64_t nSigOpsCount = 0; const uint64_t currentBlockSize = ::GetSerializeSize(block, SER_NETWORK, PROTOCOL_VERSION); const uint64_t nMaxSigOpsCount = GetMaxBlockSigOpsCount(currentBlockSize); blockundo.vtxundo.reserve(block.vtx.size() - 1); for (const auto &ptx : block.vtx) { const CTransaction &tx = *ptx; nInputs += tx.vin.size(); if (tx.IsCoinBase()) { // We've already checked for sigops count before P2SH in CheckBlock. nSigOpsCount += GetSigOpCountWithoutP2SH(tx, flags); } // We do not need to throw when a transaction is duplicated. If they are // in the same block, CheckBlock will catch it, and if they are in a // different block, it'll register as a double spend or BIP30 violation. // In both cases, we get a more meaningful feedback out of it. AddCoins(view, tx, pindex->nHeight, true); } for (const auto &ptx : block.vtx) { const CTransaction &tx = *ptx; if (tx.IsCoinBase()) { continue; } Amount txfee = Amount::zero(); if (!Consensus::CheckTxInputs(tx, state, view, pindex->nHeight, txfee)) { return error("%s: Consensus::CheckTxInputs: %s, %s", __func__, tx.GetId().ToString(), FormatStateMessage(state)); } nFees += txfee; if (!MoneyRange(nFees)) { return state.DoS( 100, error("%s: accumulated fee in the block out of range.", __func__), REJECT_INVALID, "bad-txns-accumulated-fee-outofrange"); } // Check that transaction is BIP68 final BIP68 lock checks (as // opposed to nLockTime checks) must be in ConnectBlock because they // require the UTXO set. prevheights.resize(tx.vin.size()); for (size_t j = 0; j < tx.vin.size(); j++) { prevheights[j] = view.AccessCoin(tx.vin[j].prevout).GetHeight(); } if (!SequenceLocks(tx, nLockTimeFlags, &prevheights, *pindex)) { return state.DoS( 100, error("%s: contains a non-BIP68-final transaction", __func__), REJECT_INVALID, "bad-txns-nonfinal"); } // GetTransactionSigOpCount counts 2 types of sigops: // * legacy (always) // * p2sh (when P2SH enabled in flags and excludes coinbase) auto txSigOpsCount = GetTransactionSigOpCount(tx, view, flags); if (txSigOpsCount > MAX_TX_SIGOPS_COUNT) { return state.DoS(100, false, REJECT_INVALID, "bad-txn-sigops"); } nSigOpsCount += txSigOpsCount; if (nSigOpsCount > nMaxSigOpsCount) { return state.DoS(100, error("ConnectBlock(): too many sigops"), REJECT_INVALID, "bad-blk-sigops"); } // Don't cache results if we're actually connecting blocks (still // consult the cache, though). bool fCacheResults = fJustCheck; std::vector<CScriptCheck> vChecks; if (!CheckInputs(tx, state, view, fScriptChecks, flags, fCacheResults, fCacheResults, PrecomputedTransactionData(tx), &vChecks)) { return error("ConnectBlock(): CheckInputs on %s failed with %s", tx.GetId().ToString(), FormatStateMessage(state)); } control.Add(vChecks); blockundo.vtxundo.push_back(CTxUndo()); SpendCoins(view, tx, blockundo.vtxundo.back(), pindex->nHeight); } int64_t nTime3 = GetTimeMicros(); nTimeConnect += nTime3 - nTime2; LogPrint(BCLog::BENCH, " - Connect %u transactions: %.2fms (%.3fms/tx, %.3fms/txin) " "[%.2fs (%.2fms/blk)]\n", (unsigned)block.vtx.size(), MILLI * (nTime3 - nTime2), MILLI * (nTime3 - nTime2) / block.vtx.size(), nInputs <= 1 ? 0 : MILLI * (nTime3 - nTime2) / (nInputs - 1), nTimeConnect * MICRO, nTimeConnect * MILLI / nBlocksTotal); Amount blockReward = nFees + GetBlockSubsidy(pindex->nHeight, consensusParams); if (block.vtx[0]->GetValueOut() > blockReward) { return state.DoS(100, error("ConnectBlock(): coinbase pays too much " "(actual=%d vs limit=%d)", block.vtx[0]->GetValueOut(), blockReward), REJECT_INVALID, "bad-cb-amount"); } if (!control.Wait()) { return state.DoS(100, false, REJECT_INVALID, "blk-bad-inputs", false, "parallel script check failed"); } int64_t nTime4 = GetTimeMicros(); nTimeVerify += nTime4 - nTime2; LogPrint( BCLog::BENCH, " - Verify %u txins: %.2fms (%.3fms/txin) [%.2fs (%.2fms/blk)]\n", nInputs - 1, MILLI * (nTime4 - nTime2), nInputs <= 1 ? 0 : MILLI * (nTime4 - nTime2) / (nInputs - 1), nTimeVerify * MICRO, nTimeVerify * MILLI / nBlocksTotal); if (fJustCheck) { return true; } if (!WriteUndoDataForBlock(blockundo, state, pindex, params)) { return false; } if (!pindex->IsValid(BlockValidity::SCRIPTS)) { pindex->RaiseValidity(BlockValidity::SCRIPTS); setDirtyBlockIndex.insert(pindex); } assert(pindex->phashBlock); // add this block to the view's block chain view.SetBestBlock(pindex->GetBlockHash()); int64_t nTime5 = GetTimeMicros(); nTimeIndex += nTime5 - nTime4; LogPrint(BCLog::BENCH, " - Index writing: %.2fms [%.2fs (%.2fms/blk)]\n", MILLI * (nTime5 - nTime4), nTimeIndex * MICRO, nTimeIndex * MILLI / nBlocksTotal); int64_t nTime6 = GetTimeMicros(); nTimeCallbacks += nTime6 - nTime5; LogPrint(BCLog::BENCH, " - Callbacks: %.2fms [%.2fs (%.2fms/blk)]\n", MILLI * (nTime6 - nTime5), nTimeCallbacks * MICRO, nTimeCallbacks * MILLI / nBlocksTotal); return true; } /** * Update the on-disk chain state. * The caches and indexes are flushed depending on the mode we're called with if * they're too large, if it's been a while since the last write, or always and * in all cases if we're in prune mode and are deleting files. * * If FlushStateMode::NONE is used, then FlushStateToDisk(...) won't do anything * besides checking if we need to prune. */ static bool FlushStateToDisk(const CChainParams &chainparams, CValidationState &state, FlushStateMode mode, int nManualPruneHeight) { int64_t nMempoolUsage = g_mempool.DynamicMemoryUsage(); LOCK(cs_main); static int64_t nLastWrite = 0; static int64_t nLastFlush = 0; std::set<int> setFilesToPrune; bool full_flush_completed = false; try { { bool fFlushForPrune = false; bool fDoFullFlush = false; LOCK(cs_LastBlockFile); if (fPruneMode && (fCheckForPruning || nManualPruneHeight > 0) && !fReindex) { if (nManualPruneHeight > 0) { FindFilesToPruneManual(setFilesToPrune, nManualPruneHeight); } else { FindFilesToPrune(setFilesToPrune, chainparams.PruneAfterHeight()); fCheckForPruning = false; } if (!setFilesToPrune.empty()) { fFlushForPrune = true; if (!fHavePruned) { pblocktree->WriteFlag("prunedblockfiles", true); fHavePruned = true; } } } int64_t nNow = GetTimeMicros(); // Avoid writing/flushing immediately after startup. if (nLastWrite == 0) { nLastWrite = nNow; } if (nLastFlush == 0) { nLastFlush = nNow; } int64_t nMempoolSizeMax = gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000; int64_t cacheSize = pcoinsTip->DynamicMemoryUsage(); int64_t nTotalSpace = nCoinCacheUsage + std::max<int64_t>(nMempoolSizeMax - nMempoolUsage, 0); // The cache is large and we're within 10% and 10 MiB of the limit, // but we have time now (not in the middle of a block processing). bool fCacheLarge = mode == FlushStateMode::PERIODIC && cacheSize > std::max((9 * nTotalSpace) / 10, nTotalSpace - MAX_BLOCK_COINSDB_USAGE * 1024 * 1024); // The cache is over the limit, we have to write now. bool fCacheCritical = mode == FlushStateMode::IF_NEEDED && cacheSize > nTotalSpace; // It's been a while since we wrote the block index to disk. Do this // frequently, so we don't need to redownload after a crash. bool fPeriodicWrite = mode == FlushStateMode::PERIODIC && nNow > nLastWrite + (int64_t)DATABASE_WRITE_INTERVAL * 1000000; // It's been very long since we flushed the cache. Do this // infrequently, to optimize cache usage. bool fPeriodicFlush = mode == FlushStateMode::PERIODIC && nNow > nLastFlush + (int64_t)DATABASE_FLUSH_INTERVAL * 1000000; // Combine all conditions that result in a full cache flush. fDoFullFlush = (mode == FlushStateMode::ALWAYS) || fCacheLarge || fCacheCritical || fPeriodicFlush || fFlushForPrune; // Write blocks and block index to disk. if (fDoFullFlush || fPeriodicWrite) { // Depend on nMinDiskSpace to ensure we can write block index if (!CheckDiskSpace(GetBlocksDir())) { return AbortNode(state, "Disk space is low!", _("Error: Disk space is low!")); } // First make sure all block and undo data is flushed to disk. FlushBlockFile(); // Then update all block file information (which may refer to // block and undo files). { std::vector<std::pair<int, const CBlockFileInfo *>> vFiles; vFiles.reserve(setDirtyFileInfo.size()); for (int i : setDirtyFileInfo) { vFiles.push_back(std::make_pair(i, &vinfoBlockFile[i])); } setDirtyFileInfo.clear(); std::vector<const CBlockIndex *> vBlocks; vBlocks.reserve(setDirtyBlockIndex.size()); for (const CBlockIndex *cbi : setDirtyBlockIndex) { vBlocks.push_back(cbi); } setDirtyBlockIndex.clear(); if (!pblocktree->WriteBatchSync(vFiles, nLastBlockFile, vBlocks)) { return AbortNode( state, "Failed to write to block index database"); } } // Finally remove any pruned files if (fFlushForPrune) { UnlinkPrunedFiles(setFilesToPrune); } nLastWrite = nNow; } // Flush best chain related state. This can only be done if the // blocks / block index write was also done. if (fDoFullFlush && !pcoinsTip->GetBestBlock().IsNull()) { // Typical Coin structures on disk are around 48 bytes in size. // Pushing a new one to the database can cause it to be written // twice (once in the log, and once in the tables). This is // already an overestimation, as most will delete an existing // entry or overwrite one. Still, use a conservative safety // factor of 2. if (!CheckDiskSpace(GetDataDir(), 48 * 2 * 2 * pcoinsTip->GetCacheSize())) { return AbortNode(state, "Disk space is low!", _("Error: Disk space is low!")); } // Flush the chainstate (which may refer to block index // entries). if (!pcoinsTip->Flush()) { return AbortNode(state, "Failed to write to coin database"); } nLastFlush = nNow; full_flush_completed = true; } } if (full_flush_completed) { // Update best block in wallet (so we can detect restored wallets). GetMainSignals().ChainStateFlushed(chainActive.GetLocator()); } } catch (const std::runtime_error &e) { return AbortNode(state, std::string("System error while flushing: ") + e.what()); } return true; } void FlushStateToDisk() { CValidationState state; const CChainParams &chainparams = Params(); if (!FlushStateToDisk(chainparams, state, FlushStateMode::ALWAYS)) { LogPrintf("%s: failed to flush state (%s)\n", __func__, FormatStateMessage(state)); } } void PruneAndFlush() { CValidationState state; fCheckForPruning = true; const CChainParams &chainparams = Params(); if (!FlushStateToDisk(chainparams, state, FlushStateMode::NONE)) { LogPrintf("%s: failed to flush state (%s)\n", __func__, FormatStateMessage(state)); } } /** Check warning conditions and do some notifications on new chain tip set. */ static void UpdateTip(const Config &config, CBlockIndex *pindexNew) { // New best block g_mempool.AddTransactionsUpdated(1); { LOCK(g_best_block_mutex); g_best_block = pindexNew->GetBlockHash(); g_best_block_cv.notify_all(); } LogPrintf( "%s: new best=%s height=%d version=0x%08x log2_work=%.8g tx=%lu " "date='%s' progress=%f cache=%.1fMiB(%utxo)\n", __func__, pindexNew->GetBlockHash().ToString(), pindexNew->nHeight, pindexNew->nVersion, log(pindexNew->nChainWork.getdouble()) / log(2.0), (unsigned long)pindexNew->nChainTx, FormatISO8601DateTime(pindexNew->GetBlockTime()), GuessVerificationProgress(config.GetChainParams().TxData(), pindexNew), pcoinsTip->DynamicMemoryUsage() * (1.0 / (1 << 20)), pcoinsTip->GetCacheSize()); } /** * Disconnect chainActive's tip. * After calling, the mempool will be in an inconsistent state, with * transactions from disconnected blocks being added to disconnectpool. You * should make the mempool consistent again by calling updateMempoolForReorg. * with cs_main held. * * If disconnectpool is nullptr, then no disconnected transactions are added to * disconnectpool (note that the caller is responsible for mempool consistency * in any case). */ bool CChainState::DisconnectTip(const Config &config, CValidationState &state, DisconnectedBlockTransactions *disconnectpool) EXCLUSIVE_LOCKS_REQUIRED(cs_main) { CBlockIndex *pindexDelete = chainActive.Tip(); const Consensus::Params &consensusParams = config.GetChainParams().GetConsensus(); assert(pindexDelete); // Read block from disk. std::shared_ptr<CBlock> pblock = std::make_shared<CBlock>(); CBlock &block = *pblock; if (!ReadBlockFromDisk(block, pindexDelete, consensusParams)) { return AbortNode(state, "Failed to read block"); } // Apply the block atomically to the chain state. int64_t nStart = GetTimeMicros(); { CCoinsViewCache view(pcoinsTip.get()); assert(view.GetBestBlock() == pindexDelete->GetBlockHash()); if (DisconnectBlock(block, pindexDelete, view) != DISCONNECT_OK) { return error("DisconnectTip(): DisconnectBlock %s failed", pindexDelete->GetBlockHash().ToString()); } bool flushed = view.Flush(); assert(flushed); } LogPrint(BCLog::BENCH, "- Disconnect block: %.2fms\n", (GetTimeMicros() - nStart) * MILLI); // Write the chain state to disk, if necessary. if (!FlushStateToDisk(config.GetChainParams(), state, FlushStateMode::IF_NEEDED)) { return false; } // If this block is deactivating a fork, we move all mempool transactions // in front of disconnectpool for reprocessing in a future // updateMempoolForReorg call if (pindexDelete->pprev != nullptr && GetNextBlockScriptFlags(consensusParams, pindexDelete) != GetNextBlockScriptFlags(consensusParams, pindexDelete->pprev)) { LogPrint(BCLog::MEMPOOL, "Disconnecting mempool due to rewind of upgrade block\n"); if (disconnectpool) { disconnectpool->importMempool(g_mempool); } g_mempool.clear(); } if (disconnectpool) { disconnectpool->addForBlock(block.vtx); } // If the tip is finalized, then undo it. if (pindexFinalized == pindexDelete) { pindexFinalized = pindexDelete->pprev; } chainActive.SetTip(pindexDelete->pprev); // Update chainActive and related variables. UpdateTip(config, pindexDelete->pprev); // Let wallets know transactions went from 1-confirmed to // 0-confirmed or conflicted: GetMainSignals().BlockDisconnected(pblock); return true; } static int64_t nTimeReadFromDisk = 0; static int64_t nTimeConnectTotal = 0; static int64_t nTimeFlush = 0; static int64_t nTimeChainState = 0; static int64_t nTimePostConnect = 0; struct PerBlockConnectTrace { CBlockIndex *pindex = nullptr; std::shared_ptr<const CBlock> pblock; std::shared_ptr<std::vector<CTransactionRef>> conflictedTxs; PerBlockConnectTrace() : conflictedTxs(std::make_shared<std::vector<CTransactionRef>>()) {} }; /** * Used to track blocks whose transactions were applied to the UTXO state as a * part of a single ActivateBestChainStep call. * * This class also tracks transactions that are removed from the mempool as * conflicts (per block) and can be used to pass all those transactions through * SyncTransaction. * * This class assumes (and asserts) that the conflicted transactions for a given * block are added via mempool callbacks prior to the BlockConnected() * associated with those transactions. If any transactions are marked * conflicted, it is assumed that an associated block will always be added. * * This class is single-use, once you call GetBlocksConnected() you have to * throw it away and make a new one. */ class ConnectTrace { private: std::vector<PerBlockConnectTrace> blocksConnected; CTxMemPool &pool; boost::signals2::scoped_connection m_connNotifyEntryRemoved; public: explicit ConnectTrace(CTxMemPool &_pool) : blocksConnected(1), pool(_pool) { m_connNotifyEntryRemoved = pool.NotifyEntryRemoved.connect( std::bind(&ConnectTrace::NotifyEntryRemoved, this, std::placeholders::_1, std::placeholders::_2)); } void BlockConnected(CBlockIndex *pindex, std::shared_ptr<const CBlock> pblock) { assert(!blocksConnected.back().pindex); assert(pindex); assert(pblock); blocksConnected.back().pindex = pindex; blocksConnected.back().pblock = std::move(pblock); blocksConnected.emplace_back(); } std::vector<PerBlockConnectTrace> &GetBlocksConnected() { // We always keep one extra block at the end of our list because blocks // are added after all the conflicted transactions have been filled in. // Thus, the last entry should always be an empty one waiting for the // transactions from the next block. We pop the last entry here to make // sure the list we return is sane. assert(!blocksConnected.back().pindex); assert(blocksConnected.back().conflictedTxs->empty()); blocksConnected.pop_back(); return blocksConnected; } void NotifyEntryRemoved(CTransactionRef txRemoved, MemPoolRemovalReason reason) { assert(!blocksConnected.back().pindex); if (reason == MemPoolRemovalReason::CONFLICT) { blocksConnected.back().conflictedTxs->emplace_back( std::move(txRemoved)); } } }; static bool FinalizeBlockInternal(const Config &config, CValidationState &state, const CBlockIndex *pindex) { AssertLockHeld(cs_main); if (pindex->nStatus.isInvalid()) { // We try to finalize an invalid block. return state.DoS(100, error("%s: Trying to finalize invalid block %s", __func__, pindex->GetBlockHash().ToString()), REJECT_INVALID, "finalize-invalid-block"); } // Check that the request is consistent with current finalization. if (pindexFinalized && !AreOnTheSameFork(pindex, pindexFinalized)) { return state.DoS( 20, error("%s: Trying to finalize block %s which conflicts " "with already finalized block", __func__, pindex->GetBlockHash().ToString()), REJECT_AGAINST_FINALIZED, "bad-fork-prior-finalized"); } if (IsBlockFinalized(pindex)) { // The block is already finalized. return true; } // We have a new block to finalize. pindexFinalized = pindex; return true; } static const CBlockIndex *FindBlockToFinalize(const Config &config, CBlockIndex *pindexNew) { AssertLockHeld(cs_main); const int32_t maxreorgdepth = gArgs.GetArg("-maxreorgdepth", DEFAULT_MAX_REORG_DEPTH); const int64_t finalizationdelay = gArgs.GetArg("-finalizationdelay", DEFAULT_MIN_FINALIZATION_DELAY); // Find our candidate. // If maxreorgdepth is < 0 pindex will be null and auto finalization // disabled const CBlockIndex *pindex = pindexNew->GetAncestor(pindexNew->nHeight - maxreorgdepth); int64_t now = GetTime(); // If the finalization delay is not expired since the startup time, // finalization should be avoided. Header receive time is not saved to disk // and so cannot be anterior to startup time. if (now < (GetStartupTime() + finalizationdelay)) { return nullptr; } // While our candidate is not eligible (finalization delay not expired), try // the previous one. while (pindex && (pindex != pindexFinalized)) { // Check that the block to finalize is known for a long enough time. // This test will ensure that an attacker could not cause a block to // finalize by forking the chain with a depth > maxreorgdepth. // If the block is loaded from disk, header receive time is 0 and the // block will be finalized. This is safe because the delay since the // node startup is already expired. auto headerReceivedTime = pindex->GetHeaderReceivedTime(); // If finalization delay is <= 0, finalization always occurs immediately if (now >= (headerReceivedTime + finalizationdelay)) { return pindex; } pindex = pindex->pprev; } return nullptr; } /** * Connect a new block to chainActive. pblock is either nullptr or a pointer to * a CBlock corresponding to pindexNew, to bypass loading it again from disk. * * The block is always added to connectTrace (either after loading from disk or * by copying pblock) - if that is not intended, care must be taken to remove * the last entry in blocksConnected in case of failure. */ bool CChainState::ConnectTip(const Config &config, CValidationState &state, CBlockIndex *pindexNew, const std::shared_ptr<const CBlock> &pblock, ConnectTrace &connectTrace, DisconnectedBlockTransactions &disconnectpool) { AssertLockHeld(cs_main); const CChainParams ¶ms = config.GetChainParams(); const Consensus::Params &consensusParams = params.GetConsensus(); assert(pindexNew->pprev == chainActive.Tip()); // Read block from disk. int64_t nTime1 = GetTimeMicros(); std::shared_ptr<const CBlock> pthisBlock; if (!pblock) { std::shared_ptr<CBlock> pblockNew = std::make_shared<CBlock>(); if (!ReadBlockFromDisk(*pblockNew, pindexNew, consensusParams)) { return AbortNode(state, "Failed to read block"); } pthisBlock = pblockNew; } else { pthisBlock = pblock; } const CBlock &blockConnecting = *pthisBlock; // Apply the block atomically to the chain state. int64_t nTime2 = GetTimeMicros(); nTimeReadFromDisk += nTime2 - nTime1; int64_t nTime3; LogPrint(BCLog::BENCH, " - Load block from disk: %.2fms [%.2fs]\n", (nTime2 - nTime1) * MILLI, nTimeReadFromDisk * MICRO); { CCoinsViewCache view(pcoinsTip.get()); bool rv = ConnectBlock(blockConnecting, state, pindexNew, view, params, BlockValidationOptions(config)); GetMainSignals().BlockChecked(blockConnecting, state); if (!rv) { if (state.IsInvalid()) { InvalidBlockFound(pindexNew, state); } return error("ConnectTip(): ConnectBlock %s failed (%s)", pindexNew->GetBlockHash().ToString(), FormatStateMessage(state)); } // Update the finalized block. const CBlockIndex *pindexToFinalize = FindBlockToFinalize(config, pindexNew); if (pindexToFinalize && !FinalizeBlockInternal(config, state, pindexToFinalize)) { state.SetCorruptionPossible(); return error("ConnectTip(): FinalizeBlock %s failed (%s)", pindexNew->GetBlockHash().ToString(), FormatStateMessage(state)); } nTime3 = GetTimeMicros(); nTimeConnectTotal += nTime3 - nTime2; LogPrint(BCLog::BENCH, " - Connect total: %.2fms [%.2fs (%.2fms/blk)]\n", (nTime3 - nTime2) * MILLI, nTimeConnectTotal * MICRO, nTimeConnectTotal * MILLI / nBlocksTotal); bool flushed = view.Flush(); assert(flushed); } int64_t nTime4 = GetTimeMicros(); nTimeFlush += nTime4 - nTime3; LogPrint(BCLog::BENCH, " - Flush: %.2fms [%.2fs (%.2fms/blk)]\n", (nTime4 - nTime3) * MILLI, nTimeFlush * MICRO, nTimeFlush * MILLI / nBlocksTotal); // Write the chain state to disk, if necessary. if (!FlushStateToDisk(config.GetChainParams(), state, FlushStateMode::IF_NEEDED)) { return false; } int64_t nTime5 = GetTimeMicros(); nTimeChainState += nTime5 - nTime4; LogPrint(BCLog::BENCH, " - Writing chainstate: %.2fms [%.2fs (%.2fms/blk)]\n", (nTime5 - nTime4) * MILLI, nTimeChainState * MICRO, nTimeChainState * MILLI / nBlocksTotal); // Remove conflicting transactions from the mempool.; g_mempool.removeForBlock(blockConnecting.vtx, pindexNew->nHeight); disconnectpool.removeForBlock(blockConnecting.vtx); // If this block is activating a fork, we move all mempool transactions // in front of disconnectpool for reprocessing in a future // updateMempoolForReorg call if (pindexNew->pprev != nullptr && GetNextBlockScriptFlags(consensusParams, pindexNew) != GetNextBlockScriptFlags(consensusParams, pindexNew->pprev)) { LogPrint(BCLog::MEMPOOL, "Disconnecting mempool due to acceptance of upgrade block\n"); disconnectpool.importMempool(g_mempool); } // Update chainActive & related variables. chainActive.SetTip(pindexNew); UpdateTip(config, pindexNew); int64_t nTime6 = GetTimeMicros(); nTimePostConnect += nTime6 - nTime5; nTimeTotal += nTime6 - nTime1; LogPrint(BCLog::BENCH, " - Connect postprocess: %.2fms [%.2fs (%.2fms/blk)]\n", (nTime6 - nTime5) * MILLI, nTimePostConnect * MICRO, nTimePostConnect * MILLI / nBlocksTotal); LogPrint(BCLog::BENCH, "- Connect block: %.2fms [%.2fs (%.2fms/blk)]\n", (nTime6 - nTime1) * MILLI, nTimeTotal * MICRO, nTimeTotal * MILLI / nBlocksTotal); connectTrace.BlockConnected(pindexNew, std::move(pthisBlock)); return true; } /** * Return the tip of the chain with the most work in it, that isn't known to be * invalid (it's however far from certain to be valid). */ CBlockIndex *CChainState::FindMostWorkChain() { AssertLockHeld(cs_main); do { CBlockIndex *pindexNew = nullptr; // Find the best candidate header. { std::set<CBlockIndex *, CBlockIndexWorkComparator>::reverse_iterator it = setBlockIndexCandidates.rbegin(); if (it == setBlockIndexCandidates.rend()) { return nullptr; } pindexNew = *it; } // If this block will cause a finalized block to be reorged, then we // mark it as invalid. if (pindexFinalized && !AreOnTheSameFork(pindexNew, pindexFinalized)) { LogPrintf("Mark block %s invalid because it forks prior to the " "finalization point %d.\n", pindexNew->GetBlockHash().ToString(), pindexFinalized->nHeight); pindexNew->nStatus = pindexNew->nStatus.withFailed(); InvalidChainFound(pindexNew); } const CBlockIndex *pindexFork = chainActive.FindFork(pindexNew); // Check whether all blocks on the path between the currently active // chain and the candidate are valid. Just going until the active chain // is an optimization, as we know all blocks in it are valid already. CBlockIndex *pindexTest = pindexNew; bool hasValidAncestor = true; while (hasValidAncestor && pindexTest && pindexTest != pindexFork) { assert(pindexTest->nChainTx || pindexTest->nHeight == 0); // If this is a parked chain, but it has enough PoW, clear the park // state. bool fParkedChain = pindexTest->nStatus.isOnParkedChain(); if (fParkedChain && gArgs.GetBoolArg("-parkdeepreorg", true)) { const CBlockIndex *pindexTip = chainActive.Tip(); // During initialization, pindexTip and/or pindexFork may be // null. In this case, we just ignore the fact that the chain is // parked. if (!pindexTip || !pindexFork) { UnparkBlock(pindexTest); continue; } // A parked chain can be unparked if it has twice as much PoW // accumulated as the main chain has since the fork block. CBlockIndex const *pindexExtraPow = pindexTip; arith_uint256 requiredWork = pindexTip->nChainWork; switch (pindexTip->nHeight - pindexFork->nHeight) { // Limit the penality for depth 1, 2 and 3 to half a block // worth of work to ensure we don't fork accidentally. case 3: case 2: pindexExtraPow = pindexExtraPow->pprev; // FALLTHROUGH case 1: { const arith_uint256 deltaWork = pindexExtraPow->nChainWork - pindexFork->nChainWork; requiredWork += (deltaWork >> 1); break; } default: requiredWork += pindexExtraPow->nChainWork - pindexFork->nChainWork; break; } if (pindexNew->nChainWork > requiredWork) { // We have enough, clear the parked state. LogPrintf("Unpark block %s as its chain has accumulated " "enough PoW.\n", pindexTest->GetBlockHash().ToString()); fParkedChain = false; UnparkBlock(pindexTest); } } // Pruned nodes may have entries in setBlockIndexCandidates for // which block files have been deleted. Remove those as candidates // for the most work chain if we come across them; we can't switch // to a chain unless we have all the non-active-chain parent blocks. bool fInvalidChain = pindexTest->nStatus.isInvalid(); bool fMissingData = !pindexTest->nStatus.hasData(); if (!(fInvalidChain || fParkedChain || fMissingData)) { // The current block is acceptable, move to the parent, up to // the fork point. pindexTest = pindexTest->pprev; continue; } // Candidate chain is not usable (either invalid or missing data) hasValidAncestor = false; setBlockIndexCandidates.erase(pindexTest); if (fInvalidChain && (pindexBestInvalid == nullptr || pindexNew->nChainWork > pindexBestInvalid->nChainWork)) { pindexBestInvalid = pindexNew; } if (fParkedChain && (pindexBestParked == nullptr || pindexNew->nChainWork > pindexBestParked->nChainWork)) { pindexBestParked = pindexNew; } CBlockIndex *pindexFailed = pindexNew; // Remove the entire chain from the set. while (pindexTest != pindexFailed) { if (fInvalidChain || fParkedChain) { pindexFailed->nStatus = pindexFailed->nStatus.withFailedParent(fInvalidChain) .withParkedParent(fParkedChain); } else if (fMissingData) { // If we're missing data, then add back to // mapBlocksUnlinked, so that if the block arrives in the // future we can try adding to setBlockIndexCandidates // again. mapBlocksUnlinked.insert( std::make_pair(pindexFailed->pprev, pindexFailed)); } setBlockIndexCandidates.erase(pindexFailed); pindexFailed = pindexFailed->pprev; } if (fInvalidChain || fParkedChain) { // We discovered a new chain tip that is either parked or // invalid, we may want to warn. CheckForkWarningConditionsOnNewFork(pindexNew); } } // We found a candidate that has valid ancestors. This is our guy. if (hasValidAncestor) { return pindexNew; } } while (true); } /** * Delete all entries in setBlockIndexCandidates that are worse than the current * tip. */ void CChainState::PruneBlockIndexCandidates() { // Note that we can't delete the current block itself, as we may need to // return to it later in case a reorganization to a better block fails. auto it = setBlockIndexCandidates.begin(); while (it != setBlockIndexCandidates.end() && setBlockIndexCandidates.value_comp()(*it, chainActive.Tip())) { setBlockIndexCandidates.erase(it++); } // Either the current tip or a successor of it we're working towards is left // in setBlockIndexCandidates. assert(!setBlockIndexCandidates.empty()); } /** * Try to make some progress towards making pindexMostWork the active block. * pblock is either nullptr or a pointer to a CBlock corresponding to * pindexMostWork. */ bool CChainState::ActivateBestChainStep( const Config &config, CValidationState &state, CBlockIndex *pindexMostWork, const std::shared_ptr<const CBlock> &pblock, bool &fInvalidFound, ConnectTrace &connectTrace) { AssertLockHeld(cs_main); const CBlockIndex *pindexOldTip = chainActive.Tip(); const CBlockIndex *pindexFork = chainActive.FindFork(pindexMostWork); // Disconnect active blocks which are no longer in the best chain. bool fBlocksDisconnected = false; DisconnectedBlockTransactions disconnectpool; while (chainActive.Tip() && chainActive.Tip() != pindexFork) { if (!DisconnectTip(config, state, &disconnectpool)) { // This is likely a fatal error, but keep the mempool consistent, // just in case. Only remove from the mempool in this case. disconnectpool.updateMempoolForReorg(config, false); return false; } fBlocksDisconnected = true; } // Build list of new blocks to connect. std::vector<CBlockIndex *> vpindexToConnect; bool fContinue = true; int nHeight = pindexFork ? pindexFork->nHeight : -1; while (fContinue && nHeight != pindexMostWork->nHeight) { // Don't iterate the entire list of potential improvements toward the // best tip, as we likely only need a few blocks along the way. int nTargetHeight = std::min(nHeight + 32, pindexMostWork->nHeight); vpindexToConnect.clear(); vpindexToConnect.reserve(nTargetHeight - nHeight); CBlockIndex *pindexIter = pindexMostWork->GetAncestor(nTargetHeight); while (pindexIter && pindexIter->nHeight != nHeight) { vpindexToConnect.push_back(pindexIter); pindexIter = pindexIter->pprev; } nHeight = nTargetHeight; // Connect new blocks. for (CBlockIndex *pindexConnect : reverse_iterate(vpindexToConnect)) { if (!ConnectTip(config, state, pindexConnect, pindexConnect == pindexMostWork ? pblock : std::shared_ptr<const CBlock>(), connectTrace, disconnectpool)) { if (state.IsInvalid()) { // The block violates a consensus rule. if (!state.CorruptionPossible()) { InvalidChainFound(vpindexToConnect.back()); } state = CValidationState(); fInvalidFound = true; fContinue = false; break; } // A system error occurred (disk space, database error, ...). // Make the mempool consistent with the current tip, just in // case any observers try to use it before shutdown. disconnectpool.updateMempoolForReorg(config, false); return false; } else { PruneBlockIndexCandidates(); if (!pindexOldTip || chainActive.Tip()->nChainWork > pindexOldTip->nChainWork) { // We're in a better position than we were. Return // temporarily to release the lock. fContinue = false; break; } } } } if (fBlocksDisconnected || !disconnectpool.isEmpty()) { // If any blocks were disconnected, we need to update the mempool even // if disconnectpool is empty. The disconnectpool may also be non-empty // if the mempool was imported due to new validation rules being in // effect. LogPrint(BCLog::MEMPOOL, "Updating mempool due to reorganization or " "rules upgrade/downgrade\n"); disconnectpool.updateMempoolForReorg(config, true); } g_mempool.check(pcoinsTip.get()); // Callbacks/notifications for a new best chain. if (fInvalidFound) { CheckForkWarningConditionsOnNewFork(pindexMostWork); } else { CheckForkWarningConditions(); } return true; } static void NotifyHeaderTip() { bool fNotify = false; bool fInitialBlockDownload = false; static CBlockIndex *pindexHeaderOld = nullptr; CBlockIndex *pindexHeader = nullptr; { LOCK(cs_main); pindexHeader = pindexBestHeader; if (pindexHeader != pindexHeaderOld) { fNotify = true; fInitialBlockDownload = IsInitialBlockDownload(); pindexHeaderOld = pindexHeader; } } // Send block tip changed notifications without cs_main if (fNotify) { uiInterface.NotifyHeaderTip(fInitialBlockDownload, pindexHeader); } } /** * Make the best chain active, in multiple steps. The result is either failure * or an activated best chain. pblock is either nullptr or a pointer to a block * that is already loaded (to avoid loading it again from disk). * * ActivateBestChain is split into steps (see ActivateBestChainStep) so that * we avoid holding cs_main for an extended period of time; the length of this * call may be quite long during reindexing or a substantial reorg. */ bool CChainState::ActivateBestChain(const Config &config, CValidationState &state, std::shared_ptr<const CBlock> pblock) { // Note that while we're often called here from ProcessNewBlock, this is // far from a guarantee. Things in the P2P/RPC will often end up calling // us in the middle of ProcessNewBlock - do not assume pblock is set // sanely for performance or correctness! AssertLockNotHeld(cs_main); // ABC maintains a fair degree of expensive-to-calculate internal state // because this function periodically releases cs_main so that it does not // lock up other threads for too long during large connects - and to allow // for e.g. the callback queue to drain we use m_cs_chainstate to enforce // mutual exclusion so that only one caller may execute this function at a // time LOCK(m_cs_chainstate); CBlockIndex *pindexMostWork = nullptr; CBlockIndex *pindexNewTip = nullptr; int nStopAtHeight = gArgs.GetArg("-stopatheight", DEFAULT_STOPATHEIGHT); do { boost::this_thread::interruption_point(); if (GetMainSignals().CallbacksPending() > 10) { // Block until the validation queue drains. This should largely // never happen in normal operation, however may happen during // reindex, causing memory blowup if we run too far ahead. // Note that if a validationinterface callback ends up calling // ActivateBestChain this may lead to a deadlock! We should // probably have a DEBUG_LOCKORDER test for this in the future. SyncWithValidationInterfaceQueue(); } { LOCK(cs_main); CBlockIndex *starting_tip = chainActive.Tip(); bool blocks_connected = false; do { // We absolutely may not unlock cs_main until we've made forward // progress (with the exception of shutdown due to hardware // issues, low disk space, etc). // Destructed before cs_main is unlocked ConnectTrace connectTrace(g_mempool); if (pindexMostWork == nullptr) { pindexMostWork = FindMostWorkChain(); } // Whether we have anything to do at all. if (pindexMostWork == nullptr || pindexMostWork == chainActive.Tip()) { break; } bool fInvalidFound = false; std::shared_ptr<const CBlock> nullBlockPtr; if (!ActivateBestChainStep( config, state, pindexMostWork, pblock && pblock->GetHash() == pindexMostWork->GetBlockHash() ? pblock : nullBlockPtr, fInvalidFound, connectTrace)) { return false; } blocks_connected = true; if (fInvalidFound) { // Wipe cache, we may need another branch now. pindexMostWork = nullptr; } pindexNewTip = chainActive.Tip(); for (const PerBlockConnectTrace &trace : connectTrace.GetBlocksConnected()) { assert(trace.pblock && trace.pindex); GetMainSignals().BlockConnected(trace.pblock, trace.pindex, trace.conflictedTxs); } } while (!chainActive.Tip() || (starting_tip && CBlockIndexWorkComparator()( chainActive.Tip(), starting_tip))); if (!blocks_connected) { return true; } const CBlockIndex *pindexFork = chainActive.FindFork(starting_tip); bool fInitialDownload = IsInitialBlockDownload(); // Notify external listeners about the new tip. // Enqueue while holding cs_main to ensure that UpdatedBlockTip is // called in the order in which blocks are connected if (pindexFork != pindexNewTip) { // Notify ValidationInterface subscribers GetMainSignals().UpdatedBlockTip(pindexNewTip, pindexFork, fInitialDownload); // Always notify the UI if a new block tip was connected uiInterface.NotifyBlockTip(fInitialDownload, pindexNewTip); } } // When we reach this point, we switched to a new tip (stored in // pindexNewTip). if (nStopAtHeight && pindexNewTip && pindexNewTip->nHeight >= nStopAtHeight) { StartShutdown(); } // We check shutdown only after giving ActivateBestChainStep a chance to // run once so that we never shutdown before connecting the genesis // block during LoadChainTip(). Previously this caused an assert() // failure during shutdown in such cases as the UTXO DB flushing checks // that the best block hash is non-null. if (ShutdownRequested()) { break; } } while (pindexNewTip != pindexMostWork); const CChainParams ¶ms = config.GetChainParams(); CheckBlockIndex(params.GetConsensus()); // Write changes periodically to disk, after relay. if (!FlushStateToDisk(params, state, FlushStateMode::PERIODIC)) { return false; } return true; } bool ActivateBestChain(const Config &config, CValidationState &state, std::shared_ptr<const CBlock> pblock) { return g_chainstate.ActivateBestChain(config, state, std::move(pblock)); } bool CChainState::PreciousBlock(const Config &config, CValidationState &state, CBlockIndex *pindex) { { LOCK(cs_main); if (pindex->nChainWork < chainActive.Tip()->nChainWork) { // Nothing to do, this block is not at the tip. return true; } if (chainActive.Tip()->nChainWork > nLastPreciousChainwork) { // The chain has been extended since the last call, reset the // counter. nBlockReverseSequenceId = -1; } nLastPreciousChainwork = chainActive.Tip()->nChainWork; setBlockIndexCandidates.erase(pindex); pindex->nSequenceId = nBlockReverseSequenceId; if (nBlockReverseSequenceId > std::numeric_limits<int32_t>::min()) { // We can't keep reducing the counter if somebody really wants to // call preciousblock 2**31-1 times on the same set of tips... nBlockReverseSequenceId--; } // In case this was parked, unpark it. UnparkBlock(pindex); // Make sure it is added to the candidate list if appropriate. if (pindex->IsValid(BlockValidity::TRANSACTIONS) && pindex->nChainTx) { setBlockIndexCandidates.insert(pindex); PruneBlockIndexCandidates(); } } return ActivateBestChain(config, state); } bool PreciousBlock(const Config &config, CValidationState &state, CBlockIndex *pindex) { return g_chainstate.PreciousBlock(config, state, pindex); } bool CChainState::UnwindBlock(const Config &config, CValidationState &state, CBlockIndex *pindex, bool invalidate) { AssertLockHeld(cs_main); // We first disconnect backwards and then mark the blocks as invalid. // This prevents a case where pruned nodes may fail to invalidateblock // and be left unable to start as they have no tip candidates (as there // are no blocks that meet the "have data and are not invalid per // nStatus" criteria for inclusion in setBlockIndexCandidates). bool pindex_was_in_chain = false; CBlockIndex *invalid_walk_tip = chainActive.Tip(); DisconnectedBlockTransactions disconnectpool; while (chainActive.Contains(pindex)) { pindex_was_in_chain = true; // ActivateBestChain considers blocks already in chainActive // unconditionally valid already, so force disconnect away from it. if (!DisconnectTip(config, state, &disconnectpool)) { // It's probably hopeless to try to make the mempool consistent // here if DisconnectTip failed, but we can try. disconnectpool.updateMempoolForReorg(config, false); return false; } } // Now mark the blocks we just disconnected as descendants invalid // (note this may not be all descendants). while (pindex_was_in_chain && invalid_walk_tip != pindex) { invalid_walk_tip->nStatus = invalidate ? invalid_walk_tip->nStatus.withFailedParent() : invalid_walk_tip->nStatus.withParkedParent(); setDirtyBlockIndex.insert(invalid_walk_tip); invalid_walk_tip = invalid_walk_tip->pprev; } // Mark the block as either invalid or parked. pindex->nStatus = invalidate ? pindex->nStatus.withFailed() : pindex->nStatus.withParked(); setDirtyBlockIndex.insert(pindex); if (invalidate) { m_failed_blocks.insert(pindex); } // DisconnectTip will add transactions to disconnectpool; try to add these // back to the mempool. disconnectpool.updateMempoolForReorg(config, true); // The resulting new best tip may not be in setBlockIndexCandidates anymore, // so add it again. for (const std::pair<const uint256, CBlockIndex *> &it : mapBlockIndex) { CBlockIndex *i = it.second; if (i->IsValid(BlockValidity::TRANSACTIONS) && i->nChainTx && !setBlockIndexCandidates.value_comp()(i, chainActive.Tip())) { setBlockIndexCandidates.insert(i); } } if (invalidate) { InvalidChainFound(pindex); } // Only notify about a new block tip if the active chain was modified. if (pindex_was_in_chain) { uiInterface.NotifyBlockTip(IsInitialBlockDownload(), pindex->pprev); } return true; } bool FinalizeBlockAndInvalidate(const Config &config, CValidationState &state, CBlockIndex *pindex) EXCLUSIVE_LOCKS_REQUIRED(cs_main) { AssertLockHeld(cs_main); if (!FinalizeBlockInternal(config, state, pindex)) { // state is set by FinalizeBlockInternal. return false; } // We have a valid candidate, make sure it is not parked. if (pindex->nStatus.isOnParkedChain()) { UnparkBlock(pindex); } // If the finalized block is not on the active chain, we need to rewind. if (!AreOnTheSameFork(pindex, chainActive.Tip())) { const CBlockIndex *pindexFork = chainActive.FindFork(pindex); CBlockIndex *pindexToInvalidate = chainActive.Tip()->GetAncestor(pindexFork->nHeight + 1); return InvalidateBlock(config, state, pindexToInvalidate); } return true; } bool InvalidateBlock(const Config &config, CValidationState &state, CBlockIndex *pindex) { return g_chainstate.UnwindBlock(config, state, pindex, true); } bool ParkBlock(const Config &config, CValidationState &state, CBlockIndex *pindex) { return g_chainstate.UnwindBlock(config, state, pindex, false); } template <typename F> void CChainState::UpdateFlagsForBlock(CBlockIndex *pindexBase, CBlockIndex *pindex, F f) { BlockStatus newStatus = f(pindex->nStatus); if (pindex->nStatus != newStatus && pindex->GetAncestor(pindexBase->nHeight) == pindexBase) { pindex->nStatus = newStatus; setDirtyBlockIndex.insert(pindex); if (newStatus.isValid()) { m_failed_blocks.erase(pindex); } if (pindex->IsValid(BlockValidity::TRANSACTIONS) && pindex->nChainTx && setBlockIndexCandidates.value_comp()(chainActive.Tip(), pindex)) { setBlockIndexCandidates.insert(pindex); } } } template <typename F, typename C> void CChainState::UpdateFlags(CBlockIndex *pindex, F f, C fchild) EXCLUSIVE_LOCKS_REQUIRED(cs_main) { AssertLockHeld(cs_main); // Update the current block. UpdateFlagsForBlock(pindex, pindex, f); // Update the flags from this block and all its descendants. BlockMap::iterator it = mapBlockIndex.begin(); while (it != mapBlockIndex.end()) { UpdateFlagsForBlock(pindex, it->second, fchild); it++; } // Update the flags from all ancestors too. while (pindex != nullptr) { BlockStatus newStatus = f(pindex->nStatus); if (pindex->nStatus != newStatus) { pindex->nStatus = newStatus; setDirtyBlockIndex.insert(pindex); if (newStatus.isValid()) { m_failed_blocks.erase(pindex); } } pindex = pindex->pprev; } } template <typename F> void CChainState::UpdateFlags(CBlockIndex *pindex, F f) EXCLUSIVE_LOCKS_REQUIRED(cs_main) { // Handy shorthand. UpdateFlags(pindex, f, f); } void CChainState::ResetBlockFailureFlags(CBlockIndex *pindex) { AssertLockHeld(cs_main); if (pindexBestInvalid && (pindexBestInvalid->GetAncestor(pindex->nHeight) == pindex || pindex->GetAncestor(pindexBestInvalid->nHeight) == pindexBestInvalid)) { // Reset the invalid block marker if it is about to be cleared. pindexBestInvalid = nullptr; } // In case we are reconsidering something before the finalization point, // move the finalization point to the last common ancestor. if (pindexFinalized) { pindexFinalized = LastCommonAncestor(pindex, pindexFinalized); } UpdateFlags(pindex, [](const BlockStatus status) { return status.withClearedFailureFlags(); }); } void ResetBlockFailureFlags(CBlockIndex *pindex) { return g_chainstate.ResetBlockFailureFlags(pindex); } void CChainState::UnparkBlockImpl(CBlockIndex *pindex, bool fClearChildren) { AssertLockHeld(cs_main); if (pindexBestParked && (pindexBestParked->GetAncestor(pindex->nHeight) == pindex || pindex->GetAncestor(pindexBestParked->nHeight) == pindexBestParked)) { // Reset the parked block marker if it is about to be cleared. pindexBestParked = nullptr; } UpdateFlags(pindex, [](const BlockStatus status) { return status.withClearedParkedFlags(); }, [fClearChildren](const BlockStatus status) { return fClearChildren ? status.withClearedParkedFlags() : status.withParkedParent(false); }); } void UnparkBlockAndChildren(CBlockIndex *pindex) { return g_chainstate.UnparkBlockImpl(pindex, true); } void UnparkBlock(CBlockIndex *pindex) { return g_chainstate.UnparkBlockImpl(pindex, false); } const CBlockIndex *GetFinalizedBlock() { AssertLockHeld(cs_main); return pindexFinalized; } bool IsBlockFinalized(const CBlockIndex *pindex) { AssertLockHeld(cs_main); return pindexFinalized && pindexFinalized->GetAncestor(pindex->nHeight) == pindex; } CBlockIndex *CChainState::AddToBlockIndex(const CBlockHeader &block) EXCLUSIVE_LOCKS_REQUIRED(cs_main) { AssertLockHeld(cs_main); // Check for duplicate uint256 hash = block.GetHash(); BlockMap::iterator it = mapBlockIndex.find(hash); if (it != mapBlockIndex.end()) { return it->second; } // Construct new block index object CBlockIndex *pindexNew = new CBlockIndex(block); // We assign the sequence id to blocks only when the full data is available, // to avoid miners withholding blocks but broadcasting headers, to get a // competitive advantage. pindexNew->nSequenceId = 0; BlockMap::iterator mi = mapBlockIndex.insert(std::make_pair(hash, pindexNew)).first; pindexNew->phashBlock = &((*mi).first); BlockMap::iterator miPrev = mapBlockIndex.find(block.hashPrevBlock); if (miPrev != mapBlockIndex.end()) { pindexNew->pprev = (*miPrev).second; pindexNew->nHeight = pindexNew->pprev->nHeight + 1; pindexNew->BuildSkip(); } pindexNew->nTimeReceived = GetTime(); pindexNew->nTimeMax = (pindexNew->pprev ? std::max(pindexNew->pprev->nTimeMax, pindexNew->nTime) : pindexNew->nTime); pindexNew->nChainWork = (pindexNew->pprev ? pindexNew->pprev->nChainWork : 0) + GetBlockProof(*pindexNew); pindexNew->RaiseValidity(BlockValidity::TREE); if (pindexBestHeader == nullptr || pindexBestHeader->nChainWork < pindexNew->nChainWork) { pindexBestHeader = pindexNew; } setDirtyBlockIndex.insert(pindexNew); return pindexNew; } /** * Mark a block as having its data received and checked (up to * BLOCK_VALID_TRANSACTIONS). */ bool CChainState::ReceivedBlockTransactions(const CBlock &block, CValidationState &state, CBlockIndex *pindexNew, const FlatFilePos &pos) { pindexNew->nTx = block.vtx.size(); pindexNew->nChainTx = 0; pindexNew->nFile = pos.nFile; pindexNew->nDataPos = pos.nPos; pindexNew->nUndoPos = 0; pindexNew->nStatus = pindexNew->nStatus.withData(); pindexNew->RaiseValidity(BlockValidity::TRANSACTIONS); setDirtyBlockIndex.insert(pindexNew); if (pindexNew->pprev == nullptr || pindexNew->pprev->nChainTx) { // If pindexNew is the genesis block or all parents are // BLOCK_VALID_TRANSACTIONS. std::deque<CBlockIndex *> queue; queue.push_back(pindexNew); // Recursively process any descendant blocks that now may be eligible to // be connected. while (!queue.empty()) { CBlockIndex *pindex = queue.front(); queue.pop_front(); pindex->nChainTx = (pindex->pprev ? pindex->pprev->nChainTx : 0) + pindex->nTx; if (pindex->nSequenceId == 0) { // We assign a sequence is when transaction are received to // prevent a miner from being able to broadcast a block but not // its content. However, a sequence id may have been set // manually, for instance via PreciousBlock, in which case, we // don't need to assign one. pindex->nSequenceId = nBlockSequenceId++; } if (chainActive.Tip() == nullptr || !setBlockIndexCandidates.value_comp()(pindex, chainActive.Tip())) { setBlockIndexCandidates.insert(pindex); } std::pair<std::multimap<CBlockIndex *, CBlockIndex *>::iterator, std::multimap<CBlockIndex *, CBlockIndex *>::iterator> range = mapBlocksUnlinked.equal_range(pindex); while (range.first != range.second) { std::multimap<CBlockIndex *, CBlockIndex *>::iterator it = range.first; queue.push_back(it->second); range.first++; mapBlocksUnlinked.erase(it); } } } else if (pindexNew->pprev && pindexNew->pprev->IsValid(BlockValidity::TREE)) { mapBlocksUnlinked.insert(std::make_pair(pindexNew->pprev, pindexNew)); } return true; } static bool FindBlockPos(FlatFilePos &pos, unsigned int nAddSize, unsigned int nHeight, uint64_t nTime, bool fKnown = false) { LOCK(cs_LastBlockFile); unsigned int nFile = fKnown ? pos.nFile : nLastBlockFile; if (vinfoBlockFile.size() <= nFile) { vinfoBlockFile.resize(nFile + 1); } if (!fKnown) { while (vinfoBlockFile[nFile].nSize + nAddSize >= MAX_BLOCKFILE_SIZE) { nFile++; if (vinfoBlockFile.size() <= nFile) { vinfoBlockFile.resize(nFile + 1); } } pos.nFile = nFile; pos.nPos = vinfoBlockFile[nFile].nSize; } if ((int)nFile != nLastBlockFile) { if (!fKnown) { LogPrintf("Leaving block file %i: %s\n", nLastBlockFile, vinfoBlockFile[nLastBlockFile].ToString()); } FlushBlockFile(!fKnown); nLastBlockFile = nFile; } vinfoBlockFile[nFile].AddBlock(nHeight, nTime); if (fKnown) { vinfoBlockFile[nFile].nSize = std::max(pos.nPos + nAddSize, vinfoBlockFile[nFile].nSize); } else { vinfoBlockFile[nFile].nSize += nAddSize; } if (!fKnown) { bool out_of_space; size_t bytes_allocated = BlockFileSeq().Allocate(pos, nAddSize, out_of_space); if (out_of_space) { return AbortNode("Disk space is low!", _("Error: Disk space is low!")); } if (bytes_allocated != 0 && fPruneMode) { fCheckForPruning = true; } } setDirtyFileInfo.insert(nFile); return true; } static bool FindUndoPos(CValidationState &state, int nFile, FlatFilePos &pos, unsigned int nAddSize) { pos.nFile = nFile; LOCK(cs_LastBlockFile); pos.nPos = vinfoBlockFile[nFile].nUndoSize; vinfoBlockFile[nFile].nUndoSize += nAddSize; setDirtyFileInfo.insert(nFile); bool out_of_space; size_t bytes_allocated = UndoFileSeq().Allocate(pos, nAddSize, out_of_space); if (out_of_space) { return AbortNode(state, "Disk space is low!", _("Error: Disk space is low!")); } if (bytes_allocated != 0 && fPruneMode) { fCheckForPruning = true; } return true; } /** * Return true if the provided block header is valid. * Only verify PoW if blockValidationOptions is configured to do so. * This allows validation of headers on which the PoW hasn't been done. * For example: to validate template handed to mining software. * Do not call this for any check that depends on the context. * For context-dependent calls, see ContextualCheckBlockHeader. */ static bool CheckBlockHeader(const CBlockHeader &block, CValidationState &state, const Consensus::Params ¶ms, BlockValidationOptions validationOptions) { // Check proof of work matches claimed amount if (validationOptions.shouldValidatePoW() && !CheckProofOfWork(block.GetHash(), block.nBits, params)) { return state.DoS(50, false, REJECT_INVALID, "high-hash", false, "proof of work failed"); } return true; } bool CheckBlock(const CBlock &block, CValidationState &state, const Consensus::Params ¶ms, BlockValidationOptions validationOptions) { // These are checks that are independent of context. if (block.fChecked) { return true; } // Check that the header is valid (particularly PoW). This is mostly // redundant with the call in AcceptBlockHeader. if (!CheckBlockHeader(block, state, params, validationOptions)) { return false; } // Check the merkle root. if (validationOptions.shouldValidateMerkleRoot()) { bool mutated; uint256 hashMerkleRoot2 = BlockMerkleRoot(block, &mutated); if (block.hashMerkleRoot != hashMerkleRoot2) { return state.DoS(100, false, REJECT_INVALID, "bad-txnmrklroot", true, "hashMerkleRoot mismatch"); } // Check for merkle tree malleability (CVE-2012-2459): repeating // sequences of transactions in a block without affecting the merkle // root of a block, while still invalidating it. if (mutated) { return state.DoS(100, false, REJECT_INVALID, "bad-txns-duplicate", true, "duplicate transaction"); } } // All potential-corruption validation must be done before we do any // transaction validation, as otherwise we may mark the header as invalid // because we receive the wrong transactions for it. // First transaction must be coinbase. if (block.vtx.empty()) { return state.DoS(100, false, REJECT_INVALID, "bad-cb-missing", false, "first tx is not coinbase"); } // Size limits. auto nMaxBlockSize = validationOptions.getExcessiveBlockSize(); // Bail early if there is no way this block is of reasonable size. if ((block.vtx.size() * MIN_TRANSACTION_SIZE) > nMaxBlockSize) { return state.DoS(100, false, REJECT_INVALID, "bad-blk-length", false, "size limits failed"); } auto currentBlockSize = ::GetSerializeSize(block, SER_NETWORK, PROTOCOL_VERSION); if (currentBlockSize > nMaxBlockSize) { return state.DoS(100, false, REJECT_INVALID, "bad-blk-length", false, "size limits failed"); } // And a valid coinbase. if (!CheckCoinbase(*block.vtx[0], state)) { return state.Invalid(false, state.GetRejectCode(), state.GetRejectReason(), strprintf("Coinbase check failed (txid %s) %s", block.vtx[0]->GetId().ToString(), state.GetDebugMessage())); } // Keep track of the sigops count. uint64_t nSigOps = 0; auto nMaxSigOpsCount = GetMaxBlockSigOpsCount(currentBlockSize); // Check transactions auto txCount = block.vtx.size(); auto *tx = block.vtx[0].get(); size_t i = 0; while (true) { // Count the sigops for the current transaction. If the total sigops // count is too high, the the block is invalid. nSigOps += GetSigOpCountWithoutP2SH(*tx, STANDARD_SCRIPT_VERIFY_FLAGS); if (nSigOps > nMaxSigOpsCount) { return state.DoS(100, false, REJECT_INVALID, "bad-blk-sigops", false, "out-of-bounds SigOpCount"); } // Go to the next transaction. i++; // We reached the end of the block, success. if (i >= txCount) { break; } // Check that the transaction is valid. Because this check differs for // the coinbase, the loop is arranged such as this only runs after at // least one increment. tx = block.vtx[i].get(); if (!CheckRegularTransaction(*tx, state)) { return state.Invalid( false, state.GetRejectCode(), state.GetRejectReason(), strprintf("Transaction check failed (txid %s) %s", tx->GetId().ToString(), state.GetDebugMessage())); } } if (validationOptions.shouldValidatePoW() && validationOptions.shouldValidateMerkleRoot()) { block.fChecked = true; } return true; } /** * Context-dependent validity checks. * By "context", we mean only the previous block headers, but not the UTXO * set; UTXO-related validity checks are done in ConnectBlock(). * NOTE: This function is not currently invoked by ConnectBlock(), so we * should consider upgrade issues if we change which consensus rules are * enforced in this function (eg by adding a new consensus rule). See comment * in ConnectBlock(). * Note that -reindex-chainstate skips the validation that happens here! */ static bool ContextualCheckBlockHeader(const CChainParams ¶ms, const CBlockHeader &block, CValidationState &state, const CBlockIndex *pindexPrev, int64_t nAdjustedTime) { assert(pindexPrev != nullptr); const int nHeight = pindexPrev->nHeight + 1; // Check proof of work const Consensus::Params &consensusParams = params.GetConsensus(); if (block.nBits != GetNextWorkRequired(pindexPrev, &block, consensusParams)) { LogPrintf("bad bits after height: %d\n", pindexPrev->nHeight); return state.DoS(100, false, REJECT_INVALID, "bad-diffbits", false, "incorrect proof of work"); } // Check against checkpoints if (fCheckpointsEnabled) { const CCheckpointData &checkpoints = params.Checkpoints(); // Check that the block chain matches the known block chain up to a // checkpoint. if (!Checkpoints::CheckBlock(checkpoints, nHeight, block.GetHash())) { return state.DoS(100, error("%s: rejected by checkpoint lock-in at %d", __func__, nHeight), REJECT_CHECKPOINT, "checkpoint mismatch"); } // Don't accept any forks from the main chain prior to last checkpoint. // GetLastCheckpoint finds the last checkpoint in MapCheckpoints that's // in our MapBlockIndex. CBlockIndex *pcheckpoint = Checkpoints::GetLastCheckpoint(checkpoints); if (pcheckpoint && nHeight < pcheckpoint->nHeight) { return state.DoS( 100, error("%s: forked chain older than last checkpoint (height %d)", __func__, nHeight), REJECT_CHECKPOINT, "bad-fork-prior-to-checkpoint"); } } // Check timestamp against prev if (block.GetBlockTime() <= pindexPrev->GetMedianTimePast()) { return state.Invalid(false, REJECT_INVALID, "time-too-old", "block's timestamp is too early"); } // Check timestamp if (block.GetBlockTime() > nAdjustedTime + MAX_FUTURE_BLOCK_TIME) { return state.Invalid(false, REJECT_INVALID, "time-too-new", "block timestamp too far in the future"); } // Reject outdated version blocks when 95% (75% on testnet) of the network // has upgraded: // check for version 2, 3 and 4 upgrades if ((block.nVersion < 2 && nHeight >= consensusParams.BIP34Height) || (block.nVersion < 3 && nHeight >= consensusParams.BIP66Height) || (block.nVersion < 4 && nHeight >= consensusParams.BIP65Height)) { return state.Invalid( false, REJECT_OBSOLETE, strprintf("bad-version(0x%08x)", block.nVersion), strprintf("rejected nVersion=0x%08x block", block.nVersion)); } return true; } bool ContextualCheckTransactionForCurrentBlock(const Consensus::Params ¶ms, const CTransaction &tx, CValidationState &state, int flags) { AssertLockHeld(cs_main); // By convention a negative value for flags indicates that the current // network-enforced consensus rules should be used. In a future soft-fork // scenario that would mean checking which rules would be enforced for the // next block and setting the appropriate flags. At the present time no // soft-forks are scheduled, so no flags are set. flags = std::max(flags, 0); // ContextualCheckTransactionForCurrentBlock() uses chainActive.Height()+1 // to evaluate nLockTime because when IsFinalTx() is called within // CBlock::AcceptBlock(), the height of the block *being* evaluated is what // is used. Thus if we want to know if a transaction can be part of the // *next* block, we need to call ContextualCheckTransaction() with one more // than chainActive.Height(). const int nBlockHeight = chainActive.Height() + 1; // BIP113 will require that time-locked transactions have nLockTime set to // less than the median time of the previous block they're contained in. // When the next block is created its previous block will be the current // chain tip, so we use that to calculate the median time passed to // ContextualCheckTransaction() if LOCKTIME_MEDIAN_TIME_PAST is set. const int64_t nMedianTimePast = chainActive.Tip() == nullptr ? 0 : chainActive.Tip()->GetMedianTimePast(); const int64_t nLockTimeCutoff = (flags & LOCKTIME_MEDIAN_TIME_PAST) ? nMedianTimePast : GetAdjustedTime(); return ContextualCheckTransaction(params, tx, state, nBlockHeight, nLockTimeCutoff, nMedianTimePast); } /** * NOTE: This function is not currently invoked by ConnectBlock(), so we * should consider upgrade issues if we change which consensus rules are * enforced in this function (eg by adding a new consensus rule). See comment * in ConnectBlock(). * Note that -reindex-chainstate skips the validation that happens here! */ static bool ContextualCheckBlock(const CBlock &block, CValidationState &state, const Consensus::Params ¶ms, const CBlockIndex *pindexPrev) { const int nHeight = pindexPrev == nullptr ? 0 : pindexPrev->nHeight + 1; // Start enforcing BIP113 (Median Time Past). int nLockTimeFlags = 0; if (nHeight >= params.CSVHeight) { nLockTimeFlags |= LOCKTIME_MEDIAN_TIME_PAST; } const int64_t nMedianTimePast = pindexPrev == nullptr ? 0 : pindexPrev->GetMedianTimePast(); const int64_t nLockTimeCutoff = (nLockTimeFlags & LOCKTIME_MEDIAN_TIME_PAST) ? nMedianTimePast : block.GetBlockTime(); const bool fIsMagneticAnomalyEnabled = IsMagneticAnomalyEnabled(params, pindexPrev); // Check that all transactions are finalized const CTransaction *prevTx = nullptr; for (const auto &ptx : block.vtx) { const CTransaction &tx = *ptx; if (fIsMagneticAnomalyEnabled) { if (prevTx && (tx.GetId() <= prevTx->GetId())) { if (tx.GetId() == prevTx->GetId()) { return state.DoS(100, false, REJECT_INVALID, "tx-duplicate", false, strprintf("Duplicated transaction %s", tx.GetId().ToString())); } return state.DoS( 100, false, REJECT_INVALID, "tx-ordering", false, strprintf("Transaction order is invalid (%s < %s)", tx.GetId().ToString(), prevTx->GetId().ToString())); } if (prevTx || !tx.IsCoinBase()) { prevTx = &tx; } } if (!ContextualCheckTransaction(params, tx, state, nHeight, nLockTimeCutoff, nMedianTimePast)) { // state set by ContextualCheckTransaction. return false; } } // Enforce rule that the coinbase starts with serialized block height if (nHeight >= params.BIP34Height) { CScript expect = CScript() << nHeight; if (block.vtx[0]->vin[0].scriptSig.size() < expect.size() || !std::equal(expect.begin(), expect.end(), block.vtx[0]->vin[0].scriptSig.begin())) { return state.DoS(100, false, REJECT_INVALID, "bad-cb-height", false, "block height mismatch in coinbase"); } } return true; } /** * If the provided block header is valid, add it to the block index. * * Returns true if the block is successfully added to the block index. */ bool CChainState::AcceptBlockHeader(const Config &config, const CBlockHeader &block, CValidationState &state, CBlockIndex **ppindex) { AssertLockHeld(cs_main); const CChainParams &chainparams = config.GetChainParams(); // Check for duplicate uint256 hash = block.GetHash(); BlockMap::iterator miSelf = mapBlockIndex.find(hash); CBlockIndex *pindex = nullptr; if (hash != chainparams.GetConsensus().hashGenesisBlock) { if (miSelf != mapBlockIndex.end()) { // Block header is already known. pindex = miSelf->second; if (ppindex) { *ppindex = pindex; } if (pindex->nStatus.isInvalid()) { return state.Invalid(error("%s: block %s is marked invalid", __func__, hash.ToString()), 0, "duplicate"); } return true; } if (!CheckBlockHeader(block, state, chainparams.GetConsensus(), BlockValidationOptions(config))) { return error("%s: Consensus::CheckBlockHeader: %s, %s", __func__, hash.ToString(), FormatStateMessage(state)); } // Get prev block index BlockMap::iterator mi = mapBlockIndex.find(block.hashPrevBlock); if (mi == mapBlockIndex.end()) { return state.DoS(10, error("%s: prev block not found", __func__), 0, "prev-blk-not-found"); } CBlockIndex *pindexPrev = (*mi).second; assert(pindexPrev); if (pindexPrev->nStatus.isInvalid()) { return state.DoS(100, error("%s: prev block invalid", __func__), REJECT_INVALID, "bad-prevblk"); } if (!ContextualCheckBlockHeader(chainparams, block, state, pindexPrev, GetAdjustedTime())) { return error("%s: Consensus::ContextualCheckBlockHeader: %s, %s", __func__, hash.ToString(), FormatStateMessage(state)); } // If the previous block index isn't valid, determine if it descends // from any block which has been found invalid (g_failed_blocks), then // mark pindexPrev and any blocks between them as failed. if (!pindexPrev->IsValid(BlockValidity::SCRIPTS)) { for (const CBlockIndex *failedit : m_failed_blocks) { if (pindexPrev->GetAncestor(failedit->nHeight) == failedit) { assert(failedit->nStatus.hasFailed()); CBlockIndex *invalid_walk = pindexPrev; while (invalid_walk != failedit) { invalid_walk->nStatus = invalid_walk->nStatus.withFailedParent(); setDirtyBlockIndex.insert(invalid_walk); invalid_walk = invalid_walk->pprev; } return state.DoS(100, error("%s: prev block invalid", __func__), REJECT_INVALID, "bad-prevblk"); } } } } if (pindex == nullptr) { pindex = AddToBlockIndex(block); } if (ppindex) { *ppindex = pindex; } CheckBlockIndex(chainparams.GetConsensus()); return true; } // Exposed wrapper for AcceptBlockHeader bool ProcessNewBlockHeaders(const Config &config, const std::vector<CBlockHeader> &headers, CValidationState &state, const CBlockIndex **ppindex, CBlockHeader *first_invalid) { if (first_invalid != nullptr) { first_invalid->SetNull(); } { LOCK(cs_main); for (const CBlockHeader &header : headers) { // Use a temp pindex instead of ppindex to avoid a const_cast CBlockIndex *pindex = nullptr; if (!g_chainstate.AcceptBlockHeader(config, header, state, &pindex)) { if (first_invalid) { *first_invalid = header; } return false; } if (ppindex) { *ppindex = pindex; } } } NotifyHeaderTip(); return true; } /** * Store block on disk. If dbp is non-nullptr, the file is known to already * reside on disk. */ static FlatFilePos SaveBlockToDisk(const CBlock &block, int nHeight, const CChainParams &chainparams, const FlatFilePos *dbp) { unsigned int nBlockSize = ::GetSerializeSize(block, SER_DISK, CLIENT_VERSION); FlatFilePos blockPos; if (dbp != nullptr) { blockPos = *dbp; } if (!FindBlockPos(blockPos, nBlockSize + 8, nHeight, block.GetBlockTime(), dbp != nullptr)) { error("%s: FindBlockPos failed", __func__); return FlatFilePos(); } if (dbp == nullptr) { if (!WriteBlockToDisk(block, blockPos, chainparams.DiskMagic())) { AbortNode("Failed to write block"); return FlatFilePos(); } } return blockPos; } /** * Store a block on disk. * * @param[in] config The global config. * @param[in-out] pblock The block we want to accept. * @param[in] fRequested A boolean to indicate if this block was requested * from our peers. * @param[in] dbp If non-null, the disk position of the block. * @param[in-out] fNewBlock True if block was first received via this call. * @return True if the block is accepted as a valid block and written to disk. */ bool CChainState::AcceptBlock(const Config &config, const std::shared_ptr<const CBlock> &pblock, CValidationState &state, bool fRequested, const FlatFilePos *dbp, bool *fNewBlock) { AssertLockHeld(cs_main); const CBlock &block = *pblock; if (fNewBlock) { *fNewBlock = false; } CBlockIndex *pindex = nullptr; if (!AcceptBlockHeader(config, block, state, &pindex)) { return false; } // Try to process all requested blocks that we don't have, but only // process an unrequested block if it's new and has enough work to // advance our tip, and isn't too many blocks ahead. bool fAlreadyHave = pindex->nStatus.hasData(); // TODO: deal better with return value and error conditions for duplicate // and unrequested blocks. if (fAlreadyHave) { return true; } // Compare block header timestamps and received times of the block and the // chaintip. If they have the same chain height, use these diffs as a // tie-breaker, attempting to pick the more honestly-mined block. int64_t newBlockTimeDiff = std::llabs(pindex->GetReceivedTimeDiff()); int64_t chainTipTimeDiff = chainActive.Tip() ? std::llabs(chainActive.Tip()->GetReceivedTimeDiff()) : 0; bool isSameHeight = chainActive.Tip() && (pindex->nChainWork == chainActive.Tip()->nChainWork); if (isSameHeight) { LogPrintf("Chain tip timestamp-to-received-time difference: hash=%s, " "diff=%d\n", chainActive.Tip()->GetBlockHash().ToString(), chainTipTimeDiff); LogPrintf("New block timestamp-to-received-time difference: hash=%s, " "diff=%d\n", pindex->GetBlockHash().ToString(), newBlockTimeDiff); } bool fHasMoreOrSameWork = (chainActive.Tip() ? pindex->nChainWork >= chainActive.Tip()->nChainWork : true); // Blocks that are too out-of-order needlessly limit the effectiveness of // pruning, because pruning will not delete block files that contain any // blocks which are too close in height to the tip. Apply this test // regardless of whether pruning is enabled; it should generally be safe to // not process unrequested blocks. bool fTooFarAhead = (pindex->nHeight > int(chainActive.Height() + MIN_BLOCKS_TO_KEEP)); // TODO: Decouple this function from the block download logic by removing // fRequested // This requires some new chain data structure to efficiently look up if a // block is in a chain leading to a candidate for best tip, despite not // being such a candidate itself. // If we didn't ask for it: if (!fRequested) { // This is a previously-processed block that was pruned. if (pindex->nTx != 0) { return true; } // Don't process less-work chains. if (!fHasMoreOrSameWork) { return true; } // Block height is too high. if (fTooFarAhead) { return true; } // Protect against DoS attacks from low-work chains. // If our tip is behind, a peer could try to send us // low-work blocks on a fake chain that we would never // request; don't process these. if (pindex->nChainWork < nMinimumChainWork) { return true; } } const CChainParams &chainparams = config.GetChainParams(); if (!CheckBlock(block, state, chainparams.GetConsensus(), BlockValidationOptions(config)) || !ContextualCheckBlock(block, state, chainparams.GetConsensus(), pindex->pprev)) { if (state.IsInvalid() && !state.CorruptionPossible()) { pindex->nStatus = pindex->nStatus.withFailed(); setDirtyBlockIndex.insert(pindex); } return error("%s: %s (block %s)", __func__, FormatStateMessage(state), block.GetHash().ToString()); } // If this is a deep reorg (a regorg of more than one block), preemptively // mark the chain as parked. If it has enough work, it'll unpark // automatically. We mark the block as parked at the very last minute so we // can make sure everything is ready to be reorged if needed. if (gArgs.GetBoolArg("-parkdeepreorg", true)) { const CBlockIndex *pindexFork = chainActive.FindFork(pindex); if (pindexFork && pindexFork->nHeight + 1 < pindex->nHeight) { LogPrintf("Park block %s as it would cause a deep reorg.\n", pindex->GetBlockHash().ToString()); pindex->nStatus = pindex->nStatus.withParked(); setDirtyBlockIndex.insert(pindex); } } // Header is valid/has work and the merkle tree is good. // Relay now, but if it does not build on our best tip, let the // SendMessages loop relay it. if (!IsInitialBlockDownload() && chainActive.Tip() == pindex->pprev) { GetMainSignals().NewPoWValidBlock(pindex, pblock); } // Write block to history file if (fNewBlock) { *fNewBlock = true; } try { FlatFilePos blockPos = SaveBlockToDisk(block, pindex->nHeight, chainparams, dbp); if (blockPos.IsNull()) { state.Error(strprintf( "%s: Failed to find position to write new block to disk", __func__)); return false; } if (!ReceivedBlockTransactions(block, state, pindex, blockPos)) { return error("AcceptBlock(): ReceivedBlockTransactions failed"); } } catch (const std::runtime_error &e) { return AbortNode(state, std::string("System error: ") + e.what()); } FlushStateToDisk(config.GetChainParams(), state, FlushStateMode::NONE); CheckBlockIndex(chainparams.GetConsensus()); return true; } bool ProcessNewBlock(const Config &config, const std::shared_ptr<const CBlock> pblock, bool fForceProcessing, bool *fNewBlock) { AssertLockNotHeld(cs_main); { if (fNewBlock) { *fNewBlock = false; } CValidationState state; // CheckBlock() does not support multi-threaded block validation // because CBlock::fChecked can cause data race. // Therefore, the following critical section must include the // CheckBlock() call as well. LOCK(cs_main); // Ensure that CheckBlock() passes before calling AcceptBlock, as // belt-and-suspenders. bool ret = CheckBlock(*pblock, state, config.GetChainParams().GetConsensus(), BlockValidationOptions(config)); if (ret) { // Store to disk ret = g_chainstate.AcceptBlock( config, pblock, state, fForceProcessing, nullptr, fNewBlock); } if (!ret) { GetMainSignals().BlockChecked(*pblock, state); return error("%s: AcceptBlock FAILED (%s)", __func__, FormatStateMessage(state)); } } NotifyHeaderTip(); // Only used to report errors, not invalidity - ignore it CValidationState state; if (!g_chainstate.ActivateBestChain(config, state, pblock)) { return error("%s: ActivateBestChain failed (%s)", __func__, FormatStateMessage(state)); } return true; } bool TestBlockValidity(CValidationState &state, const CChainParams ¶ms, const CBlock &block, CBlockIndex *pindexPrev, BlockValidationOptions validationOptions) { AssertLockHeld(cs_main); assert(pindexPrev && pindexPrev == chainActive.Tip()); CCoinsViewCache viewNew(pcoinsTip.get()); uint256 block_hash(block.GetHash()); CBlockIndex indexDummy(block); indexDummy.pprev = pindexPrev; indexDummy.nHeight = pindexPrev->nHeight + 1; indexDummy.phashBlock = &block_hash; // NOTE: CheckBlockHeader is called by CheckBlock if (!ContextualCheckBlockHeader(params, block, state, pindexPrev, GetAdjustedTime())) { return error("%s: Consensus::ContextualCheckBlockHeader: %s", __func__, FormatStateMessage(state)); } if (!CheckBlock(block, state, params.GetConsensus(), validationOptions)) { return error("%s: Consensus::CheckBlock: %s", __func__, FormatStateMessage(state)); } if (!ContextualCheckBlock(block, state, params.GetConsensus(), pindexPrev)) { return error("%s: Consensus::ContextualCheckBlock: %s", __func__, FormatStateMessage(state)); } if (!g_chainstate.ConnectBlock(block, state, &indexDummy, viewNew, params, validationOptions, true)) { return false; } assert(state.IsValid()); return true; } /** * BLOCK PRUNING CODE */ /** * Calculate the amount of disk space the block & undo files currently use. */ uint64_t CalculateCurrentUsage() { LOCK(cs_LastBlockFile); uint64_t retval = 0; for (const CBlockFileInfo &file : vinfoBlockFile) { retval += file.nSize + file.nUndoSize; } return retval; } /** * Prune a block file (modify associated database entries) */ void PruneOneBlockFile(const int fileNumber) { LOCK(cs_LastBlockFile); for (const auto &entry : mapBlockIndex) { CBlockIndex *pindex = entry.second; if (pindex->nFile == fileNumber) { pindex->nStatus = pindex->nStatus.withData(false).withUndo(false); pindex->nFile = 0; pindex->nDataPos = 0; pindex->nUndoPos = 0; setDirtyBlockIndex.insert(pindex); // Prune from mapBlocksUnlinked -- any block we prune would have // to be downloaded again in order to consider its chain, at which // point it would be considered as a candidate for // mapBlocksUnlinked or setBlockIndexCandidates. std::pair<std::multimap<CBlockIndex *, CBlockIndex *>::iterator, std::multimap<CBlockIndex *, CBlockIndex *>::iterator> range = mapBlocksUnlinked.equal_range(pindex->pprev); while (range.first != range.second) { std::multimap<CBlockIndex *, CBlockIndex *>::iterator _it = range.first; range.first++; if (_it->second == pindex) { mapBlocksUnlinked.erase(_it); } } } } vinfoBlockFile[fileNumber].SetNull(); setDirtyFileInfo.insert(fileNumber); } void UnlinkPrunedFiles(const std::set<int> &setFilesToPrune) { for (const int i : setFilesToPrune) { FlatFilePos pos(i, 0); fs::remove(BlockFileSeq().FileName(pos)); fs::remove(UndoFileSeq().FileName(pos)); LogPrintf("Prune: %s deleted blk/rev (%05u)\n", __func__, i); } } /** * Calculate the block/rev files to delete based on height specified by user * with RPC command pruneblockchain */ static void FindFilesToPruneManual(std::set<int> &setFilesToPrune, int nManualPruneHeight) { assert(fPruneMode && nManualPruneHeight > 0); LOCK2(cs_main, cs_LastBlockFile); if (chainActive.Tip() == nullptr) { return; } // last block to prune is the lesser of (user-specified height, // MIN_BLOCKS_TO_KEEP from the tip) unsigned int nLastBlockWeCanPrune = std::min((unsigned)nManualPruneHeight, chainActive.Tip()->nHeight - MIN_BLOCKS_TO_KEEP); int count = 0; for (int fileNumber = 0; fileNumber < nLastBlockFile; fileNumber++) { if (vinfoBlockFile[fileNumber].nSize == 0 || vinfoBlockFile[fileNumber].nHeightLast > nLastBlockWeCanPrune) { continue; } PruneOneBlockFile(fileNumber); setFilesToPrune.insert(fileNumber); count++; } LogPrintf("Prune (Manual): prune_height=%d removed %d blk/rev pairs\n", nLastBlockWeCanPrune, count); } /* This function is called from the RPC code for pruneblockchain */ void PruneBlockFilesManual(int nManualPruneHeight) { CValidationState state; const CChainParams &chainparams = Params(); if (!FlushStateToDisk(chainparams, state, FlushStateMode::NONE, nManualPruneHeight)) { LogPrintf("%s: failed to flush state (%s)\n", __func__, FormatStateMessage(state)); } } /** * Prune block and undo files (blk???.dat and undo???.dat) so that the disk * space used is less than a user-defined target. The user sets the target (in * MB) on the command line or in config file. This will be run on startup and * whenever new space is allocated in a block or undo file, staying below the * target. Changing back to unpruned requires a reindex (which in this case * means the blockchain must be re-downloaded.) * * Pruning functions are called from FlushStateToDisk when the global * fCheckForPruning flag has been set. Block and undo files are deleted in * lock-step (when blk00003.dat is deleted, so is rev00003.dat.). Pruning cannot * take place until the longest chain is at least a certain length (100000 on * mainnet, 1000 on testnet, 1000 on regtest). Pruning will never delete a block * within a defined distance (currently 288) from the active chain's tip. The * block index is updated by unsetting HAVE_DATA and HAVE_UNDO for any blocks * that were stored in the deleted files. A db flag records the fact that at * least some block files have been pruned. * * @param[out] setFilesToPrune The set of file indices that can be unlinked * will be returned */ static void FindFilesToPrune(std::set<int> &setFilesToPrune, uint64_t nPruneAfterHeight) { LOCK2(cs_main, cs_LastBlockFile); if (chainActive.Tip() == nullptr || nPruneTarget == 0) { return; } if (uint64_t(chainActive.Tip()->nHeight) <= nPruneAfterHeight) { return; } unsigned int nLastBlockWeCanPrune = chainActive.Tip()->nHeight - MIN_BLOCKS_TO_KEEP; uint64_t nCurrentUsage = CalculateCurrentUsage(); // We don't check to prune until after we've allocated new space for files, // so we should leave a buffer under our target to account for another // allocation before the next pruning. uint64_t nBuffer = BLOCKFILE_CHUNK_SIZE + UNDOFILE_CHUNK_SIZE; uint64_t nBytesToPrune; int count = 0; if (nCurrentUsage + nBuffer >= nPruneTarget) { // On a prune event, the chainstate DB is flushed. // To avoid excessive prune events negating the benefit of high dbcache // values, we should not prune too rapidly. // So when pruning in IBD, increase the buffer a bit to avoid a re-prune // too soon. if (IsInitialBlockDownload()) { // Since this is only relevant during IBD, we use a fixed 10% nBuffer += nPruneTarget / 10; } for (int fileNumber = 0; fileNumber < nLastBlockFile; fileNumber++) { nBytesToPrune = vinfoBlockFile[fileNumber].nSize + vinfoBlockFile[fileNumber].nUndoSize; if (vinfoBlockFile[fileNumber].nSize == 0) { continue; } // are we below our target? if (nCurrentUsage + nBuffer < nPruneTarget) { break; } // don't prune files that could have a block within // MIN_BLOCKS_TO_KEEP of the main chain's tip but keep scanning if (vinfoBlockFile[fileNumber].nHeightLast > nLastBlockWeCanPrune) { continue; } PruneOneBlockFile(fileNumber); // Queue up the files for removal setFilesToPrune.insert(fileNumber); nCurrentUsage -= nBytesToPrune; count++; } } LogPrint(BCLog::PRUNE, "Prune: target=%dMiB actual=%dMiB diff=%dMiB " "max_prune_height=%d removed %d blk/rev pairs\n", nPruneTarget / 1024 / 1024, nCurrentUsage / 1024 / 1024, ((int64_t)nPruneTarget - (int64_t)nCurrentUsage) / 1024 / 1024, nLastBlockWeCanPrune, count); } static FlatFileSeq BlockFileSeq() { return FlatFileSeq(GetBlocksDir(), "blk", BLOCKFILE_CHUNK_SIZE); } static FlatFileSeq UndoFileSeq() { return FlatFileSeq(GetBlocksDir(), "rev", UNDOFILE_CHUNK_SIZE); } FILE *OpenBlockFile(const FlatFilePos &pos, bool fReadOnly) { return BlockFileSeq().Open(pos, fReadOnly); } /** Open an undo file (rev?????.dat) */ static FILE *OpenUndoFile(const FlatFilePos &pos, bool fReadOnly) { return UndoFileSeq().Open(pos, fReadOnly); } fs::path GetBlockPosFilename(const FlatFilePos &pos) { return BlockFileSeq().FileName(pos); } CBlockIndex *CChainState::InsertBlockIndex(const uint256 &hash) { AssertLockHeld(cs_main); if (hash.IsNull()) { return nullptr; } // Return existing BlockMap::iterator mi = mapBlockIndex.find(hash); if (mi != mapBlockIndex.end()) { return (*mi).second; } // Create new CBlockIndex *pindexNew = new CBlockIndex(); mi = mapBlockIndex.insert(std::make_pair(hash, pindexNew)).first; pindexNew->phashBlock = &((*mi).first); return pindexNew; } bool CChainState::LoadBlockIndex(const Config &config, CBlockTreeDB &blocktree) { if (!blocktree.LoadBlockIndexGuts(config.GetChainParams().GetConsensus(), [this](const uint256 &hash) { return this->InsertBlockIndex(hash); })) { return false; } boost::this_thread::interruption_point(); // Calculate nChainWork std::vector<std::pair<int, CBlockIndex *>> vSortedByHeight; vSortedByHeight.reserve(mapBlockIndex.size()); for (const std::pair<const uint256, CBlockIndex *> &item : mapBlockIndex) { CBlockIndex *pindex = item.second; vSortedByHeight.push_back(std::make_pair(pindex->nHeight, pindex)); } sort(vSortedByHeight.begin(), vSortedByHeight.end()); for (const std::pair<int, CBlockIndex *> &item : vSortedByHeight) { CBlockIndex *pindex = item.second; pindex->nChainWork = (pindex->pprev ? pindex->pprev->nChainWork : 0) + GetBlockProof(*pindex); pindex->nTimeMax = (pindex->pprev ? std::max(pindex->pprev->nTimeMax, pindex->nTime) : pindex->nTime); // We can link the chain of blocks for which we've received transactions // at some point. Pruned nodes may have deleted the block. if (pindex->nTx > 0) { if (pindex->pprev) { if (pindex->pprev->nChainTx) { pindex->nChainTx = pindex->pprev->nChainTx + pindex->nTx; } else { pindex->nChainTx = 0; mapBlocksUnlinked.insert( std::make_pair(pindex->pprev, pindex)); } } else { pindex->nChainTx = pindex->nTx; } } if (!pindex->nStatus.hasFailed() && pindex->pprev && pindex->pprev->nStatus.hasFailed()) { pindex->nStatus = pindex->nStatus.withFailedParent(); setDirtyBlockIndex.insert(pindex); } if (pindex->IsValid(BlockValidity::TRANSACTIONS) && (pindex->nChainTx || pindex->pprev == nullptr)) { setBlockIndexCandidates.insert(pindex); } if (pindex->nStatus.isInvalid() && (!pindexBestInvalid || pindex->nChainWork > pindexBestInvalid->nChainWork)) { pindexBestInvalid = pindex; } if (pindex->nStatus.isOnParkedChain() && (!pindexBestParked || pindex->nChainWork > pindexBestParked->nChainWork)) { pindexBestParked = pindex; } if (pindex->pprev) { pindex->BuildSkip(); } if (pindex->IsValid(BlockValidity::TREE) && (pindexBestHeader == nullptr || CBlockIndexWorkComparator()(pindexBestHeader, pindex))) { pindexBestHeader = pindex; } } return true; } bool static LoadBlockIndexDB(const Config &config) { if (!g_chainstate.LoadBlockIndex(config, *pblocktree)) { return false; } // Load block file info pblocktree->ReadLastBlockFile(nLastBlockFile); vinfoBlockFile.resize(nLastBlockFile + 1); LogPrintf("%s: last block file = %i\n", __func__, nLastBlockFile); for (int nFile = 0; nFile <= nLastBlockFile; nFile++) { pblocktree->ReadBlockFileInfo(nFile, vinfoBlockFile[nFile]); } LogPrintf("%s: last block file info: %s\n", __func__, vinfoBlockFile[nLastBlockFile].ToString()); for (int nFile = nLastBlockFile + 1; true; nFile++) { CBlockFileInfo info; if (pblocktree->ReadBlockFileInfo(nFile, info)) { vinfoBlockFile.push_back(info); } else { break; } } // Check presence of blk files LogPrintf("Checking all blk files are present...\n"); std::set<int> setBlkDataFiles; for (const std::pair<const uint256, CBlockIndex *> &item : mapBlockIndex) { CBlockIndex *pindex = item.second; if (pindex->nStatus.hasData()) { setBlkDataFiles.insert(pindex->nFile); } } for (const int i : setBlkDataFiles) { FlatFilePos pos(i, 0); if (CAutoFile(OpenBlockFile(pos, true), SER_DISK, CLIENT_VERSION) .IsNull()) { return false; } } // Check whether we have ever pruned block & undo files pblocktree->ReadFlag("prunedblockfiles", fHavePruned); if (fHavePruned) { LogPrintf( "LoadBlockIndexDB(): Block files have previously been pruned\n"); } // Check whether we need to continue reindexing bool fReindexing = false; pblocktree->ReadReindexing(fReindexing); if (fReindexing) { fReindex = true; } return true; } bool LoadChainTip(const Config &config) { AssertLockHeld(cs_main); if (chainActive.Tip() && chainActive.Tip()->GetBlockHash() == pcoinsTip->GetBestBlock()) { return true; } if (pcoinsTip->GetBestBlock().IsNull() && mapBlockIndex.size() == 1) { // In case we just added the genesis block, connect it now, so // that we always have a chainActive.Tip() when we return. LogPrintf("%s: Connecting genesis block...\n", __func__); CValidationState state; if (!ActivateBestChain(config, state)) { LogPrintf("%s: failed to activate chain (%s)\n", __func__, FormatStateMessage(state)); return false; } } // Load pointer to end of best chain CBlockIndex *pindex = LookupBlockIndex(pcoinsTip->GetBestBlock()); if (!pindex) { return false; } chainActive.SetTip(pindex); g_chainstate.PruneBlockIndexCandidates(); LogPrintf( "Loaded best chain: hashBestChain=%s height=%d date=%s progress=%f\n", chainActive.Tip()->GetBlockHash().ToString(), chainActive.Height(), FormatISO8601DateTime(chainActive.Tip()->GetBlockTime()), GuessVerificationProgress(config.GetChainParams().TxData(), chainActive.Tip())); return true; } CVerifyDB::CVerifyDB() { uiInterface.ShowProgress(_("Verifying blocks..."), 0, false); } CVerifyDB::~CVerifyDB() { uiInterface.ShowProgress("", 100, false); } bool CVerifyDB::VerifyDB(const Config &config, CCoinsView *coinsview, int nCheckLevel, int nCheckDepth) { LOCK(cs_main); const CChainParams ¶ms = config.GetChainParams(); const Consensus::Params &consensusParams = params.GetConsensus(); if (chainActive.Tip() == nullptr || chainActive.Tip()->pprev == nullptr) { return true; } // Verify blocks in the best chain if (nCheckDepth <= 0 || nCheckDepth > chainActive.Height()) { nCheckDepth = chainActive.Height(); } nCheckLevel = std::max(0, std::min(4, nCheckLevel)); LogPrintf("Verifying last %i blocks at level %i\n", nCheckDepth, nCheckLevel); CCoinsViewCache coins(coinsview); CBlockIndex *pindex; CBlockIndex *pindexFailure = nullptr; int nGoodTransactions = 0; CValidationState state; int reportDone = 0; LogPrintfToBeContinued("[0%%]..."); for (pindex = chainActive.Tip(); pindex && pindex->pprev; pindex = pindex->pprev) { boost::this_thread::interruption_point(); int percentageDone = std::max( 1, std::min( 99, (int)(((double)(chainActive.Height() - pindex->nHeight)) / (double)nCheckDepth * (nCheckLevel >= 4 ? 50 : 100)))); if (reportDone < percentageDone / 10) { // report every 10% step LogPrintfToBeContinued("[%d%%]...", percentageDone); reportDone = percentageDone / 10; } uiInterface.ShowProgress(_("Verifying blocks..."), percentageDone, false); if (pindex->nHeight <= chainActive.Height() - nCheckDepth) { break; } if (fPruneMode && !pindex->nStatus.hasData()) { // If pruning, only go back as far as we have data. LogPrintf("VerifyDB(): block verification stopping at height %d " "(pruning, no data)\n", pindex->nHeight); break; } CBlock block; // check level 0: read from disk if (!ReadBlockFromDisk(block, pindex, consensusParams)) { return error( "VerifyDB(): *** ReadBlockFromDisk failed at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString()); } // check level 1: verify block validity if (nCheckLevel >= 1 && !CheckBlock(block, state, consensusParams, BlockValidationOptions(config))) { return error("%s: *** found bad block at %d, hash=%s (%s)\n", __func__, pindex->nHeight, pindex->GetBlockHash().ToString(), FormatStateMessage(state)); } // check level 2: verify undo validity if (nCheckLevel >= 2 && pindex) { CBlockUndo undo; if (!pindex->GetUndoPos().IsNull()) { if (!UndoReadFromDisk(undo, pindex)) { return error( "VerifyDB(): *** found bad undo data at %d, hash=%s\n", pindex->nHeight, pindex->GetBlockHash().ToString()); } } } // check level 3: check for inconsistencies during memory-only // disconnect of tip blocks if (nCheckLevel >= 3 && (coins.DynamicMemoryUsage() + pcoinsTip->DynamicMemoryUsage()) <= nCoinCacheUsage) { assert(coins.GetBestBlock() == pindex->GetBlockHash()); DisconnectResult res = g_chainstate.DisconnectBlock(block, pindex, coins); if (res == DISCONNECT_FAILED) { return error("VerifyDB(): *** irrecoverable inconsistency in " "block data at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString()); } if (res == DISCONNECT_UNCLEAN) { nGoodTransactions = 0; pindexFailure = pindex; } else { nGoodTransactions += block.vtx.size(); } } if (ShutdownRequested()) { return true; } } if (pindexFailure) { return error("VerifyDB(): *** coin database inconsistencies found " "(last %i blocks, %i good transactions before that)\n", chainActive.Height() - pindexFailure->nHeight + 1, nGoodTransactions); } // store block count as we move pindex at check level >= 4 int block_count = chainActive.Height() - pindex->nHeight; // check level 4: try reconnecting blocks if (nCheckLevel >= 4) { while (pindex != chainActive.Tip()) { boost::this_thread::interruption_point(); uiInterface.ShowProgress( _("Verifying blocks..."), std::max( 1, std::min(99, 100 - (int)(((double)(chainActive.Height() - pindex->nHeight)) / (double)nCheckDepth * 50))), false); pindex = chainActive.Next(pindex); CBlock block; if (!ReadBlockFromDisk(block, pindex, consensusParams)) { return error( "VerifyDB(): *** ReadBlockFromDisk failed at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString()); } if (!g_chainstate.ConnectBlock(block, state, pindex, coins, params, BlockValidationOptions(config))) { return error("VerifyDB(): *** found unconnectable block at %d, " "hash=%s (%s)", pindex->nHeight, pindex->GetBlockHash().ToString(), FormatStateMessage(state)); } } } LogPrintf("[DONE].\n"); LogPrintf("No coin database inconsistencies in last %i blocks (%i " "transactions)\n", block_count, nGoodTransactions); return true; } /** * Apply the effects of a block on the utxo cache, ignoring that it may already * have been applied. */ bool CChainState::RollforwardBlock(const CBlockIndex *pindex, CCoinsViewCache &view, const Consensus::Params ¶ms) { // TODO: merge with ConnectBlock CBlock block; if (!ReadBlockFromDisk(block, pindex, params)) { return error("ReplayBlock(): ReadBlockFromDisk failed at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString()); } for (const CTransactionRef &tx : block.vtx) { // Pass check = true as every addition may be an overwrite. AddCoins(view, *tx, pindex->nHeight, true); } for (const CTransactionRef &tx : block.vtx) { if (tx->IsCoinBase()) { continue; } for (const CTxIn &txin : tx->vin) { view.SpendCoin(txin.prevout); } } return true; } bool CChainState::ReplayBlocks(const Consensus::Params ¶ms, CCoinsView *view) { LOCK(cs_main); CCoinsViewCache cache(view); std::vector<uint256> hashHeads = view->GetHeadBlocks(); if (hashHeads.empty()) { // We're already in a consistent state. return true; } if (hashHeads.size() != 2) { return error("ReplayBlocks(): unknown inconsistent state"); } uiInterface.ShowProgress(_("Replaying blocks..."), 0, false); LogPrintf("Replaying blocks\n"); // Old tip during the interrupted flush. const CBlockIndex *pindexOld = nullptr; // New tip during the interrupted flush. const CBlockIndex *pindexNew; // Latest block common to both the old and the new tip. const CBlockIndex *pindexFork = nullptr; if (mapBlockIndex.count(hashHeads[0]) == 0) { return error( "ReplayBlocks(): reorganization to unknown block requested"); } pindexNew = mapBlockIndex[hashHeads[0]]; if (!hashHeads[1].IsNull()) { // The old tip is allowed to be 0, indicating it's the first flush. if (mapBlockIndex.count(hashHeads[1]) == 0) { return error( "ReplayBlocks(): reorganization from unknown block requested"); } pindexOld = mapBlockIndex[hashHeads[1]]; pindexFork = LastCommonAncestor(pindexOld, pindexNew); assert(pindexFork != nullptr); } // Rollback along the old branch. while (pindexOld != pindexFork) { if (pindexOld->nHeight > 0) { // Never disconnect the genesis block. CBlock block; if (!ReadBlockFromDisk(block, pindexOld, params)) { return error("RollbackBlock(): ReadBlockFromDisk() failed at " "%d, hash=%s", pindexOld->nHeight, pindexOld->GetBlockHash().ToString()); } LogPrintf("Rolling back %s (%i)\n", pindexOld->GetBlockHash().ToString(), pindexOld->nHeight); DisconnectResult res = DisconnectBlock(block, pindexOld, cache); if (res == DISCONNECT_FAILED) { return error( "RollbackBlock(): DisconnectBlock failed at %d, hash=%s", pindexOld->nHeight, pindexOld->GetBlockHash().ToString()); } // If DISCONNECT_UNCLEAN is returned, it means a non-existing UTXO // was deleted, or an existing UTXO was overwritten. It corresponds // to cases where the block-to-be-disconnect never had all its // operations applied to the UTXO set. However, as both writing a // UTXO and deleting a UTXO are idempotent operations, the result is // still a version of the UTXO set with the effects of that block // undone. } pindexOld = pindexOld->pprev; } // Roll forward from the forking point to the new tip. int nForkHeight = pindexFork ? pindexFork->nHeight : 0; for (int nHeight = nForkHeight + 1; nHeight <= pindexNew->nHeight; ++nHeight) { const CBlockIndex *pindex = pindexNew->GetAncestor(nHeight); LogPrintf("Rolling forward %s (%i)\n", pindex->GetBlockHash().ToString(), nHeight); if (!RollforwardBlock(pindex, cache, params)) { return false; } } cache.SetBestBlock(pindexNew->GetBlockHash()); cache.Flush(); uiInterface.ShowProgress("", 100, false); return true; } bool ReplayBlocks(const Consensus::Params ¶ms, CCoinsView *view) { return g_chainstate.ReplayBlocks(params, view); } bool CChainState::RewindBlockIndex(const Config &config) { LOCK(cs_main); const CChainParams ¶ms = config.GetChainParams(); int nHeight = chainActive.Height() + 1; // nHeight is now the height of the first insufficiently-validated block, or // tipheight + 1 CValidationState state; CBlockIndex *pindex = chainActive.Tip(); while (chainActive.Height() >= nHeight) { if (fPruneMode && !chainActive.Tip()->nStatus.hasData()) { // If pruning, don't try rewinding past the HAVE_DATA point; since // older blocks can't be served anyway, there's no need to walk // further, and trying to DisconnectTip() will fail (and require a // needless reindex/redownload of the blockchain). break; } if (!DisconnectTip(config, state, nullptr)) { return error("RewindBlockIndex: unable to disconnect block at " "height %i (%s)", pindex->nHeight, FormatStateMessage(state)); } // Occasionally flush state to disk. if (!FlushStateToDisk(params, state, FlushStateMode::PERIODIC)) { LogPrintf("RewindBlockIndex: unable to flush state to disk (%s)\n", FormatStateMessage(state)); return false; } } // Reduce validity flag and have-data flags. // We do this after actual disconnecting, otherwise we'll end up writing the // lack of data to disk before writing the chainstate, resulting in a // failure to continue if interrupted. for (const auto &entry : mapBlockIndex) { CBlockIndex *pindexIter = entry.second; if (pindexIter->IsValid(BlockValidity::TRANSACTIONS) && pindexIter->nChainTx) { setBlockIndexCandidates.insert(pindexIter); } } if (chainActive.Tip() != nullptr) { // We can't prune block index candidates based on our tip if we have // no tip due to chainActive being empty! PruneBlockIndexCandidates(); CheckBlockIndex(params.GetConsensus()); } return true; } bool RewindBlockIndex(const Config &config) { if (!g_chainstate.RewindBlockIndex(config)) { return false; } if (chainActive.Tip() != nullptr) { // FlushStateToDisk can possibly read chainActive. Be conservative // and skip it here, we're about to -reindex-chainstate anyway, so // it'll get called a bunch real soon. CValidationState state; if (!FlushStateToDisk(config.GetChainParams(), state, FlushStateMode::ALWAYS)) { LogPrintf("RewindBlockIndex: unable to flush state to disk (%s)\n", FormatStateMessage(state)); return false; } } return true; } // May NOT be used after any connections are up as much of the peer-processing // logic assumes a consistent block index state void CChainState::UnloadBlockIndex() { nBlockSequenceId = 1; m_failed_blocks.clear(); setBlockIndexCandidates.clear(); } // May NOT be used after any connections are up as much // of the peer-processing logic assumes a consistent // block index state void UnloadBlockIndex() { LOCK(cs_main); chainActive.SetTip(nullptr); pindexFinalized = nullptr; pindexBestInvalid = nullptr; pindexBestParked = nullptr; pindexBestHeader = nullptr; pindexBestForkTip = nullptr; pindexBestForkBase = nullptr; g_mempool.clear(); mapBlocksUnlinked.clear(); vinfoBlockFile.clear(); nLastBlockFile = 0; setDirtyBlockIndex.clear(); setDirtyFileInfo.clear(); for (BlockMap::value_type &entry : mapBlockIndex) { delete entry.second; } mapBlockIndex.clear(); fHavePruned = false; g_chainstate.UnloadBlockIndex(); } bool LoadBlockIndex(const Config &config) { // Load block index from databases bool needs_init = fReindex; if (!fReindex) { bool ret = LoadBlockIndexDB(config); if (!ret) { return false; } needs_init = mapBlockIndex.empty(); } if (needs_init) { // Everything here is for *new* reindex/DBs. Thus, though // LoadBlockIndexDB may have set fReindex if we shut down // mid-reindex previously, we don't check fReindex and // instead only check it prior to LoadBlockIndexDB to set // needs_init. LogPrintf("Initializing databases...\n"); } return true; } bool CChainState::LoadGenesisBlock(const CChainParams &chainparams) { LOCK(cs_main); // Check whether we're already initialized by checking for genesis in // mapBlockIndex. Note that we can't use chainActive here, since it is // set based on the coins db, not the block index db, which is the only // thing loaded at this point. if (mapBlockIndex.count(chainparams.GenesisBlock().GetHash())) { return true; } try { CBlock &block = const_cast<CBlock &>(chainparams.GenesisBlock()); FlatFilePos blockPos = SaveBlockToDisk(block, 0, chainparams, nullptr); if (blockPos.IsNull()) { return error("%s: writing genesis block to disk failed", __func__); } CBlockIndex *pindex = AddToBlockIndex(block); CValidationState state; if (!ReceivedBlockTransactions(block, state, pindex, blockPos)) { return error("%s: genesis block not accepted (%s)", __func__, FormatStateMessage(state)); } } catch (const std::runtime_error &e) { return error("%s: failed to write genesis block: %s", __func__, e.what()); } return true; } bool LoadGenesisBlock(const CChainParams &chainparams) { return g_chainstate.LoadGenesisBlock(chainparams); } bool LoadExternalBlockFile(const Config &config, FILE *fileIn, FlatFilePos *dbp) { // Map of disk positions for blocks with unknown parent (only used for // reindex) static std::multimap<uint256, FlatFilePos> mapBlocksUnknownParent; int64_t nStart = GetTimeMillis(); const CChainParams &chainparams = config.GetChainParams(); int nLoaded = 0; try { // This takes over fileIn and calls fclose() on it in the CBufferedFile // destructor. Make sure we have at least 2*MAX_TX_SIZE space in there // so any transaction can fit in the buffer. CBufferedFile blkdat(fileIn, 2 * MAX_TX_SIZE, MAX_TX_SIZE + 8, SER_DISK, CLIENT_VERSION); uint64_t nRewind = blkdat.GetPos(); while (!blkdat.eof()) { boost::this_thread::interruption_point(); blkdat.SetPos(nRewind); // Start one byte further next time, in case of failure. nRewind++; // Remove former limit. blkdat.SetLimit(); unsigned int nSize = 0; try { // Locate a header. uint8_t buf[CMessageHeader::MESSAGE_START_SIZE]; blkdat.FindByte(chainparams.DiskMagic()[0]); nRewind = blkdat.GetPos() + 1; - blkdat >> FLATDATA(buf); - if (memcmp(buf, std::begin(chainparams.DiskMagic()), + blkdat >> buf; + if (memcmp(buf, chainparams.DiskMagic().data(), CMessageHeader::MESSAGE_START_SIZE)) { continue; } // Read size. blkdat >> nSize; if (nSize < 80) { continue; } } catch (const std::exception &) { // No valid block header found; don't complain. break; } try { // read block uint64_t nBlockPos = blkdat.GetPos(); if (dbp) { dbp->nPos = nBlockPos; } blkdat.SetLimit(nBlockPos + nSize); blkdat.SetPos(nBlockPos); std::shared_ptr<CBlock> pblock = std::make_shared<CBlock>(); CBlock &block = *pblock; blkdat >> block; nRewind = blkdat.GetPos(); uint256 hash = block.GetHash(); { LOCK(cs_main); // detect out of order blocks, and store them for later if (hash != chainparams.GetConsensus().hashGenesisBlock && !LookupBlockIndex(block.hashPrevBlock)) { LogPrint( BCLog::REINDEX, "%s: Out of order block %s, parent %s not known\n", __func__, hash.ToString(), block.hashPrevBlock.ToString()); if (dbp) { mapBlocksUnknownParent.insert( std::make_pair(block.hashPrevBlock, *dbp)); } continue; } // process in case the block isn't known yet CBlockIndex *pindex = LookupBlockIndex(hash); if (!pindex || !pindex->nStatus.hasData()) { CValidationState state; if (g_chainstate.AcceptBlock(config, pblock, state, true, dbp, nullptr)) { nLoaded++; } if (state.IsError()) { break; } } else if (hash != chainparams.GetConsensus() .hashGenesisBlock && pindex->nHeight % 1000 == 0) { LogPrint( BCLog::REINDEX, "Block Import: already had block %s at height %d\n", hash.ToString(), pindex->nHeight); } } // Activate the genesis block so normal node progress can // continue if (hash == chainparams.GetConsensus().hashGenesisBlock) { CValidationState state; if (!ActivateBestChain(config, state)) { break; } } NotifyHeaderTip(); // Recursively process earlier encountered successors of this // block std::deque<uint256> queue; queue.push_back(hash); while (!queue.empty()) { uint256 head = queue.front(); queue.pop_front(); std::pair<std::multimap<uint256, FlatFilePos>::iterator, std::multimap<uint256, FlatFilePos>::iterator> range = mapBlocksUnknownParent.equal_range(head); while (range.first != range.second) { std::multimap<uint256, FlatFilePos>::iterator it = range.first; std::shared_ptr<CBlock> pblockrecursive = std::make_shared<CBlock>(); if (ReadBlockFromDisk(*pblockrecursive, it->second, chainparams.GetConsensus())) { LogPrint( BCLog::REINDEX, "%s: Processing out of order child %s of %s\n", __func__, pblockrecursive->GetHash().ToString(), head.ToString()); LOCK(cs_main); CValidationState dummy; if (g_chainstate.AcceptBlock( config, pblockrecursive, dummy, true, &it->second, nullptr)) { nLoaded++; queue.push_back(pblockrecursive->GetHash()); } } range.first++; mapBlocksUnknownParent.erase(it); NotifyHeaderTip(); } } } catch (const std::exception &e) { LogPrintf("%s: Deserialize or I/O error - %s\n", __func__, e.what()); } } } catch (const std::runtime_error &e) { AbortNode(std::string("System error: ") + e.what()); } if (nLoaded > 0) { LogPrintf("Loaded %i blocks from external file in %dms\n", nLoaded, GetTimeMillis() - nStart); } return nLoaded > 0; } void CChainState::CheckBlockIndex(const Consensus::Params &consensusParams) { if (!fCheckBlockIndex) { return; } LOCK(cs_main); // During a reindex, we read the genesis block and call CheckBlockIndex // before ActivateBestChain, so we have the genesis block in mapBlockIndex // but no active chain. (A few of the tests when iterating the block tree // require that chainActive has been initialized.) if (chainActive.Height() < 0) { assert(mapBlockIndex.size() <= 1); return; } // Build forward-pointing map of the entire block tree. std::multimap<CBlockIndex *, CBlockIndex *> forward; for (auto &entry : mapBlockIndex) { forward.emplace(entry.second->pprev, entry.second); } assert(forward.size() == mapBlockIndex.size()); std::pair<std::multimap<CBlockIndex *, CBlockIndex *>::iterator, std::multimap<CBlockIndex *, CBlockIndex *>::iterator> rangeGenesis = forward.equal_range(nullptr); CBlockIndex *pindex = rangeGenesis.first->second; rangeGenesis.first++; // There is only one index entry with parent nullptr. assert(rangeGenesis.first == rangeGenesis.second); // Iterate over the entire block tree, using depth-first search. // Along the way, remember whether there are blocks on the path from genesis // block being explored which are the first to have certain properties. size_t nNodes = 0; int nHeight = 0; // Oldest ancestor of pindex which is invalid. CBlockIndex *pindexFirstInvalid = nullptr; // Oldest ancestor of pindex which is parked. CBlockIndex *pindexFirstParked = nullptr; // Oldest ancestor of pindex which does not have data available. CBlockIndex *pindexFirstMissing = nullptr; // Oldest ancestor of pindex for which nTx == 0. CBlockIndex *pindexFirstNeverProcessed = nullptr; // Oldest ancestor of pindex which does not have BLOCK_VALID_TREE // (regardless of being valid or not). CBlockIndex *pindexFirstNotTreeValid = nullptr; // Oldest ancestor of pindex which does not have BLOCK_VALID_TRANSACTIONS // (regardless of being valid or not). CBlockIndex *pindexFirstNotTransactionsValid = nullptr; // Oldest ancestor of pindex which does not have BLOCK_VALID_CHAIN // (regardless of being valid or not). CBlockIndex *pindexFirstNotChainValid = nullptr; // Oldest ancestor of pindex which does not have BLOCK_VALID_SCRIPTS // (regardless of being valid or not). CBlockIndex *pindexFirstNotScriptsValid = nullptr; while (pindex != nullptr) { nNodes++; if (pindexFirstInvalid == nullptr && pindex->nStatus.hasFailed()) { pindexFirstInvalid = pindex; } if (pindexFirstParked == nullptr && pindex->nStatus.isParked()) { pindexFirstParked = pindex; } if (pindexFirstMissing == nullptr && !pindex->nStatus.hasData()) { pindexFirstMissing = pindex; } if (pindexFirstNeverProcessed == nullptr && pindex->nTx == 0) { pindexFirstNeverProcessed = pindex; } if (pindex->pprev != nullptr && pindexFirstNotTreeValid == nullptr && pindex->nStatus.getValidity() < BlockValidity::TREE) { pindexFirstNotTreeValid = pindex; } if (pindex->pprev != nullptr && pindexFirstNotTransactionsValid == nullptr && pindex->nStatus.getValidity() < BlockValidity::TRANSACTIONS) { pindexFirstNotTransactionsValid = pindex; } if (pindex->pprev != nullptr && pindexFirstNotChainValid == nullptr && pindex->nStatus.getValidity() < BlockValidity::CHAIN) { pindexFirstNotChainValid = pindex; } if (pindex->pprev != nullptr && pindexFirstNotScriptsValid == nullptr && pindex->nStatus.getValidity() < BlockValidity::SCRIPTS) { pindexFirstNotScriptsValid = pindex; } // Begin: actual consistency checks. if (pindex->pprev == nullptr) { // Genesis block checks. // Genesis block's hash must match. assert(pindex->GetBlockHash() == consensusParams.hashGenesisBlock); // The current active chain's genesis block must be this block. assert(pindex == chainActive.Genesis()); } if (pindex->nChainTx == 0) { // nSequenceId can't be set positive for blocks that aren't linked // (negative is used for preciousblock) assert(pindex->nSequenceId <= 0); } // VALID_TRANSACTIONS is equivalent to nTx > 0 for all nodes (whether or // not pruning has occurred). HAVE_DATA is only equivalent to nTx > 0 // (or VALID_TRANSACTIONS) if no pruning has occurred. if (!fHavePruned) { // If we've never pruned, then HAVE_DATA should be equivalent to nTx // > 0 assert(pindex->nStatus.hasData() == (pindex->nTx > 0)); assert(pindexFirstMissing == pindexFirstNeverProcessed); } else if (pindex->nStatus.hasData()) { // If we have pruned, then we can only say that HAVE_DATA implies // nTx > 0 assert(pindex->nTx > 0); } if (pindex->nStatus.hasUndo()) { assert(pindex->nStatus.hasData()); } // This is pruning-independent. assert((pindex->nStatus.getValidity() >= BlockValidity::TRANSACTIONS) == (pindex->nTx > 0)); // All parents having had data (at some point) is equivalent to all // parents being VALID_TRANSACTIONS, which is equivalent to nChainTx // being set. // nChainTx != 0 is used to signal that all parent blocks have been // processed (but may have been pruned). assert((pindexFirstNeverProcessed != nullptr) == (pindex->nChainTx == 0)); assert((pindexFirstNotTransactionsValid != nullptr) == (pindex->nChainTx == 0)); // nHeight must be consistent. assert(pindex->nHeight == nHeight); // For every block except the genesis block, the chainwork must be // larger than the parent's. assert(pindex->pprev == nullptr || pindex->nChainWork >= pindex->pprev->nChainWork); // The pskip pointer must point back for all but the first 2 blocks. assert(nHeight < 2 || (pindex->pskip && (pindex->pskip->nHeight < nHeight))); // All mapBlockIndex entries must at least be TREE valid assert(pindexFirstNotTreeValid == nullptr); if (pindex->nStatus.getValidity() >= BlockValidity::TREE) { // TREE valid implies all parents are TREE valid assert(pindexFirstNotTreeValid == nullptr); } if (pindex->nStatus.getValidity() >= BlockValidity::CHAIN) { // CHAIN valid implies all parents are CHAIN valid assert(pindexFirstNotChainValid == nullptr); } if (pindex->nStatus.getValidity() >= BlockValidity::SCRIPTS) { // SCRIPTS valid implies all parents are SCRIPTS valid assert(pindexFirstNotScriptsValid == nullptr); } if (pindexFirstInvalid == nullptr) { // Checks for not-invalid blocks. // The failed mask cannot be set for blocks without invalid parents. assert(!pindex->nStatus.isInvalid()); } if (pindexFirstParked == nullptr) { // Checks for not-invalid blocks. // The failed mask cannot be set for blocks without invalid parents. assert(!pindex->nStatus.isOnParkedChain()); } if (!CBlockIndexWorkComparator()(pindex, chainActive.Tip()) && pindexFirstNeverProcessed == nullptr) { if (pindexFirstInvalid == nullptr) { // If this block sorts at least as good as the current tip and // is valid and we have all data for its parents, it must be in // setBlockIndexCandidates or be parked. if (pindexFirstMissing == nullptr) { assert(pindex->nStatus.isOnParkedChain() || setBlockIndexCandidates.count(pindex)); } // chainActive.Tip() must also be there even if some data has // been pruned. if (pindex == chainActive.Tip()) { assert(setBlockIndexCandidates.count(pindex)); } // If some parent is missing, then it could be that this block // was in setBlockIndexCandidates but had to be removed because // of the missing data. In this case it must be in // mapBlocksUnlinked -- see test below. } } else { // If this block sorts worse than the current tip or some ancestor's // block has never been seen, it cannot be in // setBlockIndexCandidates. assert(setBlockIndexCandidates.count(pindex) == 0); } // Check whether this block is in mapBlocksUnlinked. std::pair<std::multimap<CBlockIndex *, CBlockIndex *>::iterator, std::multimap<CBlockIndex *, CBlockIndex *>::iterator> rangeUnlinked = mapBlocksUnlinked.equal_range(pindex->pprev); bool foundInUnlinked = false; while (rangeUnlinked.first != rangeUnlinked.second) { assert(rangeUnlinked.first->first == pindex->pprev); if (rangeUnlinked.first->second == pindex) { foundInUnlinked = true; break; } rangeUnlinked.first++; } if (pindex->pprev && pindex->nStatus.hasData() && pindexFirstNeverProcessed != nullptr && pindexFirstInvalid == nullptr) { // If this block has block data available, some parent was never // received, and has no invalid parents, it must be in // mapBlocksUnlinked. assert(foundInUnlinked); } if (!pindex->nStatus.hasData()) { // Can't be in mapBlocksUnlinked if we don't HAVE_DATA assert(!foundInUnlinked); } if (pindexFirstMissing == nullptr) { // We aren't missing data for any parent -- cannot be in // mapBlocksUnlinked. assert(!foundInUnlinked); } if (pindex->pprev && pindex->nStatus.hasData() && pindexFirstNeverProcessed == nullptr && pindexFirstMissing != nullptr) { // We HAVE_DATA for this block, have received data for all parents // at some point, but we're currently missing data for some parent. // We must have pruned. assert(fHavePruned); // This block may have entered mapBlocksUnlinked if: // - it has a descendant that at some point had more work than the // tip, and // - we tried switching to that descendant but were missing // data for some intermediate block between chainActive and the // tip. // So if this block is itself better than chainActive.Tip() and it // wasn't in // setBlockIndexCandidates, then it must be in mapBlocksUnlinked. if (!CBlockIndexWorkComparator()(pindex, chainActive.Tip()) && setBlockIndexCandidates.count(pindex) == 0) { if (pindexFirstInvalid == nullptr) { assert(foundInUnlinked); } } } // Perhaps too slow // assert(pindex->GetBlockHash() == pindex->GetBlockHeader().GetHash()); // End: actual consistency checks. // Try descending into the first subnode. std::pair<std::multimap<CBlockIndex *, CBlockIndex *>::iterator, std::multimap<CBlockIndex *, CBlockIndex *>::iterator> range = forward.equal_range(pindex); if (range.first != range.second) { // A subnode was found. pindex = range.first->second; nHeight++; continue; } // This is a leaf node. Move upwards until we reach a node of which we // have not yet visited the last child. while (pindex) { // We are going to either move to a parent or a sibling of pindex. // If pindex was the first with a certain property, unset the // corresponding variable. if (pindex == pindexFirstInvalid) { pindexFirstInvalid = nullptr; } if (pindex == pindexFirstParked) { pindexFirstParked = nullptr; } if (pindex == pindexFirstMissing) { pindexFirstMissing = nullptr; } if (pindex == pindexFirstNeverProcessed) { pindexFirstNeverProcessed = nullptr; } if (pindex == pindexFirstNotTreeValid) { pindexFirstNotTreeValid = nullptr; } if (pindex == pindexFirstNotTransactionsValid) { pindexFirstNotTransactionsValid = nullptr; } if (pindex == pindexFirstNotChainValid) { pindexFirstNotChainValid = nullptr; } if (pindex == pindexFirstNotScriptsValid) { pindexFirstNotScriptsValid = nullptr; } // Find our parent. CBlockIndex *pindexPar = pindex->pprev; // Find which child we just visited. std::pair<std::multimap<CBlockIndex *, CBlockIndex *>::iterator, std::multimap<CBlockIndex *, CBlockIndex *>::iterator> rangePar = forward.equal_range(pindexPar); while (rangePar.first->second != pindex) { // Our parent must have at least the node we're coming from as // child. assert(rangePar.first != rangePar.second); rangePar.first++; } // Proceed to the next one. rangePar.first++; if (rangePar.first != rangePar.second) { // Move to the sibling. pindex = rangePar.first->second; break; } else { // Move up further. pindex = pindexPar; nHeight--; continue; } } } // Check that we actually traversed the entire map. assert(nNodes == forward.size()); } std::string CBlockFileInfo::ToString() const { return strprintf( "CBlockFileInfo(blocks=%u, size=%u, heights=%u...%u, time=%s...%s)", nBlocks, nSize, nHeightFirst, nHeightLast, FormatISO8601DateTime(nTimeFirst), FormatISO8601DateTime(nTimeLast)); } CBlockFileInfo *GetBlockFileInfo(size_t n) { LOCK(cs_LastBlockFile); return &vinfoBlockFile.at(n); } static const uint64_t MEMPOOL_DUMP_VERSION = 1; bool LoadMempool(const Config &config) { int64_t nExpiryTimeout = gArgs.GetArg("-mempoolexpiry", DEFAULT_MEMPOOL_EXPIRY) * 60 * 60; FILE *filestr = fsbridge::fopen(GetDataDir() / "mempool.dat", "rb"); CAutoFile file(filestr, SER_DISK, CLIENT_VERSION); if (file.IsNull()) { LogPrintf( "Failed to open mempool file from disk. Continuing anyway.\n"); return false; } int64_t count = 0; int64_t skipped = 0; int64_t failed = 0; int64_t nNow = GetTime(); try { uint64_t version; file >> version; if (version != MEMPOOL_DUMP_VERSION) { return false; } uint64_t num; file >> num; double prioritydummy = 0; while (num--) { CTransactionRef tx; int64_t nTime; int64_t nFeeDelta; file >> tx; file >> nTime; file >> nFeeDelta; Amount amountdelta = nFeeDelta * SATOSHI; if (amountdelta != Amount::zero()) { g_mempool.PrioritiseTransaction(tx->GetId(), prioritydummy, amountdelta); } CValidationState state; if (nTime + nExpiryTimeout > nNow) { LOCK(cs_main); AcceptToMemoryPoolWithTime( config, g_mempool, state, tx, true /* fLimitFree */, nullptr /* pfMissingInputs */, nTime, false /* fOverrideMempoolLimit */, Amount::zero() /* nAbsurdFee */, false /* test_accept */); if (state.IsValid()) { ++count; } else { ++failed; } } else { ++skipped; } if (ShutdownRequested()) { return false; } } std::map<uint256, Amount> mapDeltas; file >> mapDeltas; for (const auto &i : mapDeltas) { g_mempool.PrioritiseTransaction(i.first, prioritydummy, i.second); } } catch (const std::exception &e) { LogPrintf("Failed to deserialize mempool data on disk: %s. Continuing " "anyway.\n", e.what()); return false; } LogPrintf("Imported mempool transactions from disk: %i successes, %i " "failed, %i expired\n", count, failed, skipped); return true; } bool DumpMempool() { int64_t start = GetTimeMicros(); std::map<uint256, Amount> mapDeltas; std::vector<TxMempoolInfo> vinfo; static Mutex dump_mutex; LOCK(dump_mutex); { LOCK(g_mempool.cs); for (const auto &i : g_mempool.mapDeltas) { mapDeltas[i.first] = i.second.second; } vinfo = g_mempool.infoAll(); } int64_t mid = GetTimeMicros(); try { FILE *filestr = fsbridge::fopen(GetDataDir() / "mempool.dat.new", "wb"); if (!filestr) { return false; } CAutoFile file(filestr, SER_DISK, CLIENT_VERSION); uint64_t version = MEMPOOL_DUMP_VERSION; file << version; file << uint64_t(vinfo.size()); for (const auto &i : vinfo) { file << *(i.tx); file << int64_t(i.nTime); file << i.nFeeDelta; mapDeltas.erase(i.tx->GetId()); } file << mapDeltas; if (!FileCommit(file.Get())) { throw std::runtime_error("FileCommit failed"); } file.fclose(); RenameOver(GetDataDir() / "mempool.dat.new", GetDataDir() / "mempool.dat"); int64_t last = GetTimeMicros(); LogPrintf("Dumped mempool: %gs to copy, %gs to dump\n", (mid - start) * MICRO, (last - mid) * MICRO); } catch (const std::exception &e) { LogPrintf("Failed to dump mempool: %s. Continuing anyway.\n", e.what()); return false; } return true; } //! Guess how far we are in the verification process at the given block index //! require cs_main if pindex has not been validated yet (because nChainTx might //! be unset) double GuessVerificationProgress(const ChainTxData &data, const CBlockIndex *pindex) { if (pindex == nullptr) { return 0.0; } // This function assumes the lock on cs_main is already held (see the // above comment). This is a temporary check until PR15997 is backported. // https://github.com/bitcoin/bitcoin/pull/15997 AssertLockHeld(cs_main); int64_t nNow = time(nullptr); double fTxTotal; if (pindex->nChainTx <= data.nTxCount) { fTxTotal = data.nTxCount + (nNow - data.nTime) * data.dTxRate; } else { fTxTotal = pindex->nChainTx + (nNow - pindex->GetBlockTime()) * data.dTxRate; } return pindex->nChainTx / fTxTotal; } class CMainCleanup { public: CMainCleanup() {} ~CMainCleanup() { // block headers for (const std::pair<const uint256, CBlockIndex *> &it : mapBlockIndex) { delete it.second; } mapBlockIndex.clear(); } } instance_of_cmaincleanup;