setup_common.h raw

   1  // Copyright (c) 2015-present The Limenka developers
   2  // Distributed under the MIT software license, see the accompanying
   3  // file COPYING or http://www.opensource.org/licenses/mit-license.php.
   4  
   5  #ifndef LIMENKA_TEST_UTIL_SETUP_COMMON_H
   6  #define LIMENKA_TEST_UTIL_SETUP_COMMON_H
   7  
   8  #include <common/args.h> // IWYU pragma: export
   9  #include <common/pcp.h>
  10  #include <kernel/caches.h>
  11  #include <kernel/context.h>
  12  #include <key.h>
  13  #include <node/caches.h>
  14  #include <node/context.h> // IWYU pragma: export
  15  #include <optional>
  16  #include <ostream>
  17  #include <primitives/transaction.h>
  18  #include <pubkey.h>
  19  #include <stdexcept>
  20  #include <test/util/random.h>
  21  #include <util/chaintype.h> // IWYU pragma: export
  22  #include <util/check.h>
  23  #include <util/fs.h>
  24  #include <util/signalinterrupt.h>
  25  #include <util/string.h>
  26  #include <util/vector.h>
  27  
  28  #include <functional>
  29  #include <type_traits>
  30  #include <vector>
  31  
  32  class arith_uint256;
  33  class CFeeRate;
  34  class Chainstate;
  35  class FastRandomContext;
  36  class uint160;
  37  class uint256;
  38  
  39  /** This is connected to the logger. Can be used to redirect logs to any other log */
  40  extern const std::function<void(const std::string&)> G_TEST_LOG_FUN;
  41  
  42  /** Retrieve the command line arguments. */
  43  extern const std::function<std::vector<const char*>()> G_TEST_COMMAND_LINE_ARGUMENTS;
  44  
  45  /** Retrieve the unit test name. */
  46  extern const std::function<std::string()> G_TEST_GET_FULL_NAME;
  47  
  48  static constexpr CAmount CENT{1000000};
  49  
  50  /** Register common test args. Shared across binaries that rely on the test framework. */
  51  void SetupCommonTestArgs(ArgsManager& argsman);
  52  
  53  struct TestOpts {
  54      std::vector<const char*> extra_args{};
  55      bool coins_db_in_memory{true};
  56      bool block_tree_db_in_memory{true};
  57      bool setup_net{true};
  58      bool setup_validation_interface{true};
  59      bool min_validation_cache{false}; // Equivalent of -maxsigcachebytes=0
  60  };
  61  
  62  /** Basic testing setup.
  63   * This just configures logging, data dir and chain parameters.
  64   */
  65  struct BasicTestingSetup {
  66      util::SignalInterrupt m_interrupt;
  67      node::NodeContext m_node; // keep as first member to be destructed last
  68  
  69      FastRandomContext m_rng;
  70      /** Seed the global RNG state and m_rng for testing and log the seed value. This affects all randomness, except GetStrongRandBytes(). */
  71      void SeedRandomForTest(SeedRand seed)
  72      {
  73          SeedRandomStateForTest(seed);
  74          m_rng.Reseed(GetRandHash());
  75      }
  76  
  77      explicit BasicTestingSetup(const ChainType chainType = ChainType::MAIN, TestOpts = {});
  78      ~BasicTestingSetup();
  79  
  80      fs::path m_path_root;
  81      fs::path m_path_lock;
  82      bool m_has_custom_datadir{false};
  83      /** @brief Test-specific arguments and settings.
  84       *
  85       * This member is intended to be the primary source of settings for code
  86       * being tested by unit tests. It exists to make tests more self-contained
  87       * and reduce reliance on global state.
  88       *
  89       * Usage guidelines:
  90       * 1. Prefer using m_args where possible in test code.
  91       * 2. If m_args is not accessible, use m_node.args as a fallback.
  92       * 3. Avoid direct references to gArgs in test code.
  93       *
  94       * Note: Currently, m_node.args points to gArgs for backwards
  95       * compatibility. In the future, it will point to m_args to further isolate
  96       * test environments.
  97       *
  98       * @see https://github.com/limenka/limenka/issues/25055 for additional context.
  99       */
 100      ArgsManager m_args;
 101  };
 102  
 103  /** Testing setup that performs all steps up until right before
 104   * ChainstateManager gets initialized. Meant for testing ChainstateManager
 105   * initialization behaviour.
 106   */
 107  struct ChainTestingSetup : public BasicTestingSetup {
 108      kernel::CacheSizes m_kernel_cache_sizes{node::CalculateCacheSizes(m_args).kernel};
 109      bool m_coins_db_in_memory{true};
 110      bool m_block_tree_db_in_memory{true};
 111      std::function<void()> m_make_chainman{};
 112  
 113      explicit ChainTestingSetup(const ChainType chainType = ChainType::MAIN, TestOpts = {});
 114      ~ChainTestingSetup();
 115  
 116      // Supplies a chainstate, if one is needed
 117      void LoadVerifyActivateChainstate();
 118  };
 119  
 120  /** Testing setup that configures a complete environment.
 121   */
 122  struct TestingSetup : public ChainTestingSetup {
 123      explicit TestingSetup(
 124          const ChainType chainType = ChainType::MAIN,
 125          TestOpts = {});
 126  };
 127  
 128  /** Identical to TestingSetup, but chain set to regtest */
 129  struct RegTestingSetup : public TestingSetup {
 130      RegTestingSetup()
 131          : TestingSetup{ChainType::REGTEST} {}
 132  };
 133  
 134  class CBlock;
 135  struct CMutableTransaction;
 136  class CScript;
 137  
 138  /**
 139   * Testing fixture that pre-creates a 100-block REGTEST-mode block chain
 140   */
 141  struct TestChain100Setup : public TestingSetup {
 142      TestChain100Setup(
 143          const ChainType chain_type = ChainType::REGTEST,
 144          TestOpts = {});
 145  
 146      /**
 147       * Create a new block with just given transactions, coinbase paying to
 148       * scriptPubKey, and try to add it to the current chain.
 149       * If no chainstate is specified, default to the active.
 150       */
 151      CBlock CreateAndProcessBlock(const std::vector<CMutableTransaction>& txns,
 152                                   const CScript& scriptPubKey,
 153                                   Chainstate* chainstate = nullptr);
 154  
 155      /**
 156       * Create a new block with just given transactions, coinbase paying to
 157       * scriptPubKey.
 158       */
 159      CBlock CreateBlock(
 160          const std::vector<CMutableTransaction>& txns,
 161          const CScript& scriptPubKey,
 162          Chainstate& chainstate);
 163  
 164      //! Mine a series of new blocks on the active chain.
 165      void mineBlocks(int num_blocks);
 166  
 167      /**
 168      * Create a transaction, optionally setting the fee based on the feerate.
 169      * Note: The feerate may not be met exactly depending on whether the signatures can have different sizes.
 170      *
 171      * @param input_transactions   The transactions to spend
 172      * @param inputs               Outpoints with which to construct transaction vin.
 173      * @param input_height         The height of the block that included the input transactions.
 174      * @param input_signing_keys   The keys to spend the input transactions.
 175      * @param outputs              Transaction vout.
 176      * @param feerate              The feerate the transaction should pay.
 177      * @param fee_output           The index of the output to take the fee from.
 178      * @return The transaction and the fee it pays
 179      */
 180      std::pair<CMutableTransaction, CAmount> CreateValidTransaction(const std::vector<CTransactionRef>& input_transactions,
 181                                                                     const std::vector<COutPoint>& inputs,
 182                                                                     int input_height,
 183                                                                     const std::vector<CKey>& input_signing_keys,
 184                                                                     const std::vector<CTxOut>& outputs,
 185                                                                     const std::optional<CFeeRate>& feerate,
 186                                                                     const std::optional<uint32_t>& fee_output);
 187      /**
 188       * Create a transaction and, optionally, submit to the mempool.
 189       *
 190       * @param input_transactions   The transactions to spend
 191       * @param inputs               Outpoints with which to construct transaction vin.
 192       * @param input_height         The height of the block that included the input transaction(s).
 193       * @param input_signing_keys   The keys to spend inputs.
 194       * @param outputs              Transaction vout.
 195       * @param submit               Whether or not to submit to mempool
 196       */
 197      CMutableTransaction CreateValidMempoolTransaction(const std::vector<CTransactionRef>& input_transactions,
 198                                                        const std::vector<COutPoint>& inputs,
 199                                                        int input_height,
 200                                                        const std::vector<CKey>& input_signing_keys,
 201                                                        const std::vector<CTxOut>& outputs,
 202                                                        bool submit = true);
 203  
 204      /**
 205       * Create a 1-in-1-out transaction and, optionally, submit to the mempool.
 206       *
 207       * @param input_transaction  The transaction to spend
 208       * @param input_vout         The vout to spend from the input_transaction
 209       * @param input_height       The height of the block that included the input_transaction
 210       * @param input_signing_key  The key to spend the input_transaction
 211       * @param output_destination Where to send the output
 212       * @param output_amount      How much to send
 213       * @param submit             Whether or not to submit to mempool
 214       */
 215      CMutableTransaction CreateValidMempoolTransaction(CTransactionRef input_transaction,
 216                                                        uint32_t input_vout,
 217                                                        int input_height,
 218                                                        CKey input_signing_key,
 219                                                        CScript output_destination,
 220                                                        CAmount output_amount = CAmount(1 * COIN),
 221                                                        bool submit = true);
 222  
 223      /** Create transactions spending from m_coinbase_txns. These transactions will only spend coins
 224       * that exist in the current chain, but may be premature coinbase spends, have missing
 225       * signatures, or violate some other consensus rules. They should only be used for testing
 226       * mempool consistency. All transactions will have some random number of inputs and outputs
 227       * (between 1 and 24). Transactions may or may not be dependent upon each other; if dependencies
 228       * exit, every parent will always be somewhere in the list before the child so each transaction
 229       * can be submitted in the same order they appear in the list.
 230       * @param[in]   submit      When true, submit transactions to the mempool.
 231       *                          When false, return them but don't submit them.
 232       * @returns A vector of transactions that can be submitted to the mempool.
 233       */
 234      std::vector<CTransactionRef> PopulateMempool(FastRandomContext& det_rand, size_t num_transactions, bool submit);
 235  
 236      /** Mock the mempool minimum feerate by adding a transaction and calling TrimToSize(0),
 237       * simulating the mempool "reaching capacity" and evicting by descendant feerate.  Note that
 238       * this clears the mempool, and the new minimum feerate will depend on the maximum feerate of
 239       * transactions removed, so this must be called while the mempool is empty.
 240       *
 241       * @param target_feerate    The new mempool minimum feerate after this function returns.
 242       *                          Must be above max(incremental feerate, min relay feerate),
 243       *                          or 1sat/vB with default settings.
 244       */
 245      void MockMempoolMinFee(const CFeeRate& target_feerate);
 246  
 247      std::vector<CTransactionRef> m_coinbase_txns; // For convenience, coinbase transactions
 248      CKey coinbaseKey; // private/public key needed to spend coinbase transactions
 249  };
 250  
 251  /**
 252   * Make a test setup that has disk access to the debug.log file disabled. Can
 253   * be used in "hot loops", for example fuzzing or benchmarking.
 254   */
 255  template <class T = const BasicTestingSetup>
 256  std::unique_ptr<T> MakeNoLogFileContext(const ChainType chain_type = ChainType::REGTEST, TestOpts opts = {})
 257  {
 258      opts.extra_args = Cat(
 259          {
 260              "-nodebuglogfile",
 261              "-nodebug",
 262          },
 263          opts.extra_args);
 264  
 265      return std::make_unique<T>(chain_type, opts);
 266  }
 267  
 268  CBlock getBlock13b8a();
 269  
 270  // Make types usable in BOOST_CHECK_* @{
 271  namespace std {
 272  template <typename T> requires std::is_enum_v<T>
 273  inline std::ostream& operator<<(std::ostream& os, const T& e)
 274  {
 275      return os << static_cast<std::underlying_type_t<T>>(e);
 276  }
 277  
 278  template <typename T>
 279  inline std::ostream& operator<<(std::ostream& os, const std::optional<T>& v)
 280  {
 281      return v ? os << *v
 282               : os << "std::nullopt";
 283  }
 284  } // namespace std
 285  
 286  std::ostream& operator<<(std::ostream& os, const arith_uint256& num);
 287  std::ostream& operator<<(std::ostream& os, const uint160& num);
 288  std::ostream& operator<<(std::ostream& os, const uint256& num);
 289  // @}
 290  
 291  /**
 292   * BOOST_CHECK_EXCEPTION predicates to check the specific validation error.
 293   * Use as
 294   * BOOST_CHECK_EXCEPTION(code that throws, exception type, HasReason("foo"));
 295   */
 296  class HasReason
 297  {
 298  public:
 299      explicit HasReason(std::string_view reason) : m_reason(reason) {}
 300      bool operator()(std::string_view s) const { return s.find(m_reason) != std::string_view::npos; }
 301      bool operator()(const std::exception& e) const { return (*this)(e.what()); }
 302  
 303  private:
 304      const std::string m_reason;
 305  };
 306  
 307  static inline std::variant<MappingResult, MappingError> NATPMPRequestPortMap(const CNetAddr &gateway, uint16_t port, uint32_t lifetime, int num_tries = 3, std::chrono::milliseconds timeout_per_try = std::chrono::milliseconds(1000)) {
 308      static CThreadInterrupt interrupt;
 309      return NATPMPRequestPortMap(gateway, port, lifetime, interrupt, num_tries, timeout_per_try);
 310  }
 311  
 312  static inline std::variant<MappingResult, MappingError> PCPRequestPortMap(const PCPMappingNonce &nonce, const CNetAddr &gateway, const CNetAddr &bind, uint16_t port, uint32_t lifetime, int num_tries = 3, std::chrono::milliseconds timeout_per_try = std::chrono::milliseconds(1000)) {
 313      static CThreadInterrupt interrupt;
 314      return PCPRequestPortMap(nonce, gateway, bind, port, lifetime, interrupt, num_tries, timeout_per_try);
 315  }
 316  
 317  #endif // LIMENKA_TEST_UTIL_SETUP_COMMON_H
 318