Skip to content
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
35 changes: 28 additions & 7 deletions Cargo.lock

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

6 changes: 1 addition & 5 deletions crates/astria-celestia-client/src/blob_space.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,7 @@ use sha2::{
Sha256,
};
use tendermint::{
block::{
Commit,
Header,
},
block::Header,
Hash,
};

Expand Down Expand Up @@ -47,7 +44,6 @@ pub fn celestia_namespace_v0_from_hashed_bytes(bytes: &[u8]) -> Namespace {
pub struct SequencerNamespaceData {
pub block_hash: Hash,
pub header: Header,
pub last_commit: Option<Commit>,
pub rollup_chain_ids: Vec<ChainId>,
pub action_tree_root: [u8; 32],
pub action_tree_root_inclusion_proof: InclusionProof,
Expand Down
2 changes: 0 additions & 2 deletions crates/astria-celestia-client/src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,6 @@ fn assemble_blobs_from_sequencer_block_data(
let RawSequencerBlockData {
block_hash,
header,
last_commit,
rollup_data,
action_tree_root,
action_tree_root_inclusion_proof,
Expand Down Expand Up @@ -264,7 +263,6 @@ fn assemble_blobs_from_sequencer_block_data(
let sequencer_namespace_data = SequencerNamespaceData {
block_hash,
header,
last_commit,
rollup_chain_ids: chain_ids,
action_tree_root,
action_tree_root_inclusion_proof,
Expand Down
118 changes: 61 additions & 57 deletions crates/astria-conductor/src/block_verifier.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
use std::collections::HashMap;

use astria_sequencer_types::calculate_last_commit_hash;
use celestia_client::{
RollupNamespaceData,
SequencerNamespaceData,
Expand Down Expand Up @@ -78,16 +77,23 @@ impl BlockVerifier {

// get validator set for the previous height, as the commit contained
// in the block is for the previous height
let parent_validator_set = client
.validators(
height - 1,
sequencer_client::tendermint_rpc::Paging::Default,
)
let commit = client
.commit(height)
.await
.wrap_err("failed to get validator set")?;
.wrap_err("failed to get commit")?;

validate_sequencer_namespace_data(&current_validator_set, &parent_validator_set, data)
.wrap_err("failed validating sequencer data inside signed namespace data")
// validate commit is for our block
ensure!(
commit.signed_header.header.hash() == data.block_hash,
"commit is not for the expected block",
);

validate_sequencer_namespace_data(
&current_validator_set,
&commit.signed_header.commit,
data,
)
.wrap_err("failed validating sequencer data inside signed namespace data")
}
}

Expand All @@ -102,15 +108,14 @@ pub(crate) fn validate_rollup_data(

fn validate_sequencer_namespace_data(
current_validator_set: &validators::Response,
parent_validator_set: &validators::Response,
commit: &block::Commit,
data: &SequencerNamespaceData,
) -> eyre::Result<()> {
use sha2::Digest as _;

let SequencerNamespaceData {
block_hash,
header,
last_commit,
rollup_chain_ids: _,
action_tree_root,
action_tree_root_inclusion_proof,
Expand All @@ -132,35 +137,10 @@ fn validate_sequencer_namespace_data(
`{received_proposer_address}`",
);

match &last_commit {
Some(last_commit) => {
// validate that commit signatures hash to header.last_commit_hash
let calculated_last_commit_hash = calculate_last_commit_hash(last_commit);
let Some(last_commit_hash) = header.last_commit_hash.as_ref() else {
bail!("last commit hash should not be empty");
};

if &calculated_last_commit_hash != last_commit_hash {
bail!("last commit hash in header does not match calculated last commit hash");
}

// verify that the validator votes on the previous block have >2/3 voting power
let last_commit = last_commit.clone();
let chain_id = header.chain_id.clone();
ensure_commit_has_quorum(&last_commit, parent_validator_set, chain_id.as_ref())
.wrap_err("failed to ensure commit has quorum")?

// TODO: commit is for previous block; how do we handle this? (#50)
}
None => {
// the last commit can only be empty on block 1
ensure!(header.height == 1u32.into(), "last commit hash not found");
ensure!(
header.last_commit_hash.is_none(),
"last commit hash should be empty"
);
}
}
// verify that the validator votes on the block have >2/3 voting power
let chain_id = header.chain_id.clone();
ensure_commit_has_quorum(commit, current_validator_set, chain_id.as_ref())
.wrap_err("failed to ensure commit has quorum")?;

// validate the block header matches the block hash
let block_hash_from_header = header.hash();
Expand Down Expand Up @@ -391,7 +371,10 @@ mod test {

use super::*;

fn make_test_validator_set(height: u32) -> (validators::Response, account::Id) {
fn make_test_validator_set_and_commit(
height: u32,
chain_id: chain::Id,
) -> (validators::Response, account::Id, Commit) {
use rand::rngs::OsRng;

let signing_key = ed25519_consensus::SigningKey::new(OsRng);
Expand All @@ -409,9 +392,38 @@ mod test {
name: None,
};

let round = 0u16;
let timestamp = tendermint::Time::unix_epoch();
let canonical_vote = CanonicalVote {
vote_type: vote::Type::Precommit,
height: height.into(),
round: round.into(),
block_id: None,
timestamp: Some(timestamp),
chain_id,
};

let message = tendermint_proto::types::CanonicalVote::try_from(canonical_vote)
.unwrap()
.encode_length_delimited_to_vec();

let signature = signing_key.sign(&message);

let commit = tendermint::block::Commit {
height: height.into(),
round: round.into(),
signatures: vec![tendermint::block::CommitSig::BlockIdFlagCommit {
validator_address: address,
timestamp,
signature: Some(signature.into()),
}],
..Default::default()
};

(
validators::Response::new(height.into(), vec![validator], 1),
address,
commit,
)
}

Expand All @@ -431,27 +443,23 @@ mod test {
let height = header.height.value() as u32;
header.data_hash = Some(Hash::try_from(data_hash.to_vec()).unwrap());

let (validator_set, proposer_address) = make_test_validator_set(height);
let (validator_set, proposer_address, commit) =
make_test_validator_set_and_commit(height, header.chain_id.clone());
header.proposer_address = proposer_address;
let block_hash = header.hash();

let sequencer_namespace_data = SequencerNamespaceData {
block_hash,
header,
last_commit: None,
rollup_chain_ids: vec![],
action_tree_root,
action_tree_root_inclusion_proof,
chain_ids_commitment,
chain_ids_commitment_inclusion_proof,
};

validate_sequencer_namespace_data(
&validator_set,
&make_test_validator_set(height - 1).0,
&sequencer_namespace_data,
)
.unwrap();
validate_sequencer_namespace_data(&validator_set, &commit, &sequencer_namespace_data)
.unwrap();
}

#[tokio::test]
Expand All @@ -476,14 +484,14 @@ mod test {
let height = header.height.value() as u32;
header.data_hash = Some(Hash::try_from(data_hash.to_vec()).unwrap());

let (validator_set, proposer_address) = make_test_validator_set(height);
let (validator_set, proposer_address, commit) =
make_test_validator_set_and_commit(height, header.chain_id.clone());
header.proposer_address = proposer_address;
let block_hash = header.hash();

let sequencer_namespace_data = SequencerNamespaceData {
block_hash,
header,
last_commit: None,
rollup_chain_ids: vec![
astria_sequencer_types::ChainId::new(test_chain_id.to_vec()).unwrap(),
],
Expand All @@ -500,12 +508,8 @@ mod test {
inclusion_proof: action_tree.prove_inclusion(0).unwrap(),
};

validate_sequencer_namespace_data(
&validator_set,
&make_test_validator_set(height - 1).0,
&sequencer_namespace_data,
)
.unwrap();
validate_sequencer_namespace_data(&validator_set, &commit, &sequencer_namespace_data)
.unwrap();
rollup_namespace_data
.verify_inclusion_proof(sequencer_namespace_data.action_tree_root)
.unwrap();
Expand Down
1 change: 0 additions & 1 deletion crates/astria-conductor/src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@ impl SequencerBlockSubset {
let RawSequencerBlockData {
block_hash,
header,
last_commit: _,
mut rollup_data,
..
} = data.into_raw();
Expand Down
Loading