Skip to content

Conversation

@kamilsa
Copy link
Contributor

@kamilsa kamilsa commented Nov 21, 2025

Update up to the leanSpec 49f70c7:

  1. Introduce hash-sig
  2. Refactorings and documentations according to spec

remove num_validators, update genesis_time, and enhance genesis state generation
@kamilsa kamilsa force-pushed the feature/c-hash-sig-rebased branch from 2a1f827 to 0230596 Compare November 25, 2025 09:59
@kamilsa kamilsa marked this pull request as ready for review November 26, 2025 09:35
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR introduces XMSS (eXtended Merkle Signature Scheme) hash-based signatures to replace placeholder signatures in the consensus protocol. The implementation integrates the qdrvm-crates dependency for cryptographic operations and refactors the validator key management system.

Key Changes:

  • Adds XMSS signature support through new crypto::xmss module with provider interface, types, and utility functions
  • Removes num_validators from Config in favor of dynamic validator list stored in State
  • Implements ValidatorKeysManifest for loading and managing validator public keys from YAML manifests
  • Refactors signature verification throughout the fork choice and state transition systems

Reviewed changes

Copilot reviewed 57 out of 60 changed files in this pull request and generated 16 comments.

Show a summary per file
File Description
vcpkg.json Adds qdrvm-crates dependency for XMSS cryptography
vcpkg-overlay/qdrvm-crates/* Configures qdrvm-crates package with c_hash_sig binding
src/crypto/xmss/* Implements XMSS provider, types, and key loading utilities
src/app/validator_keys_manifest Adds validator key manifest loading and management
src/types/{config,state,signature,validator}.hpp Refactors types to use XMSS keys and dynamic validator lists
src/blockchain/state_transition_function.* Updates genesis state generation to populate validators from manifest
src/blockchain/fork_choice.* Implements XMSS signature verification for blocks and attestations
tests/unit/crypto/* Adds XMSS provider tests
tests/unit/blockchain/* Updates tests for new validator registry and key manifest interfaces
tests/mock/* Adds mocks for XMSS provider and validator keys manifest
example/1-network/genesis/* Updates example configuration with XMSS keys and manifest

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

You can also share your feedback on Copilot code review for a chance to win a $100 gift card. Take the survey.

Comment on lines 46 to +48
ValidatorIndex validatorCount() const {
return config.num_validators;
return validators.size();
Copy link

Copilot AI Nov 26, 2025

Choose a reason for hiding this comment

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

The validatorCount() method now returns validators.size() instead of config.num_validators. However, this could be problematic if the validators vector is not properly initialized or if it's empty. In scenarios where the state is constructed but validators haven't been populated yet, this would return 0, potentially causing issues in code that depends on validator counts. Consider adding validation to ensure validators is populated, or documenting the initialization requirements clearly.

Copilot uses AI. Check for mistakes.
Comment on lines 26 to 27
auto payload = sszHash(attestation);
return SignedAttestation{.message = attestation, .signature = {}};
Copy link

Copilot AI Nov 26, 2025

Choose a reason for hiding this comment

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

The sszHash(attestation) is computed but assigned to payload which is then not used in the stub implementation. This unused variable suggests incomplete implementation. Either the payload should be used for actual signing, or it should be removed if this is intentionally a stub.

Copilot uses AI. Check for mistakes.
"Failed to produce block for slot {}: {}",
current_slot,
res.error());
time_ += 1;
Copy link

Copilot AI Nov 26, 2025

Choose a reason for hiding this comment

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

Inconsistent time increment logic. When produceBlockWithSignatures fails (line 625), the code increments time and continues (line 630). However, the normal flow already increments time at line 705. This means that when block production fails, time is incremented twice in one iteration - once at line 630 and once at line 705. This will cause the node to skip an interval, which could lead to timing issues and missed attestation opportunities.

Suggested change
time_ += 1;

Copilot uses AI. Check for mistakes.
Comment on lines 74 to 80
for (size_t i = 0; i < registry->allValidatorsIndices().size(); ++i) {
auto opt_pubkey = validator_keys_manifest->getXmssPubkeyByIndex(i);
if (not opt_pubkey) {
continue;
}
result.validators.push_back(Validator{.pubkey = *opt_pubkey});
}
Copy link

Copilot AI Nov 26, 2025

Choose a reason for hiding this comment

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

The genesis state generation iterates through validator indices using registry->allValidatorsIndices().size() (line 74) but then looks up the public key by that loop index i (line 75). This assumes that validator indices are contiguous starting from 0, which may not be the case. If allValidatorsIndices() returns a set like {5, 10, 15}, the code will look up indices 0, 1, 2 instead of 5, 10, 15, resulting in missing or incorrect validators. The loop should iterate over the actual indices returned by allValidatorsIndices(), not just its size.

Copilot uses AI. Check for mistakes.

// Check reasonable sizes
EXPECT_GT(keypair.public_key.size(), 0);
EXPECT_GT(keypair.private_key.size(), 0);
Copy link

Copilot AI Nov 26, 2025

Choose a reason for hiding this comment

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

Similar to line 30, this test checks keypair.private_key.size() on line 34, but XmssPrivateKey is a std::shared_ptr which doesn't have a size() method. This test will not compile.

Copilot uses AI. Check for mistakes.
return result;
}


Copy link

Copilot AI Nov 26, 2025

Choose a reason for hiding this comment

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

[nitpick] Empty line added at line 710. While not harmful, this extra blank line is inconsistent with the surrounding code formatting and should be removed.

Suggested change

Copilot uses AI. Check for mistakes.
Comment on lines +212 to +222
for (auto i = num_empty_slots; i > 0; --i) {
state.historical_block_hashes.push_back(kZeroHash);
}

// Build new justified slots list
//
// Genesis block is always justified
state.justified_slots.push_back(is_genesis_parent);

// Mark empty slots as not justified
for (auto i = num_empty_slots; i > 0; --i) {
Copy link

Copilot AI Nov 26, 2025

Choose a reason for hiding this comment

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

The loop variable i is declared as auto which will deduce to uint64_t (type of num_empty_slots). However, the loop counts down with --i and checks i > 0. If num_empty_slots is 0, the loop won't execute, which is correct. However, using an unsigned type in a countdown loop is a code smell - if the logic ever changes and i reaches 0 and then decrements, it will wrap around to the maximum value. Consider using a signed type or restructuring the loop to count up instead.

Copilot uses AI. Check for mistakes.
return xmss_keypair_.value();
}


Copy link

Copilot AI Nov 26, 2025

Choose a reason for hiding this comment

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

[nitpick] Extra blank line at line 68. This creates inconsistent spacing in the method implementation.

Suggested change

Copilot uses AI. Check for mistakes.
SSZ_CONT(num_validators, genesis_time);
bool operator==(const Config &) const = default;
SSZ_CONT(genesis_time);

Copy link

Copilot AI Nov 26, 2025

Choose a reason for hiding this comment

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

Trailing whitespace on line 18. This should be removed for code cleanliness.

Copilot uses AI. Check for mistakes.
ValidatorRegistry::ValidatorIndices
ValidatorRegistryImpl::allValidatorsIndices() const {
ValidatorIndices all_indices;
all_indices.reserve(index_to_node_.size());
Copy link

Copilot AI Nov 26, 2025

Choose a reason for hiding this comment

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

The reserve call on line 92 is inefficient for a set container. ValidatorIndices appears to be a set (based on the insert call on line 94), and sets don't support reserve() since they're typically implemented as red-black trees. This will either fail to compile or the reserve call has no effect. If ValidatorIndices is actually an unordered_set, then reserve would work but the size hint would be wrong since we're reserving based on index_to_node_.size() but then inserting into the set. Sets automatically handle capacity, so this line should be removed.

Suggested change
all_indices.reserve(index_to_node_.size());
// reserve() is not needed for set containers

Copilot uses AI. Check for mistakes.
/**
* Stub method to sign vote.
*/
inline SignedAttestation signAttestation(Attestation attestation) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Not used, can be removed

@kamilsa kamilsa merged commit df19a98 into master Dec 2, 2025
2 checks passed
@kamilsa kamilsa deleted the feature/c-hash-sig-rebased branch December 2, 2025 10:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants