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 &params,
                       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 &params, 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 &params);
 } 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 &params,
                                         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 &params)
     EXCLUSIVE_LOCKS_REQUIRED(cs_main) {
     AssertLockHeld(cs_main);
     return IsMagneticAnomalyEnabled(params, chainActive.Tip());
 }
 
 static bool IsGravitonEnabledForCurrentBlock(const Consensus::Params &params)
     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 &params,
                                       int64_t nMedianTimePast) {
     return nMedianTimePast >= gArgs.GetArg("-replayprotectionactivationtime",
                                            params.phononActivationTime);
 }
 
 static bool IsReplayProtectionEnabled(const Consensus::Params &params,
                                       const CBlockIndex *pindexPrev) {
     if (pindexPrev == nullptr) {
         return false;
     }
 
     return IsReplayProtectionEnabled(params, pindexPrev->GetMedianTimePast());
 }
 
 static bool
 IsReplayProtectionEnabledForCurrentBlock(const Consensus::Params &params)
     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 &params, 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 &params) {
     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 &params) {
     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 &params) {
     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 &params,
                                         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 &params,
                                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 &params = 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 &params = 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 &params,
                              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 &params,
                 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 &params,
                                        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 &params,
                                                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 &params,
                                  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 &params,
                        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 &params = 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 &params) {
     // 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 &params,
                                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 &params, CCoinsView *view) {
     return g_chainstate.ReplayBlocks(params, view);
 }
 
 bool CChainState::RewindBlockIndex(const Config &config) {
     LOCK(cs_main);
 
     const CChainParams &params = 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;