Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
Copy link

Copilot AI Jan 13, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The genesis time value 1767089000 appears to be set to a specific future date. When converted from Unix timestamp, this is approximately December 29, 2025. This seems like it might be a test value that should be updated or parameterized rather than hardcoded in the example genesis configuration.

Suggested change
GENESIS_TIME: 1767089000
GENESIS_TIME: 0 # Replace with desired genesis Unix timestamp (seconds since epoch)

Copilot uses AI. Check for mistakes.
# 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.

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
22 changes: 14 additions & 8 deletions src/app/impl/timeline_impl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ namespace lean::app {
qtils::SharedRef<blockchain::BlockTree> block_tree)
: logger_(logsys->getLogger("Timeline", "application")),
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 +67,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 @@ -144,12 +145,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 +193,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
2 changes: 1 addition & 1 deletion src/app/impl/timeline_impl.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ namespace lean::app {

qtils::SharedRef<soralog::Logger> logger_;
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
29 changes: 11 additions & 18 deletions src/blockchain/block_storage.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,12 @@
#include "types/block_body.hpp"
#include "types/block_data.hpp"
#include "types/block_header.hpp"
#include "types/justification.hpp"
#include "types/signed_block_with_attestation.hpp"
#include "types/types.hpp"

namespace lean {
struct State;
}
namespace lean::blockchain {

/**
Expand Down Expand Up @@ -147,7 +149,7 @@ namespace lean::blockchain {
// -- body --

/**
* Saves provided body of block to block storage
* Saves provided body of block to block storage
* @returns result of saving
*/
virtual outcome::result<void> putBlockBody(const BlockHash &block_hash,
Expand All @@ -167,29 +169,20 @@ namespace lean::blockchain {
virtual outcome::result<void> removeBlockBody(
const BlockHash &block_hash) = 0;

// -- justification --
// -- state --
// TODO: refactoring is needed - make special separated storage for states
Copy link

Copilot AI Jan 13, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment at line 173 says "TODO: refactoring is needed - make special separated storage for states". This indicates that the current implementation of storing states in the same BlockStorage as blocks is considered temporary. This technical debt should be tracked and addressed, especially since state storage has different access patterns and retention requirements than block storage.

Suggested change
// TODO: refactoring is needed - make special separated storage for states
// Note: state is currently accessed via BlockStorage; if a dedicated state
// storage is introduced in the future, this interface should be updated
// to reflect that separation.

Copilot uses AI. Check for mistakes.

/**
* Saves {@param justification} of block with hash {@param block_hash} to
* block storage
* Saves provided state to block storage
* @returns result of saving
*/
virtual outcome::result<void> putJustification(
const Justification &justification, const BlockHash &block_hash) = 0;
virtual outcome::result<void> putState(const BlockHash &block_hash,
const State &state) = 0;

/**
* Tries to get justification of block finality by {@param block_hash}
* @returns justification or error
*/
virtual outcome::result<std::optional<Justification>> getJustification(
[[nodiscard]] virtual outcome::result<std::optional<State>> getState(
const BlockHash &block_hash) const = 0;

/**
* Removes justification of block with hash {@param block_hash} from block
* storage
* @returns result of saving
*/
virtual outcome::result<void> removeJustification(
virtual outcome::result<void> removeState(
const BlockHash &block_hash) = 0;

// -- combined
Expand Down
14 changes: 2 additions & 12 deletions src/blockchain/block_tree.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
#pragma once

#include "blockchain/block_header_repository.hpp"
#include "types/justification.hpp"

namespace lean {
struct Block;
Expand Down Expand Up @@ -88,13 +87,11 @@ namespace lean::blockchain {
virtual outcome::result<void> removeLeaf(const BlockHash &block_hash) = 0;

/**
* Mark the block as finalized and store a finalization justification
* Mark the block as finalized (and clean up sidechain internally)
* @param block to be finalized
* @param justification of the finalization
* @return nothing or error
*/
virtual outcome::result<void> finalize(
const BlockHash &block, const Justification &justification) = 0;
virtual outcome::result<void> finalize(const BlockHash &block) = 0;

/**
* Get a chain of blocks from provided block to direction of the best block
Expand Down Expand Up @@ -167,13 +164,6 @@ namespace lean::blockchain {
*/
virtual outcome::result<std::optional<SignedBlockWithAttestation>>
tryGetSignedBlock(const BlockHash block_hash) const = 0;

// TODO(turuslan): state transition function
/**
* Import pre-sorted batch of `SignedBlockWithAttestation`.
* May change best and finalized block.
*/
virtual void import(std::vector<SignedBlockWithAttestation> blocks) = 0;
};

} // namespace lean::blockchain
Loading
Loading