Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Validator address as key for the Dht #3014

Merged
merged 3 commits into from
Nov 12, 2024
Merged
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
21 changes: 19 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ members = [
"database",
"database/database-value",
"database/database-value-derive",
"dht",
"fuzz",
"genesis",
"genesis-builder",
Expand Down Expand Up @@ -189,6 +190,7 @@ nimiq-consensus = { path = "consensus", default-features = false }
nimiq-database = { path = "database", default-features = false }
nimiq-database-value = { path = "database/database-value", default-features = false }
nimiq-database-value-derive = { path = "database/database-value-derive", default-features = false }
nimiq-dht = { path = "dht", default-features = false }
nimiq-genesis = { path = "genesis", default-features = false }
nimiq-genesis-builder = { path = "genesis-builder", default-features = false }
nimiq-handel = { path = "handel", default-features = false }
Expand Down
33 changes: 33 additions & 0 deletions dht/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
[package]
name = "nimiq-dht"
version.workspace = true
authors.workspace = true
edition.workspace = true
description = "Nimiq Dht verifier implementation."
homepage.workspace = true
repository.workspace = true
license.workspace = true
categories.workspace = true
keywords.workspace = true

[badges]
travis-ci = { repository = "nimiq/core-rs", branch = "master" }
is-it-maintained-issue-resolution = { repository = "nimiq/core-rs" }
is-it-maintained-open-issues = { repository = "nimiq/core-rs" }
maintenance = { status = "experimental" }

[lints]
workspace = true

[dependencies]
log = { workspace = true }

nimiq-blockchain-interface = { workspace = true }
nimiq-blockchain-proxy = { workspace = true }
nimiq-keys = { workspace = true }
nimiq-log = { workspace = true, optional = true }
nimiq-network-libp2p = { workspace = true }
nimiq-serde = { workspace = true }
nimiq-utils = { workspace = true }
nimiq-validator-network = { workspace = true }

85 changes: 85 additions & 0 deletions dht/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
use nimiq_blockchain_proxy::BlockchainProxy;
use nimiq_keys::{Address, KeyPair};
use nimiq_network_libp2p::{
dht::{DhtRecord, DhtVerifierError, Verifier as DhtVerifier},
libp2p::kad::Record,
PeerId,
};
use nimiq_serde::Deserialize;
use nimiq_utils::tagged_signing::{TaggedSignable, TaggedSigned};
use nimiq_validator_network::validator_record::ValidatorRecord;

pub struct Verifier {
blockchain: BlockchainProxy,
}

impl Verifier {
pub fn new(blockchain: BlockchainProxy) -> Self {
Self { blockchain }
}

fn verify_validator_record(&self, record: &Record) -> Result<DhtRecord, DhtVerifierError> {
// Deserialize the value of the record, which is a ValidatorRecord. If it fails return an error.
let validator_record =
TaggedSigned::<ValidatorRecord<PeerId>, KeyPair>::deserialize_from_vec(&record.value)
.map_err(DhtVerifierError::MalformedValue)?;

// Deserialize the key of the record which is an Address. If it fails return an error.
let validator_address = Address::deserialize_from_vec(record.key.as_ref())
.map_err(DhtVerifierError::MalformedKey)?;

// Acquire blockchain read access. For now exclude Light clients.
let blockchain = match self.blockchain {
BlockchainProxy::Light(ref _light_blockchain) => {
return Err(DhtVerifierError::UnknownTag)
}
BlockchainProxy::Full(ref full_blockchain) => full_blockchain,
};
let blockchain_read = blockchain.read();

// Get the staking contract to retrieve the public key for verification.
let staking_contract = blockchain_read
.get_staking_contract_if_complete(None)
.ok_or(DhtVerifierError::StateIncomplete)?;

// Get the public key needed for verification.
let data_store = blockchain_read.get_staking_contract_store();
let txn = blockchain_read.read_transaction();
let public_key = staking_contract
.get_validator(&data_store.read(&txn), &validator_address)
.ok_or(DhtVerifierError::UnknownValidator(validator_address))?
.signing_key;

// Verify the record.
validator_record
.verify(&public_key)
.then(|| {
DhtRecord::Validator(
record.publisher.unwrap(),
validator_record.record,
record.clone(),
)
})
.ok_or(DhtVerifierError::InvalidSignature)
}
}

impl DhtVerifier for Verifier {
fn verify(&self, record: &Record) -> Result<DhtRecord, DhtVerifierError> {
// Peek the tag to know what kind of record this is.
let Some(tag) = TaggedSigned::<ValidatorRecord<PeerId>, KeyPair>::peek_tag(&record.value)
else {
log::warn!(?record, "DHT Tag not peekable.");
return Err(DhtVerifierError::MalformedTag);
};

// Depending on tag perform the verification.
match tag {
ValidatorRecord::<PeerId>::TAG => self.verify_validator_record(record),
_ => {
log::error!(tag, "DHT invalid record tag received");
Err(DhtVerifierError::UnknownTag)
}
}
}
}
3 changes: 3 additions & 0 deletions lib/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ nimiq-blockchain-proxy = { workspace = true, default-features = false }
nimiq-bls = { workspace = true }
nimiq-consensus = { workspace = true, default-features = false }
nimiq-database = { workspace = true, optional = true }
nimiq-dht = { workspace = true, optional = true }
nimiq-genesis = { workspace = true, default-features = false }
nimiq-hash = { workspace = true }
nimiq-jsonrpc-core = { workspace = true, optional = true }
Expand Down Expand Up @@ -94,6 +95,8 @@ full-consensus = [
"database-storage",
"nimiq-blockchain",
"nimiq-consensus/full",
"nimiq-dht",
"nimiq-network-libp2p/kad",
]
launcher = []
logging = ["nimiq-log", "serde_json", "tokio", "tracing-subscriber"]
Expand Down
73 changes: 42 additions & 31 deletions lib/src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ use nimiq_consensus::{
sync::syncer_proxy::SyncerProxy, Consensus as AbstractConsensus,
ConsensusProxy as AbstractConsensusProxy,
};
#[cfg(feature = "full-consensus")]
use nimiq_dht::Verifier;
#[cfg(feature = "zkp-prover")]
use nimiq_genesis::NetworkId;
use nimiq_genesis::NetworkInfo;
Expand Down Expand Up @@ -345,11 +347,6 @@ impl ClientInner {
"Advertised addresses",
);

let network = Arc::new(Network::new(network_config).await);

// Start buffering network events as early as possible
let network_events = network.subscribe_events();

// We update the services flags depending on the pre-genesis database file being present
#[cfg(feature = "database-storage")]
let pre_genesis_environment = if config.storage.has_pre_genesis_database(config.network_id)
Expand Down Expand Up @@ -388,7 +385,7 @@ impl ClientInner {
#[cfg(not(feature = "database-storage"))]
let zkp_storage = None;

let (blockchain_proxy, syncer_proxy, zkp_component) = match config.consensus.sync_mode {
let blockchain_proxy = match config.consensus.sync_mode {
#[cfg(not(feature = "full-consensus"))]
SyncMode::History => {
panic!("Can't build a history node without the full-consensus feature enabled")
Expand All @@ -398,8 +395,8 @@ impl ClientInner {
panic!("Can't build a full node without the full-consensus feature enabled")
}
#[cfg(feature = "full-consensus")]
SyncMode::History => {
blockchain_config.keep_history = true;
SyncMode::History | SyncMode::Full => {
blockchain_config.keep_history = config.consensus.sync_mode == SyncMode::History;
blockchain_config.index_history = config.consensus.index_history;
let blockchain = match Blockchain::new_merged(
environment.clone(),
Expand All @@ -413,8 +410,41 @@ impl ClientInner {
return Err(Error::Consensus(BlockchainError(err)));
}
};
BlockchainProxy::from(&blockchain)
}
SyncMode::Light => BlockchainProxy::from(&Arc::new(RwLock::new(LightBlockchain::new(
config.network_id,
)))),
};

// Create the Dht verifier
#[cfg(feature = "full-consensus")]
let dht_verifier = Verifier::new(blockchain_proxy.clone());

// Create the network.
let network = Arc::new(
Network::new(
network_config,
#[cfg(feature = "full-consensus")]
dht_verifier,
Copy link
Contributor

Choose a reason for hiding this comment

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

Even if we have the full-consensus feature enabled at compile time, we might still start a light client.

)
.await,
);

// Start buffering network events as early as possible
let network_events = network.subscribe_events();

let blockchain_proxy = BlockchainProxy::from(&blockchain);
let (syncer_proxy, zkp_component) = match config.consensus.sync_mode {
#[cfg(not(feature = "full-consensus"))]
SyncMode::History => {
panic!("Can't build a history node without the full-consensus feature enabled")
}
#[cfg(not(feature = "full-consensus"))]
SyncMode::Full => {
panic!("Can't build a full node without the full-consensus feature enabled")
}
#[cfg(feature = "full-consensus")]
SyncMode::History => {
#[cfg(feature = "zkp-prover")]
let zkp_component = if let Some(zk_prover_config) = config.zk_prover {
ZKPComponent::with_prover(
Expand All @@ -441,27 +471,10 @@ impl ClientInner {
network_events,
)
.await;
(blockchain_proxy, syncer, zkp_component)
(syncer, zkp_component)
}
#[cfg(feature = "full-consensus")]
SyncMode::Full => {
blockchain_config.keep_history = false;
blockchain_config.index_history = config.consensus.index_history;

let blockchain = match Blockchain::new_merged(
environment.clone(),
pre_genesis_environment,
blockchain_config,
config.network_id,
time,
) {
Ok(blockchain) => Arc::new(RwLock::new(blockchain)),
Err(err) => {
return Err(Error::Consensus(BlockchainError(err)));
}
};

let blockchain_proxy = BlockchainProxy::from(&blockchain);
#[cfg(feature = "zkp-prover")]
let zkp_component = if let Some(zk_prover_config) = config.zk_prover {
ZKPComponent::with_prover(
Expand Down Expand Up @@ -491,11 +504,9 @@ impl ClientInner {
config.consensus.full_sync_threshold,
)
.await;
(blockchain_proxy, syncer, zkp_component)
(syncer, zkp_component)
}
SyncMode::Light => {
let blockchain = Arc::new(RwLock::new(LightBlockchain::new(config.network_id)));
let blockchain_proxy = BlockchainProxy::from(&blockchain);
let zkp_component =
ZKPComponent::new(blockchain_proxy.clone(), Arc::clone(&network), zkp_storage)
.await;
Expand All @@ -507,7 +518,7 @@ impl ClientInner {
network_events,
)
.await;
(blockchain_proxy, syncer, zkp_component)
(syncer, zkp_component)
}
};

Expand Down
1 change: 1 addition & 0 deletions log/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ pub static NIMIQ_MODULES: &[&str] = &[
"nimiq_collections",
"nimiq_consensus",
"nimiq_database",
"nimiq_dht",
"nimiq_genesis",
"nimiq_genesis_builder",
"nimiq_handel",
Expand Down
3 changes: 2 additions & 1 deletion network-libp2p/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ tokio-stream = "0.1"
unsigned-varint = "0.8"
void = "1.0"

nimiq-bls = { workspace = true }
nimiq-keys = { workspace = true }
nimiq-macros = { workspace = true }
nimiq-network-interface = { workspace = true }
nimiq-hash = { workspace = true }
Expand Down Expand Up @@ -92,5 +92,6 @@ nimiq-test-log = { workspace = true }
nimiq-test-utils = { workspace = true }

[features]
kad = []
metrics = ["prometheus-client"]
tokio-websocket = ["libp2p/dns", "libp2p/tcp", "libp2p/tokio", "libp2p/websocket"]
Loading
Loading