Skip to content

Commit

Permalink
feat: use block producer address instead of pub key when validating (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
segfault-magnet authored Aug 29, 2024
1 parent 0a34856 commit 03ad531
Show file tree
Hide file tree
Showing 10 changed files with 66 additions and 52 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

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

4 changes: 2 additions & 2 deletions committer/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,8 @@ pub struct Fuel {
/// URL to a fuel-core graphql endpoint.
#[serde(deserialize_with = "parse_url")]
pub graphql_endpoint: Url,
/// Block producer public key
pub block_producer_public_key: ports::fuel::FuelPublicKey,
/// Block producer address
pub block_producer_address: ports::fuel::FuelBytes32,
}

#[derive(Debug, Clone, Deserialize)]
Expand Down
4 changes: 2 additions & 2 deletions committer/src/setup.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ pub fn block_committer(
registry: &Registry,
cancel_token: CancellationToken,
) -> tokio::task::JoinHandle<()> {
let validator = BlockValidator::new(config.fuel.block_producer_public_key);
let validator = BlockValidator::new(*config.fuel.block_producer_address);

let block_committer = BlockCommitter::new(l1, storage, fuel, validator, commit_interval);

Expand Down Expand Up @@ -94,7 +94,7 @@ pub fn state_importer(
cancel_token: CancellationToken,
config: &config::Config,
) -> tokio::task::JoinHandle<()> {
let validator = BlockValidator::new(config.fuel.block_producer_public_key);
let validator = BlockValidator::new(*config.fuel.block_producer_address);
let state_importer = services::StateImporter::new(storage, fuel, validator);

schedule_polling(
Expand Down
14 changes: 5 additions & 9 deletions e2e/src/committer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ use std::{path::Path, time::Duration};
use anyhow::Context;
use eth::AwsRegion;
use ethers::abi::Address;
use ports::fuel::FuelPublicKey;
use url::Url;

#[derive(Default)]
Expand All @@ -14,7 +13,7 @@ pub struct Committer {
state_contract_address: Option<String>,
eth_rpc: Option<Url>,
fuel_rpc: Option<Url>,
fuel_block_producer_public_key: Option<String>,
fuel_block_producer_addr: Option<String>,
db_port: Option<u16>,
db_name: Option<String>,
aws_region: Option<AwsRegion>,
Expand Down Expand Up @@ -51,8 +50,8 @@ impl Committer {
get_field!(fuel_rpc).as_str(),
)
.env(
"COMMITTER__FUEL__BLOCK_PRODUCER_PUBLIC_KEY",
get_field!(fuel_block_producer_public_key),
"COMMITTER__FUEL__BLOCK_PRODUCER_ADDRESS",
get_field!(fuel_block_producer_addr),
)
.env("COMMITTER__APP__DB__PORT", get_field!(db_port).to_string())
.env("COMMITTER__APP__DB__DATABASE", get_field!(db_name))
Expand Down Expand Up @@ -110,11 +109,8 @@ impl Committer {
self
}

pub fn with_fuel_block_producer_public_key(
mut self,
fuel_block_producer_public_key: FuelPublicKey,
) -> Self {
self.fuel_block_producer_public_key = Some(fuel_block_producer_public_key.to_string());
pub fn with_fuel_block_producer_addr(mut self, fuel_block_producer_addr: [u8; 32]) -> Self {
self.fuel_block_producer_addr = Some(hex::encode(fuel_block_producer_addr));
self
}

Expand Down
4 changes: 2 additions & 2 deletions e2e/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,8 @@ mod tests {

let latest_block = stack.fuel_node.client().latest_block().await?;

let validated_block =
BlockValidator::new(stack.fuel_node.consensus_pub_key()).validate(&latest_block)?;
let validated_block = BlockValidator::new(*stack.fuel_node.consensus_pub_key().hash())
.validate(&latest_block)?;

assert!(stack.deployed_contract.finalized(validated_block).await?);

Expand Down
2 changes: 1 addition & 1 deletion e2e/src/whole_stack.rs
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ async fn start_committer(
.with_db_port(random_db.port())
.with_db_name(random_db.db_name())
.with_state_contract_address(deployed_contract.address())
.with_fuel_block_producer_public_key(fuel_node.consensus_pub_key())
.with_fuel_block_producer_addr(*fuel_node.consensus_pub_key().hash())
.with_main_key_id(main_key.id.clone())
.with_aws_region(kms.region().clone());

Expand Down
10 changes: 5 additions & 5 deletions packages/services/src/block_committer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -255,7 +255,7 @@ mod tests {
async fn will_fetch_and_submit_missed_block() {
// given
let secret_key = given_secret_key();
let block_validator = BlockValidator::new(secret_key.public_key());
let block_validator = BlockValidator::new(*secret_key.public_key().hash());
let missed_block = given_a_block(4, &secret_key);
let latest_block = given_a_block(5, &secret_key);
let fuel_adapter = given_fetcher(vec![latest_block, missed_block.clone()]);
Expand All @@ -278,7 +278,7 @@ mod tests {
async fn will_not_reattempt_submitting_missed_block() {
// given
let secret_key = given_secret_key();
let block_validator = BlockValidator::new(secret_key.public_key());
let block_validator = BlockValidator::new(*secret_key.public_key().hash());
let missed_block = given_a_block(4, &secret_key);
let latest_block = given_a_block(5, &secret_key);
let fuel_adapter = given_fetcher(vec![latest_block, missed_block]);
Expand All @@ -303,7 +303,7 @@ mod tests {
async fn will_not_reattempt_committing_latest_block() {
// given
let secret_key = given_secret_key();
let block_validator = BlockValidator::new(secret_key.public_key());
let block_validator = BlockValidator::new(*secret_key.public_key().hash());
let latest_block = given_a_block(6, &secret_key);
let fuel_adapter = given_fetcher(vec![latest_block]);

Expand All @@ -327,7 +327,7 @@ mod tests {
async fn propagates_block_if_epoch_reached() {
// given
let secret_key = given_secret_key();
let block_validator = BlockValidator::new(secret_key.public_key());
let block_validator = BlockValidator::new(*secret_key.public_key().hash());
let block = given_a_block(4, &secret_key);
let fuel_adapter = given_fetcher(vec![block.clone()]);

Expand All @@ -348,7 +348,7 @@ mod tests {
async fn updates_block_metric_regardless_if_block_is_published() {
// given
let secret_key = given_secret_key();
let block_validator = BlockValidator::new(secret_key.public_key());
let block_validator = BlockValidator::new(*secret_key.public_key().hash());
let block = given_a_block(5, &secret_key);
let fuel_adapter = given_fetcher(vec![block]);

Expand Down
2 changes: 1 addition & 1 deletion packages/services/src/state_importer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,7 @@ mod tests {
let secret_key = given_secret_key();
let block = given_a_block(1, &secret_key);
let fuel_mock = given_fetcher(block);
let block_validator = BlockValidator::new(secret_key.public_key());
let block_validator = BlockValidator::new(*secret_key.public_key().hash());

let process = PostgresProcess::shared().await.unwrap();
let db = process.create_random_db().await?;
Expand Down
1 change: 1 addition & 0 deletions packages/validator/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ mockall = { workspace = true, optional = true }
rand = { workspace = true, optional = true }
serde = { workspace = true, features = ["derive"] }
thiserror = { workspace = true }
hex = { workspace = true }

[dev-dependencies]
fuel-crypto = { workspace = true, features = ["random"] }
Expand Down
76 changes: 46 additions & 30 deletions packages/validator/src/validator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,15 @@ use fuel_core_client::client::types::{
Block as FuelBlock, Consensus as FuelConsensus, Header as FuelHeader,
PoAConsensus as FuelPoAConsensus,
},
primitives::{BlockId as FuelBlockId, Bytes32 as FuelBytes32, PublicKey as FuelPublicKey},
primitives::{BlockId as FuelBlockId, Bytes32 as FuelBytes32},
};
use fuel_crypto::{Hasher, Message};

use crate::{block::ValidatedFuelBlock, Error, Result, Validator};

#[derive(Debug)]
pub struct BlockValidator {
producer_pub_key: FuelPublicKey,
producer_addr: [u8; 32],
}

impl Validator for BlockValidator {
Expand All @@ -21,12 +21,12 @@ impl Validator for BlockValidator {
}

impl BlockValidator {
pub fn new(producer_pub_key: FuelPublicKey) -> Self {
Self { producer_pub_key }
pub fn new(producer_addr: [u8; 32]) -> Self {
Self { producer_addr }
}

fn _validate(&self, fuel_block: &FuelBlock) -> Result<ValidatedFuelBlock> {
self.validate_public_key(fuel_block)?;
self.validate_producer_addr(fuel_block)?;
Self::validate_block_id(fuel_block)?;
self.validate_block_signature(fuel_block)?;

Expand All @@ -36,18 +36,18 @@ impl BlockValidator {
})
}

fn validate_public_key(&self, fuel_block: &FuelBlock) -> Result<()> {
let Some(producer_pub_key) = fuel_block.block_producer() else {
fn validate_producer_addr(&self, fuel_block: &FuelBlock) -> Result<()> {
let Some(producer_addr) = fuel_block.block_producer().map(|key| key.hash()) else {
return Err(Error::BlockValidation(
"producer public key not found in fuel block".to_string(),
));
};

if *producer_pub_key != self.producer_pub_key {
if *producer_addr != self.producer_addr {
return Err(Error::BlockValidation(format!(
"producer public key `{producer_pub_key:x}` does not match \
expected public key `{:x}`.",
self.producer_pub_key
"producer addr '{}' does not match expected addr '{}'.",
hex::encode(producer_addr),
hex::encode(self.producer_addr)
)));
}

Expand Down Expand Up @@ -75,16 +75,23 @@ impl BlockValidator {
));
};

let block_id_message = Message::from_bytes(*fuel_block.id);

signature
.verify(&self.producer_pub_key, &block_id_message)
.map_err(|_| {
let recovered_producer_addr = *signature
.recover(&Message::from_bytes(*fuel_block.id))
.map_err(|e| {
Error::BlockValidation(format!(
"signature validation failed for fuel block with id: `{:x}` and pub key: `{:x}`",
fuel_block.id, &self.producer_pub_key
"failed to recover public key from PoAConsensus signature: {e:?}",
))
})?;
})?
.hash();

if recovered_producer_addr != self.producer_addr {
return Err(Error::BlockValidation(format!(
"recovered producer addr `{}` does not match \
expected addr`{}`.",
hex::encode(recovered_producer_addr),
hex::encode(self.producer_addr)
)));
}

Ok(())
}
Expand Down Expand Up @@ -146,17 +153,17 @@ mod tests {
#[should_panic(expected = "producer public key not found in fuel block")]
fn validate_public_key_missing() {
let fuel_block = given_a_block(None);
let validator = BlockValidator::new(FuelPublicKey::default());
let validator = BlockValidator::new([0; 32]);

validator.validate(&fuel_block).unwrap();
}

#[test]
#[should_panic(expected = "does not match expected public key")]
#[should_panic(expected = "does not match expected addr")]
fn validate_public_key_mistmach() {
let secret_key = given_secret_key();
let fuel_block = given_a_block(Some(secret_key));
let validator = BlockValidator::new(FuelPublicKey::default());
let validator = BlockValidator::new([0; 32]);

validator.validate(&fuel_block).unwrap();
}
Expand All @@ -167,7 +174,7 @@ mod tests {
let secret_key = given_secret_key();
let mut fuel_block = given_a_block(Some(secret_key));
fuel_block.header.height = 42; // Change a value to get a different block id
let validator = BlockValidator::new(secret_key.public_key());
let validator = BlockValidator::new(*secret_key.public_key().hash());

validator.validate(&fuel_block).unwrap();
}
Expand All @@ -178,20 +185,29 @@ mod tests {
let secret_key = given_secret_key();
let mut fuel_block = given_a_block(Some(secret_key));
fuel_block.consensus = FuelConsensus::Unknown;
let validator = BlockValidator::new(secret_key.public_key());
let validator = BlockValidator::new(*secret_key.public_key().hash());

validator.validate(&fuel_block).unwrap();
}

#[test]
#[should_panic(expected = "signature validation failed for fuel block with id:")]
#[should_panic(
expected = "recovered producer addr `286b769a36b01cebc43cd9820ba709b438b14566e16a287c36881194eacc45c6` does not match expected addr`f95112e76de29dca6ed315c5a5be7855e62dee55478077cf209554d5bfb7cd85`."
)]
fn validate_block_consensus_invalid_signature() {
let secret_key = given_secret_key();
let mut fuel_block = given_a_block(Some(secret_key));
let correct_secret_key = given_secret_key();

let mut fuel_block = given_a_block(Some(correct_secret_key));
let invalid_signature = {
let different_secret_key = SecretKey::random(&mut StdRng::seed_from_u64(43));
let id_message = Message::from_bytes(*fuel_block.id);
Signature::sign(&different_secret_key, &id_message)
};

fuel_block.consensus = FuelConsensus::PoAConsensus(FuelPoAConsensus {
signature: Signature::default(),
signature: invalid_signature,
});
let validator = BlockValidator::new(secret_key.public_key());
let validator = BlockValidator::new(*correct_secret_key.public_key().hash());

validator.validate(&fuel_block).unwrap();
}
Expand All @@ -200,7 +216,7 @@ mod tests {
fn validate_fuel_block() {
let secret_key = given_secret_key();
let fuel_block = given_a_block(Some(secret_key));
let validator = BlockValidator::new(secret_key.public_key());
let validator = BlockValidator::new(*secret_key.public_key().hash());

validator.validate(&fuel_block).unwrap();
}
Expand Down

0 comments on commit 03ad531

Please sign in to comment.