text: // Copyright (c) 2009-2010 Satoshi Nakamoto
  // Copyright (c) 2009-2014 The Bitcoin developers
  // Copyright (c) 2014-2015 The Dash developers
  // Copyright (c) 2015-2017 The PIVX developers
  // Copyright (c) 2017 The Eazy developers
  // Distributed under the MIT software license, see the accompanying
  // file COPYING or http://www.opensource.org/licenses/mit-license.php.
  
  #ifndef BITCOIN_MAIN_H
  #define BITCOIN_MAIN_H
  
  #if defined(HAVE_CONFIG_H)
  #include "config/eazy-config.h"
  #endif
  
  #include "amount.h"
  #include "chain.h"
  #include "chainparams.h"
  #include "coins.h"
  #include "net.h"
  #include "pow.h"
  #include "primitives/block.h"
  #include "primitives/transaction.h"
  #include "primitives/zerocoin.h"
  #include "script/script.h"
  #include "script/sigcache.h"
  #include "script/standard.h"
  #include "sync.h"
  #include "tinyformat.h"
  #include "txmempool.h"
  #include "uint256.h"
  #include "undo.h"
  
  #include <algorithm>
  #include <exception>
  #include <map>
  #include <set>
  #include <stdint.h>
  #include <string>
  #include <utility>
  #include <vector>
  
  #include "libzerocoin/CoinSpend.h"
  
  #include <boost/unordered_map.hpp>
  
  class CBlockIndex;
  class CBlockTreeDB;
  class CZerocoinDB;
  class CSporkDB;
  class CBloomFilter;
  class CInv;
  class CScriptCheck;
  class CValidationInterface;
  class CValidationState;
  
  struct CBlockTemplate;
  struct CNodeStateStats;
  
  inline int64_t GetMNCollateral(int nHeight){
  
  
        if(nHeight < 20000) {
          return 1000;
        } else if(nHeight < 100000) {
          return 2000;
        } else if(nHeight < 500000) {
          return 3500;
        } else {
          return 10000;
        }
  
  }
  
  /** Default for -blockmaxsize and -blockminsize, which control the range of sizes the mining code will create **/
  static const unsigned int DEFAULT_BLOCK_MAX_SIZE = 750000;
  static const unsigned int DEFAULT_BLOCK_MIN_SIZE = 0;
  /** Default for -blockprioritysize, maximum space for zero/low-fee transactions **/
  static const unsigned int DEFAULT_BLOCK_PRIORITY_SIZE = 50000;
  /** Default for accepting alerts from the P2P network. */
  static const bool DEFAULT_ALERTS = true;
  /** The maximum size for transactions we're willing to relay/mine */
  static const unsigned int MAX_STANDARD_TX_SIZE = 100000;
  static const unsigned int MAX_ZEROCOIN_TX_SIZE = 150000;
  /** The maximum allowed number of signature check operations in a block (network rule) */
  static const unsigned int MAX_BLOCK_SIGOPS_CURRENT = MAX_BLOCK_SIZE_CURRENT / 50;
  static const unsigned int MAX_BLOCK_SIGOPS_LEGACY = MAX_BLOCK_SIZE_LEGACY / 50;
  /** Maximum number of signature check operations in an IsStandard() P2SH script */
  static const unsigned int MAX_P2SH_SIGOPS = 15;
  /** The maximum number of sigops we're willing to relay/mine in a single tx */
  static const unsigned int MAX_TX_SIGOPS_CURRENT = MAX_BLOCK_SIGOPS_CURRENT / 5;
  static const unsigned int MAX_TX_SIGOPS_LEGACY = MAX_BLOCK_SIGOPS_LEGACY / 5;
  /** Default for -maxorphantx, maximum number of orphan transactions kept in memory */
  static const unsigned int DEFAULT_MAX_ORPHAN_TRANSACTIONS = 100;
  /** The maximum size of a blk?????.dat file (since 0.8) */
  static const unsigned int MAX_BLOCKFILE_SIZE = 0x8000000; // 128 MiB
  /** The pre-allocation chunk size for blk?????.dat files (since 0.8) */
  static const unsigned int BLOCKFILE_CHUNK_SIZE = 0x1000000; // 16 MiB
  /** The pre-allocation chunk size for rev?????.dat files (since 0.8) */
  static const unsigned int UNDOFILE_CHUNK_SIZE = 0x100000; // 1 MiB
  /** Coinbase transaction outputs can only be spent after this number of new blocks (network rule) */
  static const int COINBASE_MATURITY = 5;
  /** Threshold for nLockTime: below this value it is interpreted as block number, otherwise as UNIX timestamp. */
  static const unsigned int LOCKTIME_THRESHOLD = 500000000; // Tue Nov  5 00:53:20 1985 UTC
  /** Maximum number of script-checking threads allowed */
  static const int MAX_SCRIPTCHECK_THREADS = 16;
  /** -par default (number of script-checking threads, 0 = auto) */
  static const int DEFAULT_SCRIPTCHECK_THREADS = 0;
  /** Number of blocks that can be requested at any given time from a single peer. */
  static const int MAX_BLOCKS_IN_TRANSIT_PER_PEER = 16;
  /** Timeout in seconds during which a peer must stall block download progress before being disconnected. */
  static const unsigned int BLOCK_STALLING_TIMEOUT = 2;
  /** Number of headers sent in one getheaders result. We rely on the assumption that if a peer sends
   *  less than this number, we reached their tip. Changing this value is a protocol upgrade. */
  static const unsigned int MAX_HEADERS_RESULTS = 2000;
  /** Size of the "block download window": how far ahead of our current height do we fetch?
   *  Larger windows tolerate larger download speed differences between peer, but increase the potential
   *  degree of disordering of blocks on disk (which make reindexing and in the future perhaps pruning
   *  harder). We'll probably want to make this a per-peer adaptive value at some point. */
  static const unsigned int BLOCK_DOWNLOAD_WINDOW = 1024;
  /** Time to wait (in seconds) between writing blockchain state to disk. */
  static const unsigned int DATABASE_WRITE_INTERVAL = 3600;
  /** Maximum length of reject messages. */
  static const unsigned int MAX_REJECT_MESSAGE_LENGTH = 111;
  
  /** Enable bloom filter */
   static const bool DEFAULT_PEERBLOOMFILTERS = true;
  
  /** "reject" message codes */
  static const unsigned char REJECT_MALFORMED = 0x01;
  static const unsigned char REJECT_INVALID = 0x10;
  static const unsigned char REJECT_OBSOLETE = 0x11;
  static const unsigned char REJECT_DUPLICATE = 0x12;
  static const unsigned char REJECT_NONSTANDARD = 0x40;
  static const unsigned char REJECT_DUST = 0x41;
  static const unsigned char REJECT_INSUFFICIENTFEE = 0x42;
  static const unsigned char REJECT_CHECKPOINT = 0x43;
  
  struct BlockHasher {
      size_t operator()(const uint256& hash) const { return hash.GetLow64(); }
  };
  
  extern CScript COINBASE_FLAGS;
  extern CCriticalSection cs_main;
  extern CTxMemPool mempool;
  typedef boost::unordered_map<uint256, CBlockIndex*, BlockHasher> BlockMap;
  extern BlockMap mapBlockIndex;
  extern uint64_t nLastBlockTx;
  extern uint64_t nLastBlockSize;
  extern const std::string strMessageMagic;
  extern int64_t nTimeBestReceived;
  extern CWaitableCriticalSection csBestBlock;
  extern CConditionVariable cvBlockChange;
  extern bool fImporting;
  extern bool fReindex;
  extern int nScriptCheckThreads;
  extern bool fTxIndex;
  extern bool fIsBareMultisigStd;
  extern bool fCheckBlockIndex;
  extern unsigned int nCoinCacheSize;
  extern CFeeRate minRelayTxFee;
  extern bool fAlerts;
  extern bool fVerifyingBlocks;
  
  extern bool fLargeWorkForkFound;
  extern bool fLargeWorkInvalidChainFound;
  
  extern unsigned int nStakeMinAge;
  extern int64_t nLastCoinStakeSearchInterval;
  extern int64_t nLastCoinStakeSearchTime;
  extern int64_t nReserveBalance;
  
  extern std::map<uint256, int64_t> mapRejectedBlocks;
  extern std::map<unsigned int, unsigned int> mapHashedBlocks;
  extern std::set<std::pair<COutPoint, unsigned int> > setStakeSeen;
  
  /** Best header we've seen so far (used for getheaders queries' starting points). */
  extern CBlockIndex* pindexBestHeader;
  
  /** Minimum disk space required - used in CheckDiskSpace() */
  static const uint64_t nMinDiskSpace = 52428800;
  
  /** Register a wallet to receive updates from core */
  void RegisterValidationInterface(CValidationInterface* pwalletIn);
  /** Unregister a wallet from core */
  void UnregisterValidationInterface(CValidationInterface* pwalletIn);
  /** Unregister all wallets from core */
  void UnregisterAllValidationInterfaces();
  /** Push an updated transaction to all registered wallets */
  void SyncWithWallets(const CTransaction& tx, const CBlock* pblock = NULL);
  
  /** Register with a network node to receive its signals */
  void RegisterNodeSignals(CNodeSignals& nodeSignals);
  /** Unregister a network node */
  void UnregisterNodeSignals(CNodeSignals& nodeSignals);
  
  /** 
   * Process an incoming block. This only returns after the best known valid
   * block is made active. Note that it does not, however, guarantee that the
   * specific block passed to it has been checked for validity!
   * 
   * @param[out]  state   This may be set to an Error state if any error occurred processing it, including during validation/connection/etc of otherwise unrelated blocks during reorganisation; or it may be set to an Invalid state if pblock is itself invalid (but this is not guaranteed even when the block is checked). If you want to *possibly* get feedback on whether pblock is valid, you must also install a CValidationInterface - this will have its BlockChecked method called whenever *any* block completes validation.
   * @param[in]   pfrom   The node which we are receiving the block from; it is added to mapBlockSource and may be penalised if the block is invalid.
   * @param[in]   pblock  The block we want to process.
   * @param[out]  dbp     If pblock is stored to disk (or already there), this will be set to its location.
   * @return True if state.IsValid()
   */
  bool ProcessNewBlock(CValidationState& state, CNode* pfrom, CBlock* pblock, CDiskBlockPos* dbp = NULL);
  /** Check whether enough disk space is available for an incoming block */
  bool CheckDiskSpace(uint64_t nAdditionalBytes = 0);
  /** Open a block file (blk?????.dat) */
  FILE* OpenBlockFile(const CDiskBlockPos& pos, bool fReadOnly = false);
  /** Open an undo file (rev?????.dat) */
  FILE* OpenUndoFile(const CDiskBlockPos& pos, bool fReadOnly = false);
  /** Translation to a filesystem path */
  boost::filesystem::path GetBlockPosFilename(const CDiskBlockPos& pos, const char* prefix);
  /** Import blocks from an external file */
  bool LoadExternalBlockFile(FILE* fileIn, CDiskBlockPos* dbp = NULL);
  /** Initialize a new block tree database + block data on disk */
  bool InitBlockIndex();
  /** Load the block tree and coins database from disk */
  bool LoadBlockIndex();
  /** Unload database information */
  void UnloadBlockIndex();
  /** See whether the protocol update is enforced for connected nodes */
  int ActiveProtocol();
  /** Process protocol messages received from a given node */
  bool ProcessMessages(CNode* pfrom);
  /**
   * Send queued protocol messages to be sent to a give node.
   *
   * @param[in]   pto             The node which we are sending messages to.
   * @param[in]   fSendTrickle    When true send the trickled data, otherwise trickle the data until true.
   */
  bool SendMessages(CNode* pto, bool fSendTrickle);
  /** Run an instance of the script checking thread */
  void ThreadScriptCheck();
  
  // ***TODO*** probably not the right place for these 2
  /** Check whether a block hash satisfies the proof-of-work requirement specified by nBits */
  bool CheckProofOfWork(uint256 hash, unsigned int nBits);
  
  /** Check whether we are doing an initial block download (synchronizing from disk or network) */
  bool IsInitialBlockDownload();
  /** Format a string that describes several potential problems detected by the core */
  std::string GetWarnings(std::string strFor);
  /** Retrieve a transaction (from memory pool, or from disk, if possible) */
  bool GetTransaction(const uint256& hash, CTransaction& tx, uint256& hashBlock, bool fAllowSlow = false);
  /** Find the best known block, and make it the tip of the block chain */
  
  bool DisconnectBlocksAndReprocess(int blocks);
  
  // ***TODO***
  double ConvertBitsToDouble(unsigned int nBits);
  int64_t GetMasternodePayment(int nHeight, int64_t blockValue, int nMasternodeCount = 0);
  unsigned int GetNextWorkRequired(const CBlockIndex* pindexLast, const CBlockHeader* pblock, bool fProofOfStake);
  
  bool ActivateBestChain(CValidationState& state, CBlock* pblock = NULL, bool fAlreadyChecked = false);
  CAmount GetBlockValue(int nHeight);
  
  /** Create a new block index entry for a given block hash */
  CBlockIndex* InsertBlockIndex(uint256 hash);
  /** Abort with a message */
  bool AbortNode(const std::string& msg, const std::string& userMessage = "");
  /** Get statistics from node state */
  bool GetNodeStateStats(NodeId nodeid, CNodeStateStats& stats);
  /** Increase a node's misbehavior score. */
  void Misbehaving(NodeId nodeid, int howmuch);
  /** Flush all state, indexes and buffers to disk. */
  void FlushStateToDisk();
  
  
  /** (try to) add transaction to memory pool **/
  bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState& state, const CTransaction& tx, bool fLimitFree, bool* pfMissingInputs, bool fRejectInsaneFee = false, bool ignoreFees = false);
  
  bool AcceptableInputs(CTxMemPool& pool, CValidationState& state, const CTransaction& tx, bool fLimitFree, bool* pfMissingInputs, bool fRejectInsaneFee = false, bool isDSTX = false);
  
  int GetInputAge(CTxIn& vin);
  int GetInputAgeIX(uint256 nTXHash, CTxIn& vin);
  bool GetCoinAge(const CTransaction& tx, unsigned int nTxTime, uint64_t& nCoinAge);
  int GetIXConfirmations(uint256 nTXHash);
  
  struct CNodeStateStats {
      int nMisbehavior;
      int nSyncHeight;
      int nCommonHeight;
      std::vector<int> vHeightInFlight;
  };
  
  struct CDiskTxPos : public CDiskBlockPos {
      unsigned int nTxOffset; // after header
  
      ADD_SERIALIZE_METHODS;
  
      template <typename Stream, typename Operation>
      inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion)
      {
          READWRITE(*(CDiskBlockPos*)this);
          READWRITE(VARINT(nTxOffset));
      }
  
      CDiskTxPos(const CDiskBlockPos& blockIn, unsigned int nTxOffsetIn) : CDiskBlockPos(blockIn.nFile, blockIn.nPos), nTxOffset(nTxOffsetIn)
      {
      }
  
      CDiskTxPos()
      {
          SetNull();
      }
  
      void SetNull()
      {
          CDiskBlockPos::SetNull();
          nTxOffset = 0;
      }
  };
  
  
  CAmount GetMinRelayFee(const CTransaction& tx, unsigned int nBytes, bool fAllowFree);
  bool MoneyRange(CAmount nValueOut);
  
  /**
   * Check transaction inputs, and make sure any
   * pay-to-script-hash transactions are evaluating IsStandard scripts
   * 
   * Why bother? To avoid denial-of-service attacks; an attacker
   * can submit a standard HASH... OP_EQUAL transaction,
   * which will get accepted into blocks. The redemption
   * script can be anything; an attacker could use a very
   * expensive-to-check-upon-redemption script like:
   *   DUP CHECKSIG DROP ... repeated 100 times... OP_1
   */
  
  /** 
   * Check for standard transaction types
   * @param[in] mapInputs    Map of previous transactions that have outputs we're spending
   * @return True if all inputs (scriptSigs) use only standard transaction forms
   */
  bool AreInputsStandard(const CTransaction& tx, const CCoinsViewCache& mapInputs);
  
  /** 
   * Count ECDSA signature operations the old-fashioned (pre-0.6) way
   * @return number of sigops this transaction's outputs will produce when spent
   * @see CTransaction::FetchInputs
   */
  unsigned int GetLegacySigOpCount(const CTransaction& tx);
  
  /**
   * Count ECDSA signature operations in pay-to-script-hash inputs.
   * 
   * @param[in] mapInputs Map of previous transactions that have outputs we're spending
   * @return maximum number of sigops required to validate this transaction's inputs
   * @see CTransaction::FetchInputs
   */
  unsigned int GetP2SHSigOpCount(const CTransaction& tx, const CCoinsViewCache& mapInputs);
  
  
  /**
   * Check whether all inputs of this transaction are valid (no double spends, scripts & sigs, amounts)
   * This does not modify the UTXO set. If pvChecks is not NULL, script checks are pushed onto it
   * instead of being performed inline.
   */
  bool CheckInputs(const CTransaction& tx, CValidationState& state, const CCoinsViewCache& view, bool fScriptChecks, unsigned int flags, bool cacheStore, std::vector<CScriptCheck>* pvChecks = NULL);
  
  /** Apply the effects of this transaction on the UTXO set represented by view */
  void UpdateCoins(const CTransaction& tx, CValidationState& state, CCoinsViewCache& inputs, CTxUndo& txundo, int nHeight);
  
  /** Context-independent validity checks */
  bool CheckTransaction(const CTransaction& tx, bool fZerocoinActive, bool fRejectBadUTXO, CValidationState& state);
  bool CheckZerocoinMint(const uint256& txHash, const CTxOut& txout, CValidationState& state, bool fCheckOnly = false);
  bool CheckZerocoinSpend(const CTransaction tx, bool fVerifySignature, CValidationState& state);
  libzerocoin::CoinSpend TxInToZerocoinSpend(const CTxIn& txin);
  bool TxOutToPublicCoin(const CTxOut txout, libzerocoin::PublicCoin& pubCoin, CValidationState& state);
  bool BlockToPubcoinList(const CBlock& block, list<libzerocoin::PublicCoin>& listPubcoins);
  bool BlockToZerocoinMintList(const CBlock& block, std::list<CZerocoinMint>& vMints);
  bool BlockToMintValueVector(const CBlock& block, const libzerocoin::CoinDenomination denom, std::vector<CBigNum>& vValues);
  std::list<libzerocoin::CoinDenomination> ZerocoinSpendListFromBlock(const CBlock& block);
  void FindMints(vector<CZerocoinMint> vMintsToFind, vector<CZerocoinMint>& vMintsToUpdate, vector<CZerocoinMint>& vMissingMints, bool fExtendedSearch);
  bool GetZerocoinMint(const CBigNum& bnPubcoin, uint256& txHash);
  bool IsSerialKnown(const CBigNum& bnSerial);
  bool IsSerialInBlockchain(const CBigNum& bnSerial, int& nHeightTx);
  bool RemoveSerialFromDB(const CBigNum& bnSerial);
  int GetZerocoinStartHeight();
  bool IsTransactionInChain(uint256 txId, int& nHeightTx);
  bool IsBlockHashInChain(const uint256& hashBlock);
  void RecalculateZEAZYSpent();
  void RecalculateZEAZYMinted();
  bool RecalculateCS2Supply(int nHeightStart);
  
  
  /**
   * Check if transaction will be final in the next block to be created.
   *
   * Calls IsFinalTx() with current block height and appropriate block time.
   *
   * See consensus/consensus.h for flag definitions.
   */
  bool CheckFinalTx(const CTransaction& tx, int flags = -1);
  
  /** Check for standard transaction types
   * @return True if all outputs (scriptPubKeys) use only standard transaction forms
   */
  bool IsStandardTx(const CTransaction& tx, std::string& reason);
  
  bool IsFinalTx(const CTransaction& tx, int nBlockHeight = 0, int64_t nBlockTime = 0);
  
  /** Undo information for a CBlock */
  class CBlockUndo
  {
  public:
      std::vector<CTxUndo> vtxundo; // for all but the coinbase
  
      ADD_SERIALIZE_METHODS;
  
      template <typename Stream, typename Operation>
      inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion)
      {
          READWRITE(vtxundo);
      }
  
      bool WriteToDisk(CDiskBlockPos& pos, const uint256& hashBlock);
      bool ReadFromDisk(const CDiskBlockPos& pos, const uint256& hashBlock);
  };
  
  
  /** 
   * Closure representing one script verification
   * Note that this stores references to the spending transaction 
   */
  class CScriptCheck
  {
  private:
      CScript scriptPubKey;
      const CTransaction* ptxTo;
      unsigned int nIn;
      unsigned int nFlags;
      bool cacheStore;
      ScriptError error;
  
  public:
      CScriptCheck() : ptxTo(0), nIn(0), nFlags(0), cacheStore(false), error(SCRIPT_ERR_UNKNOWN_ERROR) {}
      CScriptCheck(const CCoins& txFromIn, const CTransaction& txToIn, unsigned int nInIn, unsigned int nFlagsIn, bool cacheIn) : scriptPubKey(txFromIn.vout[txToIn.vin[nInIn].prevout.n].scriptPubKey),
                                                                                                                                  ptxTo(&txToIn), nIn(nInIn), nFlags(nFlagsIn), cacheStore(cacheIn), error(SCRIPT_ERR_UNKNOWN_ERROR) {}
  
      bool operator()();
  
      void swap(CScriptCheck& check)
      {
          scriptPubKey.swap(check.scriptPubKey);
          std::swap(ptxTo, check.ptxTo);
          std::swap(nIn, check.nIn);
          std::swap(nFlags, check.nFlags);
          std::swap(cacheStore, check.cacheStore);
          std::swap(error, check.error);
      }
  
      ScriptError GetScriptError() const { return error; }
  };
  
  
  /** Functions for disk access for blocks */
  bool WriteBlockToDisk(CBlock& block, CDiskBlockPos& pos);
  bool ReadBlockFromDisk(CBlock& block, const CDiskBlockPos& pos);
  bool ReadBlockFromDisk(CBlock& block, const CBlockIndex* pindex);
  
  
  /** Functions for validating blocks and updating the block tree */
  
  /** Undo the effects of this block (with given index) on the UTXO set represented by coins.
   *  In case pfClean is provided, operation will try to be tolerant about errors, and *pfClean
   *  will be true if no problems were found. Otherwise, the return value will be false in case
   *  of problems. Note that in any case, coins may be modified. */
  bool DisconnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex, CCoinsViewCache& coins, bool* pfClean = NULL);
  
  /** Reprocess a number of blocks to try and get on the correct chain again **/
  bool DisconnectBlocksAndReprocess(int blocks);
  
  /** Apply the effects of this block (with given index) on the UTXO set represented by coins */
  bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pindex, CCoinsViewCache& coins, bool fJustCheck, bool fAlreadyChecked = false);
  
  /** Context-independent validity checks */
  bool CheckBlockHeader(const CBlockHeader& block, CValidationState& state, bool fCheckPOW = true);
  bool CheckBlock(const CBlock& block, CValidationState& state, bool fCheckPOW = true, bool fCheckMerkleRoot = true, bool fCheckSig = true);
  bool CheckWork(const CBlock block, CBlockIndex* const pindexPrev);
  
  /** Context-dependent validity checks */
  bool ContextualCheckBlockHeader(const CBlockHeader& block, CValidationState& state, CBlockIndex* pindexPrev);
  bool ContextualCheckBlock(const CBlock& block, CValidationState& state, CBlockIndex* pindexPrev);
  
  /** Check a block is completely valid from start to finish (only works on top of our current best block, with cs_main held) */
  bool TestBlockValidity(CValidationState& state, const CBlock& block, CBlockIndex* pindexPrev, bool fCheckPOW = true, bool fCheckMerkleRoot = true);
  
  /** Store block on disk. If dbp is provided, the file is known to already reside on disk */
  bool AcceptBlock(CBlock& block, CValidationState& state, CBlockIndex** pindex, CDiskBlockPos* dbp = NULL, bool fAlreadyCheckedBlock = false);
  bool AcceptBlockHeader(const CBlockHeader& block, CValidationState& state, CBlockIndex** ppindex = NULL);
  
  
  class CBlockFileInfo
  {
  public:
      unsigned int nBlocks;      //! number of blocks stored in file
      unsigned int nSize;        //! number of used bytes of block file
      unsigned int nUndoSize;    //! number of used bytes in the undo file
      unsigned int nHeightFirst; //! lowest height of block in file
      unsigned int nHeightLast;  //! highest height of block in file
      uint64_t nTimeFirst;       //! earliest time of block in file
      uint64_t nTimeLast;        //! latest time of block in file
  
      ADD_SERIALIZE_METHODS;
  
      template <typename Stream, typename Operation>
      inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion)
      {
          READWRITE(VARINT(nBlocks));
          READWRITE(VARINT(nSize));
          READWRITE(VARINT(nUndoSize));
          READWRITE(VARINT(nHeightFirst));
          READWRITE(VARINT(nHeightLast));
          READWRITE(VARINT(nTimeFirst));
          READWRITE(VARINT(nTimeLast));
      }
  
      void SetNull()
      {
          nBlocks = 0;
          nSize = 0;
          nUndoSize = 0;
          nHeightFirst = 0;
          nHeightLast = 0;
          nTimeFirst = 0;
          nTimeLast = 0;
      }
  
      CBlockFileInfo()
      {
          SetNull();
      }
  
      std::string ToString() const;
  
      /** update statistics (does not update nSize) */
      void AddBlock(unsigned int nHeightIn, uint64_t nTimeIn)
      {
          if (nBlocks == 0 || nHeightFirst > nHeightIn)
              nHeightFirst = nHeightIn;
          if (nBlocks == 0 || nTimeFirst > nTimeIn)
              nTimeFirst = nTimeIn;
          nBlocks++;
          if (nHeightIn > nHeightLast)
              nHeightLast = nHeightIn;
          if (nTimeIn > nTimeLast)
              nTimeLast = nTimeIn;
      }
  };
  
  /** Capture information about block/transaction validation */
  class CValidationState
  {
  private:
      enum mode_state {
          MODE_VALID,   //! everything ok
          MODE_INVALID, //! network rule violation (DoS value may be set)
          MODE_ERROR,   //! run-time error
      } mode;
      int nDoS;
      std::string strRejectReason;
      unsigned char chRejectCode;
      bool corruptionPossible;
  
  public:
      CValidationState() : mode(MODE_VALID), nDoS(0), chRejectCode(0), corruptionPossible(false) {}
      bool DoS(int level, bool ret = false, unsigned char chRejectCodeIn = 0, std::string strRejectReasonIn = "", bool corruptionIn = false)
      {
          chRejectCode = chRejectCodeIn;
          strRejectReason = strRejectReasonIn;
          corruptionPossible = corruptionIn;
          if (mode == MODE_ERROR)
              return ret;
          nDoS += level;
          mode = MODE_INVALID;
          return ret;
      }
      bool Invalid(bool ret = false,
          unsigned char _chRejectCode = 0,
          std::string _strRejectReason = "")
      {
          return DoS(0, ret, _chRejectCode, _strRejectReason);
      }
      bool Error(std::string strRejectReasonIn = "")
      {
          if (mode == MODE_VALID)
              strRejectReason = strRejectReasonIn;
          mode = MODE_ERROR;
          return false;
      }
      bool Abort(const std::string& msg)
      {
          AbortNode(msg);
          return Error(msg);
      }
      bool IsValid() const
      {
          return mode == MODE_VALID;
      }
      bool IsInvalid() const
      {
          return mode == MODE_INVALID;
      }
      bool IsError() const
      {
          return mode == MODE_ERROR;
      }
      bool IsInvalid(int& nDoSOut) const
      {
          if (IsInvalid()) {
              nDoSOut = nDoS;
              return true;
          }
          return false;
      }
      bool CorruptionPossible() const
      {
          return corruptionPossible;
      }
      unsigned char GetRejectCode() const { return chRejectCode; }
      std::string GetRejectReason() const { return strRejectReason; }
  };
  
  /** RAII wrapper for VerifyDB: Verify consistency of the block and coin databases */
  class CVerifyDB
  {
  public:
      CVerifyDB();
      ~CVerifyDB();
      bool VerifyDB(CCoinsView* coinsview, int nCheckLevel, int nCheckDepth);
  };
  
  /** Find the last common block between the parameter chain and a locator. */
  CBlockIndex* FindForkInGlobalIndex(const CChain& chain, const CBlockLocator& locator);
  
  /** Mark a block as invalid. */
  bool InvalidateBlock(CValidationState& state, CBlockIndex* pindex);
  
  /** Remove invalidity status from a block and its descendants. */
  bool ReconsiderBlock(CValidationState& state, CBlockIndex* pindex);
  
  /** The currently-connected chain of blocks. */
  extern CChain chainActive;
  
  /** Global variable that points to the active CCoinsView (protected by cs_main) */
  extern CCoinsViewCache* pcoinsTip;
  
  /** Global variable that points to the active block tree (protected by cs_main) */
  extern CBlockTreeDB* pblocktree;
  
  /** Global variable that points to the zerocoin database (protected by cs_main) */
  extern CZerocoinDB* zerocoinDB;
  
  /** Global variable that points to the spork database (protected by cs_main) */
  extern CSporkDB* pSporkDB;
  
  struct CBlockTemplate {
      CBlock block;
      std::vector<CAmount> vTxFees;
      std::vector<int64_t> vTxSigOps;
  };
  
  /*
  class CValidationInterface
  {
  protected:
      virtual void SyncTransaction(const CTransaction& tx, const CBlock* pblock){};
      virtual void EraseFromWallet(const uint256& hash){};
      virtual void SetBestChain(const CBlockLocator& locator){};
      virtual bool UpdatedTransaction(const uint256& hash) { return false; };
      virtual void Inventory(const uint256& hash){};
      virtual void ResendWalletTransactions(){};
      virtual void BlockChecked(const CBlock&, const CValidationState&){};
      friend void ::RegisterValidationInterface(CValidationInterface*);
      friend void ::UnregisterValidationInterface(CValidationInterface*);
      friend void ::UnregisterAllValidationInterfaces();
  };
  */
  #endif // BITCOIN_MAIN_H
  