Skip to content
Merged
Show file tree
Hide file tree
Changes from 32 commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
9a44c3b
feature: anchor block and state
xDimon Dec 17, 2025
48705c1
refactor: changes generating some files
xDimon Dec 23, 2025
2597140
draft
xDimon Dec 23, 2025
ab14d7a
draft
xDimon Dec 24, 2025
64113fc
refactor: log config over cli
xDimon Dec 26, 2025
06cfe1a
fix: review issues
xDimon Dec 29, 2025
adfb692
draft
xDimon Dec 30, 2025
8b665f2
draft
xDimon Dec 30, 2025
c935b3e
git: Merge branch 'master' into refactor/block_tree_and_forck_choice
xDimon Jan 13, 2026
186a4a6
fix: test
xDimon Jan 13, 2026
193479d
more logs
xDimon Jan 13, 2026
16597ea
fix: receiving and import blocks with correct
xDimon Jan 14, 2026
d4fa0af
fix: fork-choice initialization after restart
xDimon Jan 15, 2026
bf61bf8
docs: update README with instructions to clear data repositories and …
kamilsa Jan 15, 2026
3711251
hotfix
xDimon Jan 15, 2026
ec96d97
hotfix
xDimon Jan 15, 2026
0df1d8a
refactor: approach of store and operate with attestations and signatures
xDimon Jan 16, 2026
6960f45
git: Merge branch 'master' into refactor/block_tree_and_forck_choice
xDimon Jan 16, 2026
241fede
fix: tests
xDimon Jan 16, 2026
88075cb
+ todo
xDimon Jan 16, 2026
a545459
refactor: bit_cast for hash_tree_root
xDimon Jan 16, 2026
718dff6
+ todo
xDimon Jan 16, 2026
098bf93
fix: lru cache and cover it by tests
xDimon Jan 16, 2026
685a40d
fix: interval enumeration if log
xDimon Jan 16, 2026
2146991
digest-banner
xDimon Jan 19, 2026
442ac89
feature: save/restore finalized and justified block
xDimon Jan 20, 2026
b37a3ec
fix: digest-banner
xDimon Jan 20, 2026
f161609
hotfix
xDimon Jan 20, 2026
63a0d59
fix: use last justified over block tree
xDimon Jan 20, 2026
c981ad1
improve validation checks for attestation targets in fork choice logic
kamilsa Jan 20, 2026
c004b2c
improve connection handling in networking module
kamilsa Jan 20, 2026
555d9cf
fix: tests
xDimon Jan 21, 2026
8e2cf16
fix: TODOs, tests, etc. and update
xDimon Jan 21, 2026
efabce9
refactor: import of block with its children and use queue
xDimon Jan 21, 2026
ff73f49
update: qtils
xDimon Jan 21, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion example/0-single/genesis/config.yaml
Original file line number Diff line number Diff line change
@@ -1,2 +1,8 @@
# Genesis Settings
GENESIS_TIME: 1763468331
GENESIS_TIME: 1767089000
# Key Settings
ACTIVE_EPOCH: 18
# Validator Settings
VALIDATOR_COUNT: 1
GENESIS_VALIDATORS:
- "b3183808a14d1875748fd96989441855092ef73c6bf5a7680f03b32d1c12f96ec4659e64fd526f3d975eb168907ee7589946f972"
27 changes: 27 additions & 0 deletions example/0-single/genesis/validator-keys-manifest.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Hash-Signature Validator Keys Manifest
# Generated by hash-sig-cli

key_scheme: SIGTopLevelTargetSumLifetime32Dim64Base8
hash_function: Poseidon2
encoding: TargetSum
lifetime: 4294967296
log_num_active_epochs: 18
num_active_epochs: 262144
num_validators: 4

validators:
- index: 0
pubkey_hex: 0xb3183808a14d1875748fd96989441855092ef73c6bf5a7680f03b32d1c12f96ec4659e64fd526f3d975eb168907ee7589946f972
privkey_file: validator_0_sk.json

- index: 1
pubkey_hex: 0xd89fef7b41821a67c118e26808f48e28d00cbe082a1f88369b8828147f3905656b3b04785a5b26403a0c0a574b4f333bd4d48a03
privkey_file: validator_1_sk.json

- index: 2
pubkey_hex: 0xd404bb1091a750710952a64b22240d0013bcc66a64b5b1780b89b120c0dd7d212fa3cb5e42f56e74fcc68950e6b493647524a627
privkey_file: validator_2_sk.json

- index: 3
pubkey_hex: 0xb196071b88a6f16d658e512f753be6222e54e91a19e0d676201ac5161cd90e1e40210d36e024f81ae30ee67714092009c20b415c
privkey_file: validator_3_sk.json
1 change: 1 addition & 0 deletions example/0-single/genesis/validator_0_pk.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"root":[2087627763,754406247,1168254309,360212415,1578884007,772417919,1172421982,1682864316],"parameter":[472747929,573156706,1979134263,986355730,1782443694]}
1 change: 1 addition & 0 deletions example/0-single/genesis/validator_0_sk.json

Large diffs are not rendered by default.

7 changes: 6 additions & 1 deletion example/1-network/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,10 @@ Prerequisites:

## Prepare genesis time

Before starting nodes, set `GENESIS_TIME` in `example/1-network/genesis/config.yaml` to a future Unix timestamp so the chain can start. For example, current time + 20 seconds:
Before starting nodes, set `GENESIS_TIME` in `example/1-network/genesis/config.yaml` to a future Unix timestamp so the chain can start. For example, current time + 20 seconds. Also, clear any existing data repositories to avoid conflicts from previous runs.

```bash
rm -rf data/
future_time=$(( $(date +%s) + 20 ))
sed -i '' "s/GENESIS_TIME: .*/GENESIS_TIME: $future_time/" example/1-network/genesis/config.yaml
```
Expand All @@ -29,6 +30,7 @@ Node 0:
```bash
./build/src/executable/qlean \
--modules-dir ./build/src/modules \
--base-path data/node_0 \
--bootnodes example/1-network/genesis/nodes.yaml \
--genesis example/1-network/genesis/config.yaml \
--validator-registry-path example/1-network/genesis/validators.yaml \
Expand All @@ -46,6 +48,7 @@ Node 1:
```bash
./build/src/executable/qlean \
--modules-dir ./build/src/modules \
--base-path data/node_1 \
--bootnodes example/1-network/genesis/nodes.yaml \
--genesis example/1-network/genesis/config.yaml \
--validator-registry-path example/1-network/genesis/validators.yaml \
Expand All @@ -63,6 +66,7 @@ Node 2:
```bash
./build/src/executable/qlean \
--modules-dir ./build/src/modules \
--base-path data/node_2 \
--bootnodes example/1-network/genesis/nodes.yaml \
--genesis example/1-network/genesis/config.yaml \
--validator-registry-path example/1-network/genesis/validators.yaml \
Expand All @@ -80,6 +84,7 @@ Node 3:
```bash
./build/src/executable/qlean \
--modules-dir ./build/src/modules \
--base-path data/node_3 \
--bootnodes example/1-network/genesis/nodes.yaml \
--genesis example/1-network/genesis/config.yaml \
--validator-registry-path example/1-network/genesis/validators.yaml \
Expand Down
12 changes: 10 additions & 2 deletions example/config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,20 @@ logging:
thread: name
color: true
latency: 0
- name: digest
type: console
stream: stderr
thread: name
color: true
latency: 0
groups:
- name: main
sink: console
level: info
is_fallback: true
children:
- name: digest
level: verbose
- name: lean
children:
- name: modules
Expand All @@ -46,7 +54,7 @@ logging:
children:
- name: block_tree
- name: fork_choice
level: trace
# level: trace
children:
- name: stf
level: info
# level: info
49 changes: 45 additions & 4 deletions src/app/configurator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -546,14 +546,55 @@ namespace lean::app {
config_->base_path_ = std::move(resolved);
}

// Validate base_path_ exists and is a directory
if (not is_directory(config_->base_path_)) {
std::error_code ec;

// If the base path doesn't exist -> try to create it (including parents)
if (not exists(config_->base_path_, ec)) {
if (ec) {
SL_ERROR(logger_,
"Failed to check existence of 'base_path': {} ({}: {})",
config_->base_path_,
ec.value(),
ec.message());
return Error::InvalidValue;
}

if (not create_directories(config_->base_path_, ec)) {
if (ec) {
SL_ERROR(logger_,
"Failed to create 'base_path' directory: {} ({}: {})",
config_->base_path_,
ec.value(),
ec.message());
return Error::InvalidValue;
}
}
} else if (ec) {
SL_ERROR(logger_,
"The 'base_path' does not exist or is not a directory: {}",
config_->base_path_);
"Failed to check existence of 'base_path': {} ({}: {})",
config_->base_path_,
ec.value(),
ec.message());
return Error::InvalidValue;
}

// Now it should exist; ensure it's a directory
if (not is_directory(config_->base_path_, ec)) {
if (ec) {
SL_ERROR(logger_,
"Failed to check if 'base_path' is a directory: {} ({}: {})",
config_->base_path_,
ec.value(),
ec.message());
} else {
SL_ERROR(logger_,
"The 'base_path' exists but is not a directory: {}",
config_->base_path_);
}
return Error::InvalidValue;
}


// Helper to resolve general paths: if provided via CLI -> relative to CWD,
// else if provided via config file -> relative to config file dir,
// else fallback to CWD. Always normalize.
Expand Down
74 changes: 36 additions & 38 deletions src/app/impl/timeline_impl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,9 @@ namespace lean::app {
qtils::SharedRef<GenesisConfig> config,
qtils::SharedRef<blockchain::BlockTree> block_tree)
: logger_(logsys->getLogger("Timeline", "application")),
digest_(logsys->getLogger("Digest", "digest")),
state_manager_(std::move(state_manager)),
config_(std::move(config)),
genesis_config_(std::move(config)),
clock_(std::move(clock)),
se_manager_(std::move(se_manager)),
block_tree_(std::move(block_tree)) {
Expand Down Expand Up @@ -67,10 +68,11 @@ namespace lean::app {
void TimelineImpl::start() {
auto now = clock_->nowMsec();
auto next_slot =
now > config_->config.genesis_time * 1000
? (now - config_->config.genesis_time * 1000) / SLOT_DURATION_MS + 1
now > genesis_config_->genesis_time * 1000
? (now - genesis_config_->genesis_time * 1000) / SLOT_DURATION_MS
+ 1
: 1;
auto time_to_next_slot = config_->config.genesis_time * 1000
auto time_to_next_slot = genesis_config_->genesis_time * 1000
+ SLOT_DURATION_MS * next_slot - now;
if (time_to_next_slot < SLOT_DURATION_MS / 2) {
++next_slot;
Expand Down Expand Up @@ -105,36 +107,27 @@ namespace lean::app {
state_root = head_block_header_res.value().state_root;
}

fmt::println(
std::cerr,
"+===============================================================+");
fmt::println(std::cerr,
" CHAIN STATUS: Current Slot: {} | Head Slot: {}",
msg->slot,
head.slot);
fmt::println(std::cerr,
"+---------------------------------------------------------------+");
fmt::println(std::cerr, " Connected Peers: {}",
connected_peers_.load());
fmt::println(std::cerr,
"+---------------------------------------------------------------+");
fmt::println(std::cerr, " Head Block Root: 0x{}", head.hash.toHex());
fmt::println(std::cerr, " Parent Block Root: 0x{}", parent_root.toHex());
fmt::println(std::cerr, " State Root: 0x{}", state_root.toHex());
fmt::println(
std::cerr,
"+---------------------------------------------------------------+");
fmt::println(std::cerr,
" Latest Justified: Slot {:>6} | Root: 0x{}",
justified.slot,
justified.root.toHex());
fmt::println(std::cerr,
" Latest Finalized: Slot {:>6} | Root: 0x{}",
finalized.slot,
finalized.hash.toHex());
fmt::println(
std::cerr,
"+===============================================================+");
constexpr int kFillWidth = 98;
auto hnc = "\x1b[1G"; // Move home and clear line
// clang-format off
SL_VERBOSE(digest_, "\x1b[2J\x1b[H" // Clear screen and move cursor to home
"{}+{:-<{}}+", hnc, "", kFillWidth);
SL_VERBOSE(digest_, "{}|{: ^{}}|", hnc, "CHAIN STATUS", kFillWidth);
SL_VERBOSE(digest_, "{}+{:-<{}}+{:-<{}}+", hnc, "", kFillWidth/2-1, "", kFillWidth/2);
SL_VERBOSE(digest_, "{}|{: ^{}}|{: ^{}}|", hnc,
fmt::format("Current Slot: {}", msg->slot), kFillWidth/2-1,
fmt::format("Head Slot: {}", head.slot), kFillWidth/2);
SL_VERBOSE(digest_, "{}+{:-<{}}+{:-<{}}+", hnc, "", kFillWidth/2-1, "", kFillWidth/2);
SL_VERBOSE(digest_, "{}| Connected Peers: {: <{}} |", hnc, connected_peers_.load(), kFillWidth - 22);
SL_VERBOSE(digest_, "{}+{:-<{}}+", hnc, "", kFillWidth);
SL_VERBOSE(digest_, "{}| Head Block Root: {: <{};0xx} |", hnc, head.hash, kFillWidth - 22);
SL_VERBOSE(digest_, "{}| Parent Block Root: {: <{};0xx} |", hnc, parent_root, kFillWidth - 22);
SL_VERBOSE(digest_, "{}| State Root: {: <{};0xx} |", hnc, state_root, kFillWidth - 22);
SL_VERBOSE(digest_, "{}+{:-<{}}+", hnc, "", kFillWidth);
SL_VERBOSE(digest_, "{}| Latest Justified: {: <{};l} |", hnc, justified, kFillWidth - 22);
SL_VERBOSE(digest_, "{}| Latest Finalized: {: <{};l} |", hnc, finalized, kFillWidth - 22);
SL_VERBOSE(digest_, "{}+{:-<{}}+", hnc, "", kFillWidth);
// clang-format on

SL_INFO(logger_, "⚡ Slot {} started", msg->slot);
if (stopped_) [[unlikely]] {
Expand All @@ -144,12 +137,17 @@ namespace lean::app {

auto now = clock_->nowMsec();
auto next_slot =
(now - config_->config.genesis_time * 1000) / SLOT_DURATION_MS + 1;
auto time_to_next_slot = config_->config.genesis_time * 1000
(now - genesis_config_->genesis_time * 1000) / SLOT_DURATION_MS + 1;
auto time_to_next_slot = genesis_config_->genesis_time * 1000
+ SLOT_DURATION_MS * next_slot - now;

SL_TRACE(logger_,
"Next slot {} is scheduled in {}ms",
next_slot,
time_to_next_slot);

const auto slot_start_abs =
config_->config.genesis_time * 1000
genesis_config_->genesis_time * 1000
+ SLOT_DURATION_MS * msg->slot; // in milliseconds

auto abs_interval1 = slot_start_abs + SECONDS_PER_INTERVAL * 1000;
Expand Down Expand Up @@ -187,7 +185,7 @@ namespace lean::app {
std::make_shared<const messages::SlotIntervalStarted>(
3, msg->slot, msg->epoch));

const auto next_slot_abs = config_->config.genesis_time * 1000
const auto next_slot_abs = genesis_config_->genesis_time * 1000
+ SLOT_DURATION_MS * (msg->slot + 1);
auto time_to_next_slot_abs = ms_to_abs(next_slot_abs);
se_manager_->notifyDelayed(
Expand Down
3 changes: 2 additions & 1 deletion src/app/impl/timeline_impl.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,9 @@ namespace lean::app {
void on_slot_started(std::shared_ptr<const messages::SlotStarted> msg);

qtils::SharedRef<soralog::Logger> logger_;
qtils::SharedRef<soralog::Logger> digest_;
qtils::SharedRef<StateManager> state_manager_;
qtils::SharedRef<GenesisConfig> config_;
qtils::SharedRef<GenesisConfig> genesis_config_;
qtils::SharedRef<clock::SystemClock> clock_;
qtils::SharedRef<Subscription> se_manager_;
qtils::SharedRef<blockchain::BlockTree> block_tree_;
Expand Down
3 changes: 2 additions & 1 deletion src/blockchain/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,15 @@ target_link_libraries(validator_registry
add_library(blockchain
fork_choice.cpp
genesis_config.cpp
impl/anchor_block_impl.cpp
impl/anchor_state_impl.cpp
impl/block_storage_error.cpp
impl/block_storage_impl.cpp
impl/block_storage_initializer.cpp
impl/block_tree_error.cpp
impl/block_tree_impl.cpp
impl/block_tree_initializer.cpp
impl/cached_tree.cpp
impl/fc_block_tree.cpp
impl/storage_util.cpp
state_transition_function.cpp
)
Expand Down
4 changes: 2 additions & 2 deletions src/blockchain/block_header_repository.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,10 @@ namespace lean::blockchain {
virtual ~BlockHeaderRepository() = default;

/**
* @return the number of the block with the provided {@param block_hash}
* @return the slot of the block with the provided {@param block_hash}
* in case one is in the storage or an error
*/
[[nodiscard]] virtual outcome::result<Slot> getNumberByHash(
[[nodiscard]] virtual outcome::result<Slot> getSlotByHash(
const BlockHash &block_hash) const = 0;

/**
Expand Down
Loading
Loading