Kagome
Polkadot Runtime Engine in C++17
proposer_impl.cpp
Go to the documentation of this file.
1 
7 
9 
10 namespace {
11  constexpr const char *kTransactionsIncludedInBlock =
12  "kagome_proposer_number_of_transactions";
13 }
14 
15 namespace kagome::authorship {
16 
18  std::shared_ptr<BlockBuilderFactory> block_builder_factory,
19  std::shared_ptr<transaction_pool::TransactionPool> transaction_pool,
20  std::shared_ptr<primitives::events::ExtrinsicSubscriptionEngine>
21  ext_sub_engine,
22  std::shared_ptr<subscription::ExtrinsicEventKeyRepository>
23  extrinsic_event_key_repo)
24  : block_builder_factory_{std::move(block_builder_factory)},
25  transaction_pool_{std::move(transaction_pool)},
26  ext_sub_engine_{std::move(ext_sub_engine)},
27  extrinsic_event_key_repo_{std::move(extrinsic_event_key_repo)} {
28  BOOST_ASSERT(block_builder_factory_);
29  BOOST_ASSERT(transaction_pool_);
30  BOOST_ASSERT(ext_sub_engine_);
31  BOOST_ASSERT(extrinsic_event_key_repo_);
32 
33  metrics_registry_->registerGaugeFamily(
34  kTransactionsIncludedInBlock,
35  "Number of transactions included in block");
37  metrics_registry_->registerGaugeMetric(kTransactionsIncludedInBlock);
38  }
39 
40  outcome::result<primitives::Block> ProposerImpl::propose(
41  const primitives::BlockInfo &parent_block,
42  const primitives::InherentData &inherent_data,
43  const primitives::Digest &inherent_digest) {
44  OUTCOME_TRY(block_builder,
45  block_builder_factory_->make(parent_block, inherent_digest));
46 
47  auto inherent_xts_res = block_builder->getInherentExtrinsics(inherent_data);
48  if (not inherent_xts_res) {
49  logger_->error("BlockBuilder->inherent_extrinsics failed with error: {}",
50  inherent_xts_res.error().message());
51  return inherent_xts_res.error();
52  }
53  const auto &inherent_xts = inherent_xts_res.value();
54 
55  for (const auto &xt : inherent_xts) {
56  SL_DEBUG(logger_, "Adding inherent extrinsic: {}", xt.data);
57  auto inserted_res = block_builder->pushExtrinsic(xt);
58  if (not inserted_res) {
59  if (BlockBuilderError::EXHAUSTS_RESOURCES == inserted_res.error()) {
60  SL_WARN(logger_,
61  "Dropping non-mandatory inherent extrinsic from overweight "
62  "block.");
63  } else if (BlockBuilderError::BAD_MANDATORY == inserted_res.error()) {
64  SL_ERROR(logger_,
65  "Mandatory inherent extrinsic returned error. Block cannot "
66  "be produced.");
67  return inserted_res.error();
68  } else {
69  SL_ERROR(
70  logger_,
71  "Inherent extrinsic returned unexpected error: {}. Dropping.",
72  inserted_res.error().message());
73  return inserted_res.error();
74  }
75  }
76  }
77 
78  auto remove_res = transaction_pool_->removeStale(parent_block.number);
79  if (remove_res.has_error()) {
80  SL_ERROR(logger_,
81  "Stale transactions remove failure: {}, Parent is {}",
82  remove_res.error().message(),
83  parent_block);
84  }
85  const auto &ready_txs = transaction_pool_->getReadyTransactions();
86 
87  bool transaction_pushed = false;
88  bool hit_block_size_limit = false;
89 
90  auto skipped = 0;
91  auto block_size_limit = kBlockSizeLimit;
92  const auto kMaxVarintLength = 9;
93  // we move estimateBlockSize() out of the loop for optimization purposes.
94  // to avoid varint bytes length recalculation which indicates extrinsics
95  // quantity, we add the maximum varint length at once.
96  auto block_size = block_builder->estimateBlockSize() + kMaxVarintLength;
97  // at the moment block_size includes block headers and a counter to hold a
98  // number of transactions to be pushed to the block
99 
100  size_t included_tx_count = 0;
101  for (const auto &[hash, tx] : ready_txs) {
102  const auto &tx_ref = tx;
103  scale::ScaleEncoderStream s(true);
104  s << tx_ref->ext;
105  auto estimate_tx_size = s.size();
106 
107  if (block_size + estimate_tx_size > block_size_limit) {
108  if (skipped < kMaxSkippedTransactions) {
109  ++skipped;
110  SL_DEBUG(logger_,
111  "Transaction would overflow the block size limit, but will "
112  "try {} more transactions before quitting.",
113  kMaxSkippedTransactions - skipped);
114  continue;
115  }
116  SL_DEBUG(logger_,
117  "Reached block size limit, proceeding with proposing.");
118  hit_block_size_limit = true;
119  break;
120  }
121 
122  SL_DEBUG(logger_, "Adding extrinsic: {}", tx_ref->ext.data);
123  auto inserted_res = block_builder->pushExtrinsic(tx->ext);
124  if (not inserted_res) {
125  if (BlockBuilderError::EXHAUSTS_RESOURCES == inserted_res.error()) {
126  if (skipped < kMaxSkippedTransactions) {
127  ++skipped;
128  SL_DEBUG(logger_,
129  "Block seems full, but will try {} more transactions "
130  "before quitting.",
131  kMaxSkippedTransactions - skipped);
132  } else { // maximum amount of txs is pushed
133  SL_DEBUG(logger_, "Block is full, proceed with proposing.");
134  break;
135  }
136  } else { // any other error than exhausted resources
137  logger_->warn("Extrinsic {} was not added to the block. Reason: {}",
138  tx->ext.data,
139  inserted_res.error().message());
140  }
141  } else { // tx was pushed successfully
142  block_size += estimate_tx_size;
143  transaction_pushed = true;
144  ++included_tx_count;
145  }
146  }
147  metric_tx_included_in_block_->set(included_tx_count);
148 
149  if (hit_block_size_limit and not transaction_pushed) {
150  SL_WARN(logger_,
151  "Hit block size limit of `{}` without including any transaction!",
152  block_size_limit);
153  }
154 
155  OUTCOME_TRY(block, block_builder->bake());
156 
157  for (const auto &[hash, tx] : ready_txs) {
158  auto removed_res = transaction_pool_->removeOne(hash);
159  if (not removed_res) {
160  logger_->error(
161  "Can't remove extrinsic {} after adding to the block. Reason: {}",
162  hash,
163  removed_res.error().message());
164  }
165  }
166 
167  return std::move(block);
168  }
169 
170 } // namespace kagome::authorship
std::shared_ptr< BlockBuilderFactory > block_builder_factory_
ProposerImpl(std::shared_ptr< BlockBuilderFactory > block_builder_factory, std::shared_ptr< transaction_pool::TransactionPool > transaction_pool, std::shared_ptr< primitives::events::ExtrinsicSubscriptionEngine > ext_sub_engine, std::shared_ptr< subscription::ExtrinsicEventKeyRepository > extrinsic_event_key_repo)
std::shared_ptr< subscription::ExtrinsicEventKeyRepository > extrinsic_event_key_repo_
static constexpr size_t kBlockSizeLimit
Default block size limit in bytes.
virtual void set(double val)=0
Set the gauge to the given value.
std::shared_ptr< transaction_pool::TransactionPool > transaction_pool_
std::shared_ptr< primitives::events::ExtrinsicSubscriptionEngine > ext_sub_engine_
metrics::Gauge * metric_tx_included_in_block_
metrics::RegistryPtr metrics_registry_
static constexpr auto kMaxSkippedTransactions
outcome::result< primitives::Block > propose(const primitives::BlockInfo &parent_block, const primitives::InherentData &inherent_data, const primitives::Digest &inherent_digest) override