Skip to content

Commit a34e6a2

Browse files
committed
fcmp++: hash the tree root in the PoW hash
1 parent 7ca251c commit a34e6a2

22 files changed

+220
-64
lines changed

src/blockchain_db/blockchain_db.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1807,6 +1807,8 @@ class BlockchainDB
18071807
*/
18081808
virtual bool for_all_alt_blocks(std::function<bool(const crypto::hash &blkid, const alt_block_data_t &data, const cryptonote::blobdata_ref *blob)> f, bool include_blob = false) const = 0;
18091809

1810+
virtual void advance_tree_one_block(const uint64_t block_idx) = 0;
1811+
18101812
// TODO: description and make private
18111813
virtual void grow_tree(const uint64_t block_id, std::vector<fcmp_pp::curve_trees::OutputContext> &&new_outputs) = 0;
18121814

src/blockchain_db/lmdb/db_lmdb.cpp

Lines changed: 30 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -819,13 +819,6 @@ void BlockchainLMDB::add_block(const block& blk, size_t block_weight, uint64_t l
819819
throw0(BLOCK_PARENT_DNE("Top block is not new block's parent"));
820820
}
821821

822-
// Grow the tree with outputs that are no longer locked once this block is in the chain
823-
auto unlocked_outputs = this->get_outs_at_last_locked_block_id(m_height);
824-
this->grow_tree(m_height, std::move(unlocked_outputs));
825-
826-
// Now that we've used the unlocked leaves to grow the tree, we can delete them from the locked outputs table
827-
this->del_locked_outs_at_block_id(m_height);
828-
829822
int result = 0;
830823

831824
MDB_val_set(key, m_height);
@@ -1405,6 +1398,31 @@ void BlockchainLMDB::remove_spent_key(const crypto::key_image& k_image)
14051398
}
14061399
}
14071400

1401+
void BlockchainLMDB::advance_tree_one_block(const uint64_t blk_idx)
1402+
{
1403+
LOG_PRINT_L3("BlockchainLMDB::" << __func__);
1404+
check_open();
1405+
1406+
if (blk_idx == 0)
1407+
throw0(DB_ERROR("Expected to advance to blk_idx > 0"));
1408+
1409+
if (blk_idx == 1)
1410+
{
1411+
// If we're advancing 1 block past genesis, make sure to initialize the tree for the genesis block.
1412+
// We grow the genesis block with empty outputs, since no outputs should be spendable once it's in the chain.
1413+
this->grow_tree(0, {});
1414+
}
1415+
1416+
// Now we can advance the tree 1 block
1417+
auto unlocked_outputs = this->get_outs_at_last_locked_block_id(blk_idx);
1418+
1419+
// Grow the tree with outputs that are spendable once blk_idx is in the chain (i.e. they can be included starting in blk_idx+1)
1420+
this->grow_tree(blk_idx, std::move(unlocked_outputs));
1421+
1422+
// Now that we've used the unlocked leaves to grow the tree, we delete them from the locked outputs table
1423+
this->del_locked_outs_at_block_id(blk_idx);
1424+
}
1425+
14081426
void BlockchainLMDB::grow_tree(const uint64_t blk_idx, std::vector<fcmp_pp::curve_trees::OutputContext> &&new_outputs)
14091427
{
14101428
LOG_PRINT_L3("BlockchainLMDB::" << __func__);
@@ -1426,13 +1444,15 @@ void BlockchainLMDB::grow_tree(const uint64_t blk_idx, std::vector<fcmp_pp::curv
14261444
// Get the block idx corresponding to the current tree in the db
14271445
const uint64_t tree_block_idx = this->get_tree_block_idx();
14281446
if (blk_idx > (tree_block_idx + 1))
1429-
throw0(DB_ERROR("The chain has extended too far past the tree"));
1447+
throw0(DB_ERROR(("The chain has extended too far past the tree (blk_idx=" + std::to_string(blk_idx) + ", tree_block_idx=" + std::to_string(tree_block_idx) + ")").c_str()));
14301448
if (tree_block_idx >= blk_idx)
14311449
{
14321450
LOG_PRINT_L1("Skip re-growing the tree at block " << blk_idx << ", waiting for the chain to catch up to the tree's block " << tree_block_idx);
14331451
return;
14341452
}
1435-
// blk_idx == (tree_block_idx + 1), we're growing the tree 1 block
1453+
// We're supposed to be growing the tree to the given blk_idx
1454+
if (blk_idx != (tree_block_idx + 1))
1455+
throw0(DB_ERROR(("Mismatch block idx to tree tip (blk_idx=" + std::to_string(blk_idx) + ", tree_block_idx=" + std::to_string(tree_block_idx) + ")").c_str()));
14361456

14371457
// Get the prev block's tree edge (i.e. the current tree edge before growing)
14381458
prev_blk_idx = blk_idx - 1;
@@ -7585,12 +7605,7 @@ void BlockchainLMDB::migrate_5_6()
75857605
}
75867606
}
75877607

7588-
// Get leaf tuples of outputs that unlock once i is in the chain, since i is their last locked block
7589-
auto unlocked_outputs = this->get_outs_at_last_locked_block_id(i);
7590-
this->grow_tree(i, std::move(unlocked_outputs));
7591-
7592-
// Now that we've used the unlocked leaves to grow the tree, we delete them from the locked outputs table
7593-
this->del_locked_outs_at_block_id(i);
7608+
this->advance_tree_one_block(i);
75947609

75957610
LOGIF(el::Level::Info)
75967611
{

src/blockchain_db/lmdb/db_lmdb.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -342,6 +342,8 @@ class BlockchainLMDB : public BlockchainDB
342342
, const std::vector<std::pair<transaction, blobdata>>& txs
343343
);
344344

345+
virtual void advance_tree_one_block(const uint64_t blk_idx);
346+
345347
virtual void set_batch_transactions(bool batch_transactions);
346348
virtual bool batch_start(uint64_t batch_num_blocks=0, uint64_t batch_bytes=0);
347349
virtual void batch_commit();

src/blockchain_db/testdb.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,7 @@ class BaseTestDB: public cryptonote::BlockchainDB {
117117
virtual void add_tx_amount_output_indices(const uint64_t tx_index, const std::vector<uint64_t>& amount_output_indices) override {}
118118
virtual void add_spent_key(const crypto::key_image& k_image) override {}
119119
virtual void remove_spent_key(const crypto::key_image& k_image) override {}
120+
virtual void advance_tree_one_block(const uint64_t block_idx) override {}
120121
virtual void grow_tree(const uint64_t block_idx, std::vector<fcmp_pp::curve_trees::OutputContext> &&new_outputs) override {};
121122
virtual std::pair<uint64_t, fcmp_pp::curve_trees::PathBytes> get_last_path(const uint64_t block_idx) const override { return {0, fcmp_pp::curve_trees::PathBytes{}}; };
122123
virtual void trim_tree(const uint64_t new_n_leaf_tuples, const uint64_t trim_block_id) override {};

src/crypto/crypto.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -353,6 +353,9 @@ namespace crypto {
353353
inline std::ostream &operator <<(std::ostream &o, const crypto::view_tag &v) {
354354
epee::to_hex::formatted(o, epee::as_byte_span(v)); return o;
355355
}
356+
inline std::ostream &operator <<(std::ostream &o, const crypto::ec_point &v) {
357+
epee::to_hex::formatted(o, epee::as_byte_span(v)); return o;
358+
}
356359

357360
const extern crypto::public_key null_pkey;
358361
const extern crypto::secret_key null_skey;

src/cryptonote_basic/cryptonote_basic.h

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -478,8 +478,8 @@ namespace cryptonote
478478

479479
public:
480480
block(): block_header(), hash_valid(false) {}
481-
block(const block &b): block_header(b), hash_valid(false), miner_tx(b.miner_tx), tx_hashes(b.tx_hashes) { if (b.is_hash_valid()) { hash = b.hash; set_hash_valid(true); } }
482-
block &operator=(const block &b) { block_header::operator=(b); hash_valid = false; miner_tx = b.miner_tx; tx_hashes = b.tx_hashes; if (b.is_hash_valid()) { hash = b.hash; set_hash_valid(true); } return *this; }
481+
block(const block &b): block_header(b), hash_valid(false), miner_tx(b.miner_tx), tx_hashes(b.tx_hashes), fcmp_pp_tree_root(b.fcmp_pp_tree_root) { if (b.is_hash_valid()) { hash = b.hash; set_hash_valid(true); } }
482+
block &operator=(const block &b) { block_header::operator=(b); hash_valid = false; miner_tx = b.miner_tx; tx_hashes = b.tx_hashes; fcmp_pp_tree_root = b.fcmp_pp_tree_root; if (b.is_hash_valid()) { hash = b.hash; set_hash_valid(true); } return *this; }
483483
void invalidate_hashes() { set_hash_valid(false); }
484484
bool is_hash_valid() const { return hash_valid.load(std::memory_order_acquire); }
485485
void set_hash_valid(bool v) const { hash_valid.store(v,std::memory_order_release); }
@@ -488,6 +488,8 @@ namespace cryptonote
488488
transaction miner_tx;
489489
std::vector<crypto::hash> tx_hashes;
490490

491+
crypto::ec_point fcmp_pp_tree_root;
492+
491493
// hash cash
492494
mutable crypto::hash hash;
493495

@@ -500,6 +502,8 @@ namespace cryptonote
500502
FIELD(tx_hashes)
501503
if (tx_hashes.size() > CRYPTONOTE_MAX_TX_PER_BLOCK)
502504
return false;
505+
if (major_version >= HF_VERSION_FCMP_PLUS_PLUS)
506+
FIELD(fcmp_pp_tree_root)
503507
END_SERIALIZE()
504508
};
505509

src/cryptonote_basic/cryptonote_boost_serialization.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,8 @@ namespace boost
202202
//------------------
203203
a & b.miner_tx;
204204
a & b.tx_hashes;
205+
if (b.major_version >= HF_VERSION_FCMP_PLUS_PLUS)
206+
a & b.fcmp_pp_tree_root;
205207
}
206208

207209
template <class Archive>

src/cryptonote_basic/cryptonote_format_utils.cpp

Lines changed: 25 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,18 @@ namespace cryptonote
9999
const uint64_t bp_clawback = (bp_base * n_padded_outputs - bp_size) * 4 / 5;
100100
return bp_clawback;
101101
}
102+
103+
static void get_tree_hash(const std::vector<crypto::hash>& tx_hashes, crypto::hash& h)
104+
{
105+
tree_hash(tx_hashes.data(), tx_hashes.size(), h);
106+
}
107+
108+
static crypto::hash get_tree_hash(const std::vector<crypto::hash>& tx_hashes)
109+
{
110+
crypto::hash h = null_hash;
111+
get_tree_hash(tx_hashes, h);
112+
return h;
113+
}
102114
//---------------------------------------------------------------
103115
}
104116

@@ -1464,7 +1476,7 @@ namespace cryptonote
14641476
blobdata get_block_hashing_blob(const block& b)
14651477
{
14661478
blobdata blob = t_serializable_object_to_blob(static_cast<block_header>(b));
1467-
crypto::hash tree_root_hash = get_tx_tree_hash(b);
1479+
crypto::hash tree_root_hash = get_tree_hash(b);
14681480
blob.append(reinterpret_cast<const char*>(&tree_root_hash), sizeof(tree_root_hash));
14691481
blob.append(tools::get_varint_data(b.tx_hashes.size()+1));
14701482
return blob;
@@ -1607,29 +1619,23 @@ namespace cryptonote
16071619
return t_serializable_object_to_blob(tx, b_blob);
16081620
}
16091621
//---------------------------------------------------------------
1610-
void get_tx_tree_hash(const std::vector<crypto::hash>& tx_hashes, crypto::hash& h)
1611-
{
1612-
tree_hash(tx_hashes.data(), tx_hashes.size(), h);
1613-
}
1614-
//---------------------------------------------------------------
1615-
crypto::hash get_tx_tree_hash(const std::vector<crypto::hash>& tx_hashes)
1616-
{
1617-
crypto::hash h = null_hash;
1618-
get_tx_tree_hash(tx_hashes, h);
1619-
return h;
1620-
}
1621-
//---------------------------------------------------------------
1622-
crypto::hash get_tx_tree_hash(const block& b)
1622+
crypto::hash get_tree_hash(const block& b)
16231623
{
1624-
std::vector<crypto::hash> txs_ids;
1625-
txs_ids.reserve(1 + b.tx_hashes.size());
1624+
std::vector<crypto::hash> hashes;
1625+
hashes.reserve(1 + 1 + b.tx_hashes.size());
1626+
// 1. FCMP++ root
1627+
static_assert(sizeof(crypto::hash) == sizeof(crypto::ec_point), "expected sizeof hash == sizeof ec_point");
1628+
if (b.major_version >= HF_VERSION_FCMP_PLUS_PLUS)
1629+
hashes.push_back((crypto::hash&)b.fcmp_pp_tree_root);
1630+
// 2. Miner tx
16261631
crypto::hash h = null_hash;
16271632
size_t bl_sz = 0;
16281633
CHECK_AND_ASSERT_THROW_MES(get_transaction_hash(b.miner_tx, h, bl_sz), "Failed to calculate transaction hash");
1629-
txs_ids.push_back(h);
1634+
hashes.push_back(h);
1635+
// 3. All other txs
16301636
for(auto& th: b.tx_hashes)
1631-
txs_ids.push_back(th);
1632-
return get_tx_tree_hash(txs_ids);
1637+
hashes.push_back(th);
1638+
return get_tree_hash(hashes);
16331639
}
16341640
//---------------------------------------------------------------
16351641
bool is_valid_decomposed_amount(uint64_t amount)

src/cryptonote_basic/cryptonote_format_utils.h

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -260,9 +260,7 @@ namespace cryptonote
260260
bool block_to_blob(const block& b, blobdata& b_blob);
261261
blobdata tx_to_blob(const transaction& b);
262262
bool tx_to_blob(const transaction& b, blobdata& b_blob);
263-
void get_tx_tree_hash(const std::vector<crypto::hash>& tx_hashes, crypto::hash& h);
264-
crypto::hash get_tx_tree_hash(const std::vector<crypto::hash>& tx_hashes);
265-
crypto::hash get_tx_tree_hash(const block& b);
263+
crypto::hash get_tree_hash(const block& b);
266264
bool is_valid_decomposed_amount(uint64_t amount);
267265
void get_hash_stats(uint64_t &tx_hashes_calculated, uint64_t &tx_hashes_cached, uint64_t &block_hashes_calculated, uint64_t & block_hashes_cached);
268266

src/cryptonote_core/blockchain.cpp

Lines changed: 82 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1529,6 +1529,7 @@ bool Blockchain::create_block_template(block& b, const crypto::hash *from_block,
15291529
invalidate_block_template_cache();
15301530
}
15311531

1532+
const uint64_t cur_n_blocks = m_db->height();
15321533
if (from_block)
15331534
{
15341535
//build alternative subchain, front -> mainchain, back -> alternative head
@@ -1589,6 +1590,32 @@ bool Blockchain::create_block_template(block& b, const crypto::hash *from_block,
15891590
b.minor_version = m_hardfork->get_ideal_version();
15901591
b.prev_id = *from_block;
15911592

1593+
if (b.major_version >= HF_VERSION_FCMP_PLUS_PLUS)
1594+
{
1595+
if (alt_chain.size() > CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE)
1596+
{
1597+
// Since the daemon is not wasting cycles calculating alt-trees, it does
1598+
// not know the correct root to use for an alt chain longer than 10
1599+
// blocks. The daemon could theoretically build the altchain's tree in
1600+
// handle_alternative_block, but that seems like a pre-mature
1601+
// optimization. Who wants to mine altchains longer than 10 blocks
1602+
// anyway?
1603+
MERROR("altchain is too long, the daemon is not structured to calculate its FCMP++ tree root");
1604+
return false;
1605+
}
1606+
1607+
if (height > cur_n_blocks)
1608+
{
1609+
MERROR("altchain is longer than the mainchain, so we don't have the FCMP++ tree root saved for its block");
1610+
// Theoretically the daemon could calculate the tree 10 blocks ahead
1611+
// of the mainchain, which would allow us to mine on top of an altchain
1612+
// while growing the mainchain tree (which we need to do anyway), but it
1613+
// seems like pre-mature optimization to support mining on top
1614+
// of altchains longer than the mainchain anyway.
1615+
return false;
1616+
}
1617+
}
1618+
15921619
// cheat and use the weight of the block we start from, virtually certain to be acceptable
15931620
// and use 1.9 times rather than 2 times so we're even more sure
15941621
if (parent_in_main)
@@ -1611,7 +1638,7 @@ bool Blockchain::create_block_template(block& b, const crypto::hash *from_block,
16111638
}
16121639
else
16131640
{
1614-
height = m_db->height();
1641+
height = cur_n_blocks;
16151642
b.major_version = m_hardfork->get_current_version();
16161643
b.minor_version = m_hardfork->get_ideal_version();
16171644
b.prev_id = get_tail_id();
@@ -1635,6 +1662,11 @@ bool Blockchain::create_block_template(block& b, const crypto::hash *from_block,
16351662

16361663
CHECK_AND_ASSERT_MES(diffic, false, "difficulty overhead.");
16371664

1665+
if (b.major_version >= HF_VERSION_FCMP_PLUS_PLUS)
1666+
{
1667+
m_db->get_tree_root_at_blk_idx(height, b.fcmp_pp_tree_root);
1668+
}
1669+
16381670
size_t txs_weight;
16391671
uint64_t fee;
16401672
if (!m_tx_pool.fill_block_template(b, median_weight, already_generated_coins, txs_weight, fee, expected_reward, b.major_version))
@@ -1768,15 +1800,21 @@ bool Blockchain::create_block_template(block& b, const account_public_address& m
17681800
return create_block_template(b, NULL, miner_address, diffic, height, expected_reward, cumulative_weight, ex_nonce, seed_height, seed_hash);
17691801
}
17701802
//------------------------------------------------------------------
1771-
bool Blockchain::get_miner_data(uint8_t& major_version, uint64_t& height, crypto::hash& prev_id, crypto::hash& seed_hash, difficulty_type& difficulty, uint64_t& median_weight, uint64_t& already_generated_coins, std::vector<tx_block_template_backlog_entry>& tx_backlog)
1803+
bool Blockchain::get_miner_data(uint8_t& major_version, uint64_t& height, crypto::hash& prev_id, crypto::ec_point& fcmp_pp_tree_root, crypto::hash& seed_hash, difficulty_type& difficulty, uint64_t& median_weight, uint64_t& already_generated_coins, std::vector<tx_block_template_backlog_entry>& tx_backlog)
17721804
{
17731805
prev_id = m_db->top_block_hash(&height);
17741806
++height;
17751807

17761808
major_version = m_hardfork->get_ideal_version(height);
17771809

1810+
const uint8_t cur_version = m_hardfork->get_current_version();
1811+
1812+
fcmp_pp_tree_root = crypto::ec_point{};
1813+
if (cur_version >= HF_VERSION_FCMP_PLUS_PLUS)
1814+
m_db->get_tree_root_at_blk_idx(height, fcmp_pp_tree_root);
1815+
17781816
seed_hash = crypto::null_hash;
1779-
if (m_hardfork->get_current_version() >= RX_BLOCK_VERSION)
1817+
if (cur_version >= RX_BLOCK_VERSION)
17801818
{
17811819
uint64_t seed_height, next_height;
17821820
crypto::rx_seedheights(height, &seed_height, &next_height);
@@ -4316,6 +4354,19 @@ bool Blockchain::handle_block_to_main_chain(const block& bl, const crypto::hash&
43164354

43174355
TIME_MEASURE_START(t3);
43184356

4357+
// Make sure the block uses the correct FCMP++ tree root
4358+
if (hf_version >= HF_VERSION_FCMP_PLUS_PLUS)
4359+
{
4360+
crypto::ec_point fcmp_pp_tree_root;
4361+
m_db->get_tree_root_at_blk_idx(blockchain_height, fcmp_pp_tree_root);
4362+
if (bl.fcmp_pp_tree_root != fcmp_pp_tree_root)
4363+
{
4364+
MERROR_VER("Block with id: " << id << " used incorrect FCMP++ tree root");
4365+
bvc.m_verifivation_failed = true;
4366+
goto leave;
4367+
}
4368+
}
4369+
43194370
// sanity check basic miner tx properties;
43204371
if(!prevalidate_miner_transaction(bl, blockchain_height, hf_version))
43214372
{
@@ -4630,14 +4681,36 @@ bool Blockchain::handle_block_to_main_chain(const block& bl, const crypto::hash&
46304681
return false;
46314682
}
46324683

4684+
// Assume we're adding block n, now we grow the FCMP++ tree and generate a
4685+
// new root that will be usable once block n+1 is in the chain. We keep the
4686+
// tree 1 block ahead of the chain so that miners can use the block's tree
4687+
// root to calculate the next block's PoW hash. Note that we're still growing
4688+
// the tree 1 block at a time in the sync process. We just grow the tree ahead
4689+
// of the "next" block after syncing "this" block. This approach works because
4690+
// outputs are not immediately spendable upon inclusion in the chain. There is
4691+
// room for improvement in parallelizing tree building, left for another day.
4692+
TIME_MEASURE_START(advance_tree);
4693+
4694+
static_assert(CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE > 0, "Expect a non-0 spendable age");
4695+
try { m_db->advance_tree_one_block(new_height); }
4696+
catch (const std::exception& e)
4697+
{
4698+
LOG_ERROR("Failed to advance tree at block with hash: " << id << ", what = " << e.what());
4699+
bvc.m_verifivation_failed = true;
4700+
return false;
4701+
}
4702+
4703+
TIME_MEASURE_FINISH(advance_tree);
4704+
46334705
MINFO("+++++ BLOCK SUCCESSFULLY ADDED" << std::endl << "id:\t" << id << std::endl << "PoW:\t" << proof_of_work << std::endl << "HEIGHT " << new_height-1 << ", difficulty:\t" << current_diffic << std::endl << "block reward: " << print_money(fee_summary + base_reward) << "(" << print_money(base_reward) << " + " << print_money(fee_summary) << "), coinbase_weight: " << coinbase_weight << ", cumulative weight: " << cumulative_block_weight << ", " << block_processing_time << "(" << target_calculating_time << "/" << longhash_calculating_time << ")ms");
46344706
if(m_show_time_stats)
46354707
{
46364708
MINFO("Height: " << new_height << " coinbase weight: " << coinbase_weight << " cumm: "
46374709
<< cumulative_block_weight << " p/t: " << block_processing_time << " ("
46384710
<< target_calculating_time << "/" << longhash_calculating_time << "/"
46394711
<< t1 << "/" << t2 << "/" << t3 << "/" << t_exists << "/" << t_pool
4640-
<< "/" << t_checktx << "/" << t_dblspnd << "/" << vmt << "/" << addblock << ")ms");
4712+
<< "/" << t_checktx << "/" << t_dblspnd << "/" << vmt << "/" << addblock
4713+
<< "/" << advance_tree << ")ms");
46414714
}
46424715

46434716
bvc.m_added_to_main_chain = true;
@@ -5909,9 +5982,13 @@ void Blockchain::send_miner_notifications(uint64_t height, const crypto::hash &s
59095982
std::vector<tx_block_template_backlog_entry> tx_backlog;
59105983
m_tx_pool.get_block_template_backlog(tx_backlog);
59115984

5985+
crypto::ec_point fcmp_pp_tree_root{};
5986+
if (m_hardfork->get_current_version() >= HF_VERSION_FCMP_PLUS_PLUS)
5987+
m_db->get_tree_root_at_blk_idx(height, fcmp_pp_tree_root);
5988+
59125989
for (const auto& notifier : m_miner_notifiers)
59135990
{
5914-
notifier(major_version, height, prev_id, seed_hash, diff, median_weight, already_generated_coins, tx_backlog);
5991+
notifier(major_version, height, prev_id, fcmp_pp_tree_root, seed_hash, diff, median_weight, already_generated_coins, tx_backlog);
59155992
}
59165993
}
59175994

0 commit comments

Comments
 (0)