LCOV - code coverage report
Current view: top level - src/node - miner.cpp (source / functions) Coverage Total Hit
Test: total_coverage.info Lines: 99.4 % 173 172
Test Date: 2026-09-29 06:14:05 Functions: 100.0 % 13 13
Branches: 65.2 % 230 150

             Branch data     Line data    Source code
       1                 :             : // Copyright (c) 2009-2010 Satoshi Nakamoto
       2                 :             : // Copyright (c) 2009-present The Bitcoin Core developers
       3                 :             : // Distributed under the MIT software license, see the accompanying
       4                 :             : // file COPYING or http://www.opensource.org/licenses/mit-license.php.
       5                 :             : 
       6                 :             : #include <node/miner.h>
       7                 :             : 
       8                 :             : #include <chain.h>
       9                 :             : #include <chainparams.h>
      10                 :             : #include <common/args.h>
      11                 :             : #include <consensus/amount.h>
      12                 :             : #include <consensus/consensus.h>
      13                 :             : #include <consensus/merkle.h>
      14                 :             : #include <consensus/params.h>
      15                 :             : #include <consensus/tx_verify.h>
      16                 :             : #include <consensus/validation.h>
      17                 :             : #include <node/blockstorage.h>
      18                 :             : #include <node/mining_args.h>
      19                 :             : #include <node/mining_types.h>
      20                 :             : #include <policy/feerate.h>
      21                 :             : #include <policy/policy.h>
      22                 :             : #include <pow.h>
      23                 :             : #include <primitives/block.h>
      24                 :             : #include <primitives/transaction.h>
      25                 :             : #include <script/script.h>
      26                 :             : #include <sync.h>
      27                 :             : #include <tinyformat.h>
      28                 :             : #include <txgraph.h>
      29                 :             : #include <txmempool.h>
      30                 :             : #include <uint256.h>
      31                 :             : #include <util/check.h>
      32                 :             : #include <util/feefrac.h>
      33                 :             : #include <util/log.h>
      34                 :             : #include <util/result.h>
      35                 :             : #include <util/time.h>
      36                 :             : #include <util/translation.h>
      37                 :             : #include <validation.h>
      38                 :             : #include <versionbits.h>
      39                 :             : 
      40                 :             : #include <algorithm>
      41                 :             : #include <cstddef>
      42                 :             : #include <functional>
      43                 :             : #include <span>
      44                 :             : #include <stdexcept>
      45                 :             : #include <string>
      46                 :             : #include <utility>
      47                 :             : 
      48                 :             : namespace node {
      49                 :             : 
      50                 :       47907 : int64_t GetMinimumTime(const CBlockIndex* pindexPrev, const int64_t difficulty_adjustment_interval)
      51                 :             : {
      52                 :       47907 :     int64_t min_time{pindexPrev->GetMedianTimePast() + 1};
      53                 :             :     // Height of block to be mined.
      54                 :       47907 :     const int height{pindexPrev->nHeight + 1};
      55                 :             :     // Account for BIP94 timewarp rule on all networks. This makes future
      56                 :             :     // activation safer.
      57         [ +  + ]:       47907 :     if (height % difficulty_adjustment_interval == 0) {
      58         [ +  + ]:         449 :         min_time = std::max<int64_t>(min_time, pindexPrev->GetBlockTime() - MAX_TIMEWARP);
      59                 :             :     }
      60                 :             :     // Account for the BIP54 Murch-Zawy rule on all networks: the last block of
      61                 :             :     // a difficulty adjustment period may not be earlier than its first block.
      62         [ +  + ]:       47907 :     if (height % difficulty_adjustment_interval == difficulty_adjustment_interval - 1) {
      63                 :         227 :         const int first_height{height - static_cast<int>(difficulty_adjustment_interval) + 1};
      64         [ -  + ]:         227 :         const CBlockIndex* first_block{Assert(pindexPrev->GetAncestor(first_height))};
      65         [ +  + ]:         450 :         min_time = std::max<int64_t>(min_time, first_block->GetBlockTime());
      66                 :             :     }
      67                 :       47907 :     return min_time;
      68                 :             : }
      69                 :             : 
      70                 :       47816 : int64_t UpdateTime(CBlockHeader* pblock, const Consensus::Params& consensusParams, const CBlockIndex* pindexPrev)
      71                 :             : {
      72                 :       47816 :     int64_t nOldTime = pblock->nTime;
      73         [ +  + ]:       47816 :     int64_t nNewTime{std::max<int64_t>(GetMinimumTime(pindexPrev, consensusParams.DifficultyAdjustmentInterval()),
      74                 :       47816 :                                        TicksSinceEpoch<std::chrono::seconds>(NodeClock::now()))};
      75                 :             : 
      76         [ +  + ]:       47816 :     if (nOldTime < nNewTime) {
      77                 :       36218 :         pblock->nTime = nNewTime;
      78                 :             :     }
      79                 :             : 
      80                 :             :     // Updating time can change work required on testnet:
      81         [ +  + ]:       47816 :     if (consensusParams.fPowAllowMinDifficultyBlocks) {
      82                 :       47656 :         pblock->nBits = GetNextWorkRequired(pindexPrev, pblock, consensusParams);
      83                 :             :     }
      84                 :             : 
      85                 :       47816 :     return nNewTime - nOldTime;
      86                 :             : }
      87                 :             : 
      88                 :        8703 : void RegenerateCommitments(CBlock& block, ChainstateManager& chainman)
      89                 :             : {
      90                 :        8703 :     CMutableTransaction tx{*block.vtx.at(0)};
      91                 :        8703 :     tx.vout.erase(tx.vout.begin() + GetWitnessCommitmentIndex(block));
      92   [ +  -  +  -  :       17406 :     block.vtx.at(0) = MakeTransactionRef(tx);
                   -  + ]
      93                 :             : 
      94   [ +  -  +  -  :       26109 :     const CBlockIndex* prev_block = WITH_LOCK(::cs_main, return chainman.m_blockman.LookupBlockIndex(block.hashPrevBlock));
                   +  - ]
      95         [ +  - ]:        8703 :     chainman.GenerateCoinbaseCommitment(block, prev_block);
      96                 :             : 
      97         [ +  - ]:        8703 :     block.hashMerkleRoot = BlockMerkleRoot(block);
      98                 :        8703 : }
      99                 :             : 
     100                 :       47701 : BlockAssembler::BlockAssembler(Chainstate& chainstate,
     101                 :             :                                const CTxMemPool* mempool,
     102                 :       47701 :                                BlockCreateOptions options)
     103         [ +  + ]:       47701 :     : chainparams{chainstate.m_chainman.GetParams()},
     104         [ +  + ]:       47701 :       m_mempool{options.use_mempool ? mempool : nullptr},
     105                 :       47701 :       m_chainstate{chainstate},
     106         [ +  + ]:       47701 :       m_options{[&] {
     107   [ +  -  +  + ]:       95402 :           if (auto result{CheckMiningOptions(options, /*use_argnames=*/false)}; !result) {
     108   [ +  -  +  - ]:           2 :               throw std::runtime_error(util::ErrorString(result).original);
     109                 :           1 :           }
     110         [ +  - ]:       95400 :           return FlattenMiningOptions(std::move(options));
     111         [ +  + ]:       47701 :       }()}
     112                 :             : {
     113                 :       47701 : }
     114                 :             : 
     115                 :       47700 : void BlockAssembler::resetBlock()
     116                 :             : {
     117                 :             :     // Reserve space for fixed-size block header, txs count, and coinbase tx.
     118         [ -  + ]:       47700 :     nBlockWeight = *Assert(m_options.block_reserved_weight);
     119                 :       47700 :     nBlockSigOpsCost = m_options.coinbase_output_max_additional_sigops;
     120                 :             : 
     121                 :             :     // These counters do not include coinbase tx
     122                 :       47700 :     nBlockTx = 0;
     123                 :       47700 :     nFees = 0;
     124                 :       47700 : }
     125                 :             : 
     126                 :       47700 : std::unique_ptr<CBlockTemplate> BlockAssembler::CreateNewBlock()
     127                 :             : {
     128                 :       47700 :     const auto time_start{SteadyClock::now()};
     129                 :             : 
     130                 :       47700 :     resetBlock();
     131                 :             : 
     132         [ -  + ]:       47700 :     pblocktemplate.reset(new CBlockTemplate());
     133                 :       47700 :     CBlock* const pblock = &pblocktemplate->block; // pointer for convenience
     134                 :             : 
     135                 :             :     // Add dummy coinbase tx as first transaction. It is skipped by the
     136                 :             :     // getblocktemplate RPC and mining interface consumers must not use it.
     137                 :       47700 :     pblock->vtx.emplace_back();
     138                 :             : 
     139                 :       47700 :     LOCK(::cs_main);
     140         [ -  + ]:       47700 :     CBlockIndex* pindexPrev = m_chainstate.m_chain.Tip();
     141         [ -  + ]:       47700 :     assert(pindexPrev != nullptr);
     142                 :       47700 :     nHeight = pindexPrev->nHeight + 1;
     143                 :             : 
     144         [ +  - ]:       47700 :     pblock->nVersion = m_chainstate.m_chainman.m_versionbitscache.ComputeBlockVersion(pindexPrev, chainparams.GetConsensus());
     145                 :             :     // -regtest only: allow overriding block.nVersion with
     146                 :             :     // -blockversion=N to test forking scenarios
     147         [ +  + ]:       47700 :     if (chainparams.MineBlocksOnDemand()) {
     148         [ +  - ]:       95094 :         pblock->nVersion = gArgs.GetIntArg("-blockversion", pblock->nVersion);
     149                 :             :     }
     150                 :             : 
     151                 :       47700 :     pblock->nTime = TicksSinceEpoch<std::chrono::seconds>(NodeClock::now());
     152                 :       47700 :     m_lock_time_cutoff = pindexPrev->GetMedianTimePast();
     153                 :             : 
     154         [ +  + ]:       47700 :     if (m_mempool) {
     155         [ +  - ]:       38973 :         LOCK(m_mempool->cs);
     156                 :       38973 :         m_mempool->StartBlockBuilding();
     157         [ +  - ]:       38973 :         addChunks();
     158         [ +  - ]:       38973 :         m_mempool->StopBlockBuilding();
     159                 :       38973 :     }
     160                 :             : 
     161                 :       47700 :     const auto time_1{SteadyClock::now()};
     162                 :             : 
     163         [ +  + ]:       47700 :     m_last_block_num_txs = nBlockTx;
     164         [ +  + ]:       47700 :     m_last_block_weight = nBlockWeight;
     165                 :             : 
     166                 :             :     // Create coinbase transaction.
     167         [ +  - ]:       47700 :     CMutableTransaction coinbaseTx;
     168                 :             : 
     169                 :             :     // Construct coinbase transaction struct in parallel
     170         [ +  - ]:       47700 :     CoinbaseTx& coinbase_tx{pblocktemplate->m_coinbase_tx};
     171                 :       47700 :     coinbase_tx.version = coinbaseTx.version;
     172                 :             : 
     173         [ +  - ]:       47700 :     coinbaseTx.vin.resize(1);
     174                 :       47700 :     coinbaseTx.vin[0].prevout.SetNull();
     175         [ +  - ]:       47700 :     coinbaseTx.vin[0].nSequence = CTxIn::MAX_SEQUENCE_NONFINAL; // Make sure timelock is enforced.
     176                 :       47700 :     coinbase_tx.sequence = coinbaseTx.vin[0].nSequence;
     177                 :             : 
     178                 :             :     // Add an output that spends the full coinbase reward.
     179         [ +  - ]:       47700 :     coinbaseTx.vout.resize(1);
     180                 :       47700 :     coinbaseTx.vout[0].scriptPubKey = m_options.coinbase_output_script;
     181                 :             :     // Block subsidy + fees
     182         [ +  - ]:       47700 :     const CAmount block_reward{nFees + GetBlockSubsidy(nHeight, chainparams.GetConsensus())};
     183         [ +  - ]:       47700 :     coinbaseTx.vout[0].nValue = block_reward;
     184                 :       47700 :     coinbase_tx.block_reward_remaining = block_reward;
     185                 :             : 
     186                 :             :     // Start the coinbase scriptSig with the block height as required by BIP34.
     187                 :             :     // Mining clients are expected to append extra data to this prefix, so
     188                 :             :     // increasing its length would reduce the space they can use and may break
     189                 :             :     // existing clients.
     190         [ +  - ]:       47700 :     coinbaseTx.vin[0].scriptSig = CScript() << nHeight;
     191                 :             :     // Set script_sig_prefix here, so IPC mining clients are not affected by
     192                 :             :     // the optional scriptSig padding below. They provide their own extraNonce,
     193                 :             :     // and in a typical setup a pool name or realistic extraNonce already makes
     194                 :             :     // the scriptSig long enough.
     195                 :       47700 :     coinbase_tx.script_sig_prefix = coinbaseTx.vin[0].scriptSig;
     196         [ +  + ]:       47700 :     if (nHeight <= 16) {
     197                 :             :         // For blocks at heights <= 16, the BIP34-encoded height alone is only
     198                 :             :         // one byte. Consensus requires coinbase scriptSigs to be at least two
     199                 :             :         // bytes long (bad-cb-length), so an OP_0 is always appended at those
     200                 :             :         // heights.
     201         [ +  - ]:        2919 :         coinbaseTx.vin[0].scriptSig << OP_0;
     202                 :             :     }
     203         [ -  + ]:       47700 :     Assert(nHeight > 0);
     204                 :       47700 :     coinbaseTx.nLockTime = static_cast<uint32_t>(nHeight - 1);
     205                 :       47700 :     coinbase_tx.lock_time = coinbaseTx.nLockTime;
     206                 :             : 
     207   [ +  -  -  + ]:       95400 :     pblock->vtx[0] = MakeTransactionRef(std::move(coinbaseTx));
     208         [ +  - ]:       47700 :     m_chainstate.m_chainman.GenerateCoinbaseCommitment(*pblock, pindexPrev);
     209                 :             : 
     210         [ +  + ]:       47700 :     const CTransactionRef& final_coinbase{pblock->vtx[0]};
     211         [ +  + ]:       47700 :     if (final_coinbase->HasWitness()) {
     212         [ -  + ]:       47002 :         const auto& witness_stack{final_coinbase->vin[0].scriptWitness.stack};
     213                 :             :         // Consensus requires the coinbase witness stack to have exactly one
     214                 :             :         // element of 32 bytes.
     215   [ -  +  +  -  :       47002 :         Assert(witness_stack.size() == 1 && witness_stack[0].size() == 32);
          -  +  -  +  -  
                      + ]
     216   [ -  +  -  + ]:       47002 :         coinbase_tx.witness = uint256(witness_stack[0]);
     217                 :             :     }
     218         [ +  - ]:       47700 :     if (const int witness_index = GetWitnessCommitmentIndex(*pblock); witness_index != NO_WITNESS_COMMITMENT) {
     219   [ +  -  -  +  :       47700 :         Assert(witness_index >= 0 && static_cast<size_t>(witness_index) < final_coinbase->vout.size());
             -  +  -  + ]
     220         [ +  - ]:       47700 :         coinbase_tx.required_outputs.push_back(final_coinbase->vout[witness_index]);
     221                 :             :     }
     222                 :             : 
     223         [ +  - ]:       47700 :     LogInfo("CreateNewBlock(): block weight: %u txs: %u fees: %ld sigops %d\n", GetBlockWeight(*pblock), nBlockTx, nFees, nBlockSigOpsCost);
     224                 :             : 
     225                 :             :     // Fill in header
     226                 :       47700 :     pblock->hashPrevBlock  = pindexPrev->GetBlockHash();
     227         [ +  - ]:       47700 :     UpdateTime(pblock, chainparams.GetConsensus(), pindexPrev);
     228         [ +  - ]:       47700 :     pblock->nBits          = GetNextWorkRequired(pindexPrev, pblock, chainparams.GetConsensus());
     229                 :       47700 :     pblock->nNonce         = 0;
     230                 :             : 
     231         [ +  + ]:       47700 :     if (m_options.test_block_validity) {
     232   [ +  -  +  + ]:       47245 :         if (BlockValidationState state{TestBlockValidity(m_chainstate, *pblock, /*check_pow=*/false, /*check_merkle_root=*/false)}; !state.IsValid()) {
     233   [ +  -  +  -  :          10 :             throw std::runtime_error(strprintf("TestBlockValidity failed: %s", state.ToString()));
                   +  - ]
     234                 :       47245 :         }
     235                 :             :     }
     236                 :       47695 :     const auto time_2{SteadyClock::now()};
     237                 :             : 
     238   [ +  -  +  -  :       47695 :     LogDebug(BCLog::BENCH, "CreateNewBlock() chunks: %.2fms, validity: %.2fms (total %.2fms)\n",
                   +  - ]
     239                 :             :              Ticks<MillisecondsDouble>(time_1 - time_start),
     240                 :             :              Ticks<MillisecondsDouble>(time_2 - time_1),
     241                 :             :              Ticks<MillisecondsDouble>(time_2 - time_start));
     242                 :             : 
     243                 :       47695 :     return std::move(pblocktemplate);
     244         [ +  - ]:       95395 : }
     245                 :             : 
     246                 :       83564 : bool BlockAssembler::TestChunkBlockLimits(int64_t chunk_weight, int64_t chunk_sigops_cost) const
     247                 :             : {
     248                 :             :     // block_max_weight has been flattened before block assembly limit checks.
     249         [ -  + ]:       83564 :     Assert(m_options.block_max_weight);
     250         [ +  + ]:       83564 :     if (nBlockWeight + chunk_weight >= m_options.block_max_weight) {
     251                 :             :         return false;
     252                 :             :     }
     253         [ +  + ]:       50434 :     if (nBlockSigOpsCost + chunk_sigops_cost >= MAX_BLOCK_SIGOPS_COST) {
     254                 :           2 :         return false;
     255                 :             :     }
     256                 :             :     return true;
     257                 :             : }
     258                 :             : 
     259                 :             : // Perform transaction-level checks before adding to block:
     260                 :             : // - transaction finality (locktime)
     261                 :       50432 : bool BlockAssembler::TestChunkTransactions(const std::vector<CTxMemPoolEntryRef>& txs) const
     262                 :             : {
     263         [ +  + ]:      102257 :     for (const auto tx : txs) {
     264         [ +  + ]:       51827 :         if (!IsFinalTx(tx.get().GetTx(), nHeight, m_lock_time_cutoff)) {
     265                 :             :             return false;
     266                 :             :         }
     267                 :             :     }
     268                 :             :     return true;
     269                 :             : }
     270                 :             : 
     271                 :       51825 : void BlockAssembler::AddToBlock(const CTxMemPoolEntry& entry)
     272                 :             : {
     273   [ +  -  +  - ]:      103650 :     pblocktemplate->block.vtx.emplace_back(entry.GetSharedTx());
     274                 :       51825 :     pblocktemplate->vTxFees.push_back(entry.GetFee());
     275                 :       51825 :     pblocktemplate->vTxSigOpsCost.push_back(entry.GetSigOpCost());
     276         [ +  + ]:       51825 :     nBlockWeight += entry.GetTxWeight();
     277                 :       51825 :     ++nBlockTx;
     278         [ +  + ]:       51825 :     nBlockSigOpsCost += entry.GetSigOpCost();
     279                 :       51825 :     nFees += entry.GetFee();
     280                 :             : 
     281         [ +  + ]:       51825 :     if (*m_options.print_modified_fee) {
     282   [ +  -  +  -  :          88 :         LogInfo("fee rate %s txid %s\n",
             +  -  +  - ]
     283                 :             :                   CFeeRate(entry.GetModifiedFee(), entry.GetTxSize()).ToString(),
     284                 :             :                   entry.GetTx().GetHash().ToString());
     285                 :             :     }
     286                 :       51825 : }
     287                 :             : 
     288                 :       38973 : void BlockAssembler::addChunks()
     289                 :             : {
     290                 :             :     // Limit the number of attempts to add transactions to the block when it is
     291                 :             :     // close to full; this is just a simple heuristic to finish quickly if the
     292                 :             :     // mempool has a lot of entries.
     293                 :       38973 :     const int64_t MAX_CONSECUTIVE_FAILURES = 1000;
     294                 :       38973 :     constexpr int32_t BLOCK_FULL_ENOUGH_WEIGHT_DELTA = 4000;
     295                 :       38973 :     int64_t nConsecutiveFailed = 0;
     296                 :             : 
     297                 :       38973 :     std::vector<CTxMemPoolEntry::CTxMemPoolEntryRef> selected_transactions;
     298         [ +  - ]:       38973 :     selected_transactions.reserve(MAX_CLUSTER_COUNT_LIMIT);
     299                 :       38973 :     FeePerWeight chunk_feerate;
     300                 :             : 
     301                 :             :     // This fills selected_transactions
     302         [ +  - ]:       38973 :     chunk_feerate = m_mempool->GetBlockBuilderChunk(selected_transactions);
     303                 :       38973 :     FeePerVSize chunk_feerate_vsize = ToFeePerVSize(chunk_feerate);
     304                 :             : 
     305   [ -  +  +  + ]:      122532 :     while (selected_transactions.size() > 0) {
     306                 :             :         // Check to see if min fee rate is still respected.
     307         [ +  + ]:       83610 :         if (ByRatio{chunk_feerate_vsize} < ByRatio{m_options.block_min_fee_rate->GetFeePerVSize()}) {
     308                 :             :             // Everything else we might consider has a lower feerate
     309                 :             :             return;
     310                 :             :         }
     311                 :             : 
     312                 :       83564 :         int64_t chunk_sig_ops = 0;
     313                 :       83564 :         int64_t chunk_weight = 0;
     314         [ +  + ]:      170602 :         for (const auto& tx : selected_transactions) {
     315                 :       87038 :             chunk_sig_ops += tx.get().GetSigOpCost();
     316                 :       87038 :             chunk_weight += tx.get().GetTxWeight();
     317                 :             :         }
     318                 :             : 
     319                 :             :         // Check to see if this chunk will fit.
     320   [ +  -  +  +  :       83564 :         if (!TestChunkBlockLimits(chunk_weight, chunk_sig_ops) || !TestChunkTransactions(selected_transactions)) {
             +  -  +  + ]
     321                 :             :             // This chunk won't fit, so we skip it and will try the next best one.
     322                 :       33134 :             m_mempool->SkipBuilderChunk();
     323                 :       33134 :             ++nConsecutiveFailed;
     324                 :             : 
     325                 :             :             // block_max_weight has been flattened before block assembly limit checks.
     326         [ -  + ]:       33134 :             Assert(m_options.block_max_weight);
     327         [ +  + ]:       33134 :             if (nConsecutiveFailed > MAX_CONSECUTIVE_FAILURES && nBlockWeight +
     328         [ -  + ]:           5 :                     BLOCK_FULL_ENOUGH_WEIGHT_DELTA > *m_options.block_max_weight) {
     329                 :             :                 // Give up if we're close to full and haven't succeeded in a while
     330                 :             :                 return;
     331                 :             :             }
     332                 :             :         } else {
     333                 :       50430 :             m_mempool->IncludeBuilderChunk();
     334                 :             : 
     335                 :             :             // This chunk will fit, so add it to the block.
     336                 :       50430 :             nConsecutiveFailed = 0;
     337         [ +  + ]:      102255 :             for (const auto& tx : selected_transactions) {
     338         [ +  - ]:       51825 :                 AddToBlock(tx);
     339                 :             :             }
     340         [ +  - ]:       50430 :             pblocktemplate->m_package_feerates.emplace_back(chunk_feerate_vsize);
     341                 :             :         }
     342                 :             : 
     343         [ +  - ]:       83559 :         selected_transactions.clear();
     344         [ +  - ]:       83559 :         chunk_feerate = m_mempool->GetBlockBuilderChunk(selected_transactions);
     345                 :       83559 :         chunk_feerate_vsize = ToFeePerVSize(chunk_feerate);
     346                 :             :     }
     347                 :       38973 : }
     348                 :             : 
     349                 :          55 : void AddMerkleRootAndCoinbase(CBlock& block, CTransactionRef coinbase, uint32_t version, uint32_t timestamp, uint32_t nonce)
     350                 :             : {
     351   [ -  +  -  + ]:          55 :     if (block.vtx.size() == 0) {
     352                 :           0 :         block.vtx.emplace_back(coinbase);
     353                 :             :     } else {
     354                 :          55 :         block.vtx[0] = coinbase;
     355                 :             :     }
     356                 :          55 :     block.nVersion = version;
     357                 :          55 :     block.nTime = timestamp;
     358                 :          55 :     block.nNonce = nonce;
     359                 :          55 :     block.hashMerkleRoot = BlockMerkleRoot(block);
     360                 :             : 
     361                 :             :     // Reset cached checks
     362                 :          55 :     block.m_checked_witness_commitment = false;
     363                 :          55 :     block.m_checked_merkle_root = false;
     364                 :          55 :     block.fChecked = false;
     365                 :          55 : }
     366                 :             : 
     367                 :             : } // namespace node
        

Generated by: LCOV version 2.0-1