Skip to content

Commit 6d4a2ed

Browse files
committed
Add extended ledger index to map send blocks to receive blocks
The index is automatically backfilled from existing ledger data on initialization and kept consistent via ledger put/delete block hooks.
1 parent 9caebd9 commit 6d4a2ed

11 files changed

Lines changed: 187 additions & 1 deletion

nano/secure/ext_ledger.cpp

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@
55
#include <nano/secure/common.hpp>
66
#include <nano/secure/ext_ledger.hpp>
77
#include <nano/secure/ledger.hpp>
8+
#include <nano/store/ext_ledger/receive_block_by_send_block.hpp>
89
#include <nano/store/ext_ledger_store.hpp>
10+
#include <nano/store/ledger/block.hpp>
911
#include <nano/store/ledger_store.hpp>
1012
#include <nano/store/meta.hpp>
1113

@@ -36,11 +38,81 @@ void nano::ext_ledger::initialize ()
3638

3739
if (store.get_mode () != nano::store::open_mode::read_only)
3840
{
41+
auto has_flags = [&store] (nano::store::ext_ledger_flags flags) -> bool {
42+
return store.ext.has_flags (store.tx_begin_read (), flags);
43+
};
44+
45+
if (!has_flags (nano::store::ext_ledger_flags::receive_block_by_send_block_initialized))
46+
{
47+
store.ext.receive_block_by_send_block.clear ();
48+
initialize_receive_block_by_send_block_index ();
49+
}
3950
}
4051

4152
initialized = true;
4253
}
4354

55+
void nano::ext_ledger::initialize_receive_block_by_send_block_index ()
56+
{
57+
nano::store::ledger_store & store = ledger.store;
58+
nano::store::write_transaction txn = store.tx_begin_write ();
59+
60+
release_assert (store.ext.is_initialized (), "Extended ledger store must be initialized");
61+
release_assert (store.ext.receive_block_by_send_block.empty (txn), "The index must be cleared before rebuilding to avoid inconsistent or duplicate entries.");
62+
release_assert (store.get_mode () != nano::store::open_mode::read_only, "The index cannot be built while the backend is opened in read-only mode.");
63+
64+
logger.info (nano::log::type::ext_ledger, "Building receive block by send block index from existing ledger data, this may take a while...");
65+
66+
size_t processed = 0;
67+
size_t indexed = 0;
68+
size_t const batch_size = 100000;
69+
70+
for (auto itr = store.block.begin (txn), end = store.block.end (txn); itr != end; ++itr)
71+
{
72+
auto const & sideband = itr->second;
73+
auto block = sideband.block;
74+
if (block->is_receive ())
75+
{
76+
nano::block_hash receive_block_hash = itr->first;
77+
nano::block_hash send_block_hash = block->source ();
78+
store.ext.receive_block_by_send_block.put (txn, send_block_hash, receive_block_hash);
79+
++indexed;
80+
}
81+
++processed;
82+
if (processed % batch_size == 0)
83+
{
84+
logger.info (nano::log::type::ext_ledger, "Build progress: processed {} blocks, indexed {} entries", processed, indexed);
85+
86+
txn.refresh ();
87+
}
88+
}
89+
90+
logger.info (nano::log::type::ext_ledger, "Build completed: processed {} blocks, indexed {} entries", processed, indexed);
91+
92+
// Mark index as fully built and consistent with the current ledger state
93+
store.ext.add_flags (txn, nano::store::ext_ledger_flags::receive_block_by_send_block_initialized);
94+
}
95+
96+
void nano::ext_ledger::on_put_block (nano::store::write_transaction const & txn, nano::block_hash const & hash, nano::block const & block)
97+
{
98+
nano::store::ledger_store & store = ledger.store;
99+
release_assert (store.ext.is_initialized (), "Extended ledger store must be initialized");
100+
if (block.is_receive ())
101+
{
102+
store.ext.receive_block_by_send_block.put (txn, block.source (), hash);
103+
}
104+
}
105+
106+
void nano::ext_ledger::on_del_block (nano::store::write_transaction const & txn, nano::block_hash const & hash, nano::block const & block)
107+
{
108+
nano::store::ledger_store & store = ledger.store;
109+
release_assert (store.ext.is_initialized (), "Extended ledger store must be initialized");
110+
if (block.is_receive ())
111+
{
112+
store.ext.receive_block_by_send_block.del (txn, block.source ());
113+
}
114+
}
115+
44116
void nano::ext_ledger::clear (nano::store::write_transaction const & txn)
45117
{
46118
nano::store::ledger_store & store = ledger.store;

nano/secure/ext_ledger.hpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,11 @@ class ext_ledger final
1717

1818
bool is_initialized ();
1919
void initialize ();
20+
void initialize_receive_block_by_send_block_index ();
21+
22+
void on_put_block (nano::store::write_transaction const &, nano::block_hash const &, nano::block const &);
23+
void on_del_block (nano::store::write_transaction const &, nano::block_hash const &, nano::block const &);
24+
2025
void clear (nano::store::write_transaction const &);
2126
void drop (nano::store::write_transaction const &);
2227

nano/secure/ledger.cpp

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
#include <nano/secure/ledger_set_any.hpp>
1717
#include <nano/secure/ledger_set_confirmed.hpp>
1818
#include <nano/secure/rep_weights.hpp>
19+
#include <nano/store/ext_ledger/receive_block_by_send_block.hpp>
1920
#include <nano/store/ext_ledger_store.hpp>
2021
#include <nano/store/ledger/account.hpp>
2122
#include <nano/store/ledger/block.hpp>
@@ -79,10 +80,22 @@ void nano::ledger::del_account (nano::store::write_transaction const & transacti
7980
void nano::ledger::put_block (nano::store::write_transaction const & transaction_a, nano::block_hash const & hash_a, nano::block const & block_a)
8081
{
8182
store.block.put (transaction_a, hash_a, block_a);
83+
84+
if (ext.is_initialized ())
85+
{
86+
ext.on_put_block (transaction_a, hash_a, block_a);
87+
}
8288
}
8389

8490
void nano::ledger::del_block (nano::store::write_transaction const & transaction_a, nano::block_hash const & hash_a)
8591
{
92+
if (ext.is_initialized ())
93+
{
94+
auto block = store.block.get (transaction_a, hash_a);
95+
release_assert (block, "Block to be deleted was not found in the ledger");
96+
ext.on_del_block (transaction_a, hash_a, *block);
97+
}
98+
8699
store.block.del (transaction_a, hash_a);
87100
}
88101

@@ -724,6 +737,17 @@ std::shared_ptr<nano::block> nano::ledger::find_receive_block_by_send_hash (secu
724737
std::shared_ptr<nano::block> result;
725738
debug_assert (send_block_hash != 0);
726739

740+
// Use the extended ledger index to directly look up the receive block by send block hash, if enabled
741+
if (store.ext.is_initialized ())
742+
{
743+
auto receive_block_hash = store.ext.receive_block_by_send_block.get (transaction, send_block_hash);
744+
if (receive_block_hash.has_value ())
745+
{
746+
return store.block.get (transaction, receive_block_hash.value ());
747+
}
748+
return nullptr;
749+
}
750+
727751
// get the cemented frontier
728752
nano::confirmation_height_info info;
729753
if (store.confirmation_height.get (transaction, destination, info))

nano/store/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ add_library(
66
ext_ledger_store.cpp
77
backend.hpp
88
backend.cpp
9+
ext_ledger/receive_block_by_send_block.hpp
10+
ext_ledger/receive_block_by_send_block.cpp
911
ledger/account.hpp
1012
ledger/account.cpp
1113
ledger/block.hpp
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
#include <nano/store/db_val_templ.hpp>
2+
#include <nano/store/ext_ledger/receive_block_by_send_block.hpp>
3+
4+
namespace nano::store::ext_ledger
5+
{
6+
receive_block_by_send_block_view::receive_block_by_send_block_view (nano::store::backend & backend_a) :
7+
backend{ backend_a }
8+
{
9+
}
10+
11+
void receive_block_by_send_block_view::put (nano::store::write_transaction const & txn, nano::block_hash const & send_block_hash, nano::block_hash const & receive_block_hash)
12+
{
13+
auto status = backend.put (txn, nano::store::table::ext_receive_block_by_send_block, send_block_hash, receive_block_hash);
14+
backend.release_assert_success (status);
15+
}
16+
17+
std::optional<nano::block_hash> receive_block_by_send_block_view::get (nano::store::transaction const & txn, nano::block_hash const & send_block_hash) const
18+
{
19+
nano::store::db_val result;
20+
auto status = backend.get (txn, nano::store::table::ext_receive_block_by_send_block, send_block_hash, result);
21+
std::optional<nano::block_hash> receive_block_hash;
22+
if (backend.success (status))
23+
{
24+
receive_block_hash = static_cast<nano::block_hash> (result);
25+
}
26+
return receive_block_hash;
27+
}
28+
29+
void receive_block_by_send_block_view::del (nano::store::write_transaction const & txn, nano::block_hash const & send_block_hash)
30+
{
31+
auto status = backend.del (txn, nano::store::table::ext_receive_block_by_send_block, send_block_hash);
32+
backend.release_assert_success (status);
33+
}
34+
35+
bool receive_block_by_send_block_view::empty (nano::store::transaction const & txn) const
36+
{
37+
return backend.empty (txn, nano::store::table::ext_receive_block_by_send_block);
38+
}
39+
40+
void receive_block_by_send_block_view::clear ()
41+
{
42+
auto status = backend.clear (nano::store::table::ext_receive_block_by_send_block);
43+
backend.release_assert_success (status);
44+
}
45+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
#pragma once
2+
3+
#include <nano/lib/numbers.hpp>
4+
#include <nano/store/backend.hpp>
5+
6+
#include <functional>
7+
8+
namespace nano::store::ext_ledger
9+
{
10+
class receive_block_by_send_block_view
11+
{
12+
public:
13+
explicit receive_block_by_send_block_view (nano::store::backend &);
14+
15+
void put (nano::store::write_transaction const &, nano::block_hash const &, nano::block_hash const &);
16+
std::optional<nano::block_hash> get (nano::store::transaction const &, nano::block_hash const &) const;
17+
void del (nano::store::write_transaction const &, nano::block_hash const &);
18+
bool empty (nano::store::transaction const &) const;
19+
void clear ();
20+
21+
private:
22+
nano::store::backend & backend;
23+
};
24+
}

nano/store/ext_ledger_store.cpp

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
#include <nano/lib/logging.hpp>
22
#include <nano/lib/stats.hpp>
33
#include <nano/store/backend.hpp>
4+
#include <nano/store/ext_ledger/receive_block_by_send_block.hpp>
45
#include <nano/store/ext_ledger_store.hpp>
56
#include <nano/store/ledger_store.hpp>
67

78
namespace nano::store
89
{
910
nano::store::column_schema const ext_ledger_store::schema_current{
11+
{ nano::store::table::ext_receive_block_by_send_block, "ext_receive_block_by_send_block" },
1012
{ nano::store::table::meta, "meta" }
1113
};
1214
}
@@ -18,7 +20,9 @@ ext_ledger_store::ext_ledger_store (nano::store::backend & backend_a, nano::stat
1820
stats{ stats_a },
1921
logger{ logger_a },
2022
meta_impl{ std::make_unique<nano::store::meta_view> (backend_a) },
21-
meta{ *meta_impl }
23+
receive_block_by_send_block_impl{ std::make_unique<nano::store::ext_ledger::receive_block_by_send_block_view> (backend_a) },
24+
meta{ *meta_impl },
25+
receive_block_by_send_block{ *receive_block_by_send_block_impl }
2226
{
2327
}
2428

nano/store/ext_ledger_store.hpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ namespace nano::store
1414
enum class ext_ledger_flags : uint64_t
1515
{
1616
none = 0,
17+
receive_block_by_send_block_initialized = 1 << 0,
1718
};
1819

1920
inline nano::store::ext_ledger_flags operator| (nano::store::ext_ledger_flags a, nano::store::ext_ledger_flags b)
@@ -55,9 +56,11 @@ class ext_ledger_store
5556

5657
private:
5758
std::unique_ptr<nano::store::meta_view> meta_impl;
59+
std::unique_ptr<nano::store::ext_ledger::receive_block_by_send_block_view> receive_block_by_send_block_impl;
5860

5961
public:
6062
nano::store::meta_view & meta;
63+
nano::store::ext_ledger::receive_block_by_send_block_view & receive_block_by_send_block;
6164

6265
public:
6366
static nano::store::version_value_t constexpr version_minimum{ 1 };

nano/store/fwd.hpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,11 @@ class transaction;
1414
class write_transaction;
1515
}
1616

17+
namespace nano::store::ext_ledger
18+
{
19+
class receive_block_by_send_block_view;
20+
}
21+
1722
namespace nano::store::ledger
1823
{
1924
class account_view;

nano/store/rocksdb/backend_rocksdb.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -353,6 +353,7 @@ bool backend_rocksdb::count_is_exact (nano::store::table table) const
353353
case nano::store::table::blocks:
354354
case nano::store::table::confirmation_height:
355355
case nano::store::table::default_unused:
356+
case nano::store::table::ext_receive_block_by_send_block:
356357
case nano::store::table::meta:
357358
case nano::store::table::online_weight:
358359
case nano::store::table::peers:

0 commit comments

Comments
 (0)