diff --git a/crates/astria-core/src/protocol/test_utils.rs b/crates/astria-core/src/protocol/test_utils.rs index 99284603d..74b3a2c27 100644 --- a/crates/astria-core/src/protocol/test_utils.rs +++ b/crates/astria-core/src/protocol/test_utils.rs @@ -18,7 +18,10 @@ use crate::{ }, protocol::transaction::v1::TransactionBody, sequencerblock::v1::{ - block::Deposit, + block::{ + Deposit, + SequencerBlockBuilder, + }, SequencerBlock, }, Protobuf as _, @@ -53,6 +56,7 @@ pub struct ConfigureSequencerBlock { pub sequence_data: Vec<(RollupId, Vec)>, pub deposits: Vec, pub unix_timestamp: UnixTimeStamp, + pub with_extended_commit_info: bool, } impl ConfigureSequencerBlock { @@ -60,6 +64,7 @@ impl ConfigureSequencerBlock { #[must_use] #[expect( clippy::missing_panics_doc, + clippy::too_many_lines, reason = "This should only be used in tests, so everything here is unwrapped" )] pub fn make(self) -> SequencerBlock { @@ -79,6 +84,7 @@ impl ConfigureSequencerBlock { sequence_data, unix_timestamp, deposits, + with_extended_commit_info, } = self; let block_hash = block_hash.unwrap_or_default(); @@ -142,13 +148,6 @@ impl ConfigureSequencerBlock { rollup_transactions.sort_unstable_keys(); let rollup_transactions_tree = derive_merkle_tree_from_rollup_txs(&rollup_transactions); - let extended_commit_info: tendermint_proto::abci::ExtendedCommitInfo = ExtendedCommitInfo { - round: 0u16.into(), - votes: vec![], - } - .into(); - let extended_commit_info_bytes = extended_commit_info.encode_to_vec(); - let rollup_ids_root = merkle::Tree::from_leaves( rollup_transactions .keys() @@ -156,21 +155,35 @@ impl ConfigureSequencerBlock { ) .root(); let mut data = vec![ - extended_commit_info_bytes, rollup_transactions_tree.root().to_vec(), rollup_ids_root.to_vec(), ]; + + if with_extended_commit_info { + let extended_commit_info: tendermint_proto::abci::ExtendedCommitInfo = + ExtendedCommitInfo { + round: 0u16.into(), + votes: vec![], + } + .into(); + let extended_commit_info_bytes = extended_commit_info.encode_to_vec(); + data.push(extended_commit_info_bytes); + } + data.extend(txs.into_iter().map(|tx| tx.into_raw().encode_to_vec())); let data = data.into_iter().map(Bytes::from).collect(); - SequencerBlock::try_from_block_info_and_data( + + SequencerBlockBuilder { block_hash, - chain_id.try_into().unwrap(), - height.into(), - Time::from_unix_timestamp(unix_timestamp.secs, unix_timestamp.nanos).unwrap(), + chain_id: chain_id.try_into().unwrap(), + height: height.into(), + time: Time::from_unix_timestamp(unix_timestamp.secs, unix_timestamp.nanos).unwrap(), proposer_address, data, - deposits_map, - ) + deposits: deposits_map, + with_extended_commit_info, + } + .try_build() .unwrap() } } diff --git a/crates/astria-core/src/sequencerblock/v1/block.rs b/crates/astria-core/src/sequencerblock/v1/block.rs index e2cd5d0d0..0d2528b51 100644 --- a/crates/astria-core/src/sequencerblock/v1/block.rs +++ b/crates/astria-core/src/sequencerblock/v1/block.rs @@ -595,6 +595,148 @@ pub struct SequencerBlockParts { pub rollup_ids_proof: merkle::Proof, } +pub struct SequencerBlockBuilder { + pub block_hash: [u8; 32], + pub chain_id: tendermint::chain::Id, + pub height: tendermint::block::Height, + pub time: Time, + pub proposer_address: account::Id, + pub data: Vec, + pub deposits: HashMap>, + pub with_extended_commit_info: bool, +} + +impl SequencerBlockBuilder { + /// Attempts to build a [`SequencerBlock`] from the provided data. + /// + /// # Errors + /// + /// - if the first transaction in the data is not the 32-byte rollup transactions root. + /// - if the second transaction in the data is not the 32-byte rollup IDs root. + /// - if `with_extended_commit_info` is set to `true` and the third transaction in the data is + /// not the extended commit info. + /// - if any transaction in the data cannot be decoded. + /// - if the rollup transactions root does not match the reconstructed root. + /// - if the rollup IDs root does not match the reconstructed root. + /// + /// # Panics + /// + /// - if a rollup data merkle proof cannot be constructed. + pub fn try_build(self) -> Result { + use prost::Message as _; + + let SequencerBlockBuilder { + block_hash, + chain_id, + height, + time, + proposer_address, + data, + deposits, + with_extended_commit_info, + } = self; + + let InitialDataElements { + data_root_hash, + // This iterator over `data` has been advanced to skip the injected transactions while + // retrieving the initial elements. The next element to be yielded will be + // the first of the actual user-submitted txs. + data_iter, + // TODO: this needs to go into the block header + _extended_commit_info, + rollup_transactions_root, + rollup_transactions_proof, + rollup_ids_root, + rollup_ids_proof, + } = take_initial_elements_from_data(data, with_extended_commit_info)?; + + let mut rollup_datas = IndexMap::new(); + for elem in data_iter { + let raw_tx = crate::generated::protocol::transaction::v1::Transaction::decode(&*elem) + .map_err(SequencerBlockError::transaction_protobuf_decode)?; + let tx = Transaction::try_from_raw(raw_tx) + .map_err(SequencerBlockError::raw_signed_transaction_conversion)?; + for action in tx.into_unsigned().into_actions() { + // XXX: The fee asset is dropped. We should explain why that's ok. + if let action::Action::RollupDataSubmission(action::RollupDataSubmission { + rollup_id, + data, + fee_asset: _, + }) = action + { + let elem = rollup_datas.entry(rollup_id).or_insert(vec![]); + let data = RollupData::SequencedData(data) + .into_raw() + .encode_to_vec() + .into(); + elem.push(data); + } + } + } + for (id, deposits) in deposits { + rollup_datas + .entry(id) + .or_default() + .extend(deposits.into_iter().map(|deposit| { + RollupData::Deposit(Box::new(deposit)) + .into_raw() + .encode_to_vec() + .into() + })); + } + + // XXX: The rollup data must be sorted by its keys before constructing the merkle tree. + // Since it's constructed from non-deterministically ordered sources, there is otherwise no + // guarantee that the same data will give the root. + rollup_datas.sort_unstable_keys(); + + // ensure the rollup IDs commitment matches the one calculated from the rollup data + if rollup_ids_root != merkle::Tree::from_leaves(rollup_datas.keys()).root() { + return Err(SequencerBlockError::rollup_ids_root_does_not_match_reconstructed()); + } + + let rollup_transaction_tree = derive_merkle_tree_from_rollup_txs(&rollup_datas); + if rollup_transactions_root != rollup_transaction_tree.root() { + return Err( + SequencerBlockError::rollup_transactions_root_does_not_match_reconstructed(), + ); + } + + let mut rollup_transactions = IndexMap::new(); + for (i, (rollup_id, data)) in rollup_datas.into_iter().enumerate() { + // NOTE: This should never error as the tree was derived with the same `rollup_datas` + // just above. + let proof = rollup_transaction_tree + .construct_proof(i) + .expect("the proof must exist because the tree was derived with the same leaf"); + rollup_transactions.insert( + rollup_id, + RollupTransactions { + rollup_id, + transactions: data, // TODO: rename this field? + proof, + }, + ); + } + rollup_transactions.sort_unstable_keys(); + + Ok(SequencerBlock { + block_hash, + header: SequencerBlockHeader { + chain_id, + height, + time, + rollup_transactions_root, + data_hash: data_root_hash, + proposer_address, + }, + rollup_transactions, + rollup_transactions_proof, + rollup_ids_proof, + }) + } +} + /// `SequencerBlock` is constructed from a tendermint/cometbft block by /// converting its opaque `data` bytes into sequencer specific types. #[derive(Clone, Debug, PartialEq)] @@ -764,124 +906,6 @@ impl SequencerBlock { celestia::PreparedBlock::from_sequencer_block(self).into_parts() } - /// Converts from relevant header fields and the block data. - /// - /// # Errors - /// TODO(https://github.com/astriaorg/astria/issues/612) - /// - /// # Panics - /// - /// - if a rollup data merkle proof cannot be constructed. - pub fn try_from_block_info_and_data( - block_hash: [u8; 32], - chain_id: tendermint::chain::Id, - height: tendermint::block::Height, - time: Time, - proposer_address: account::Id, - data: Vec, - deposits: HashMap>, - ) -> Result { - use prost::Message as _; - - let InitialDataElements { - data_root_hash, - // This iterator over `data` has been advanced by 3 steps while retrieving the initial - // elements. The next element to be yielded will be the first of the actual rollup txs. - data_iter, - // TODO: this needs to go into the block header - _extended_commit_info, - rollup_transactions_root, - rollup_transactions_proof, - rollup_ids_root, - rollup_ids_proof, - } = take_initial_elements_from_data(data)?; - - let mut rollup_datas = IndexMap::new(); - for elem in data_iter { - let raw_tx = crate::generated::protocol::transaction::v1::Transaction::decode(&*elem) - .map_err(SequencerBlockError::transaction_protobuf_decode)?; - let tx = Transaction::try_from_raw(raw_tx) - .map_err(SequencerBlockError::raw_signed_transaction_conversion)?; - for action in tx.into_unsigned().into_actions() { - // XXX: The fee asset is dropped. We shjould explain why that's ok. - if let action::Action::RollupDataSubmission(action::RollupDataSubmission { - rollup_id, - data, - fee_asset: _, - }) = action - { - let elem = rollup_datas.entry(rollup_id).or_insert(vec![]); - let data = RollupData::SequencedData(data) - .into_raw() - .encode_to_vec() - .into(); - elem.push(data); - } - } - } - for (id, deposits) in deposits { - rollup_datas - .entry(id) - .or_default() - .extend(deposits.into_iter().map(|deposit| { - RollupData::Deposit(Box::new(deposit)) - .into_raw() - .encode_to_vec() - .into() - })); - } - - // XXX: The rollup data must be sorted by its keys before constructing the merkle tree. - // Since it's constructed from non-deterministically ordered sources, there is otherwise no - // guarantee that the same data will give the root. - rollup_datas.sort_unstable_keys(); - - // ensure the rollup IDs commitment matches the one calculated from the rollup data - if rollup_ids_root != merkle::Tree::from_leaves(rollup_datas.keys()).root() { - return Err(SequencerBlockError::rollup_ids_root_does_not_match_reconstructed()); - } - - let rollup_transaction_tree = derive_merkle_tree_from_rollup_txs(&rollup_datas); - if rollup_transactions_root != rollup_transaction_tree.root() { - return Err( - SequencerBlockError::rollup_transactions_root_does_not_match_reconstructed(), - ); - } - - let mut rollup_transactions = IndexMap::new(); - for (i, (rollup_id, data)) in rollup_datas.into_iter().enumerate() { - // NOTE: This should never error as the tree was derived with the same `rollup_datas` - // just above. - let proof = rollup_transaction_tree - .construct_proof(i) - .expect("the proof must exist because the tree was derived with the same leaf"); - rollup_transactions.insert( - rollup_id, - RollupTransactions { - rollup_id, - transactions: data, // TODO: rename this field? - proof, - }, - ); - } - rollup_transactions.sort_unstable_keys(); - - Ok(Self { - block_hash, - header: SequencerBlockHeader { - chain_id, - height, - time, - rollup_transactions_root, - data_hash: data_root_hash, - proposer_address, - }, - rollup_transactions, - rollup_transactions_proof, - rollup_ids_proof, - }) - } - /// Converts from the raw decoded protobuf representation of this type. /// /// # Errors @@ -1000,7 +1024,7 @@ impl SequencerBlock { struct InitialDataElements { data_root_hash: [u8; 32], data_iter: IntoIter, - _extended_commit_info: Bytes, + _extended_commit_info: Option, rollup_transactions_root: [u8; 32], rollup_transactions_proof: merkle::Proof, rollup_ids_root: [u8; 32], @@ -1009,15 +1033,12 @@ struct InitialDataElements { fn take_initial_elements_from_data( data: Vec, + with_extended_commit_info: bool, ) -> Result { let tree = merkle_tree_from_data(&data); let data_root_hash = tree.root(); let mut data_iter = data.into_iter(); - let extended_commit_info = data_iter - .next() - .ok_or(SequencerBlockError::no_extended_commit_info())?; - let rollup_transactions_root: [u8; 32] = data_iter .next() .ok_or(SequencerBlockError::no_rollup_transactions_root())? @@ -1026,7 +1047,7 @@ fn take_initial_elements_from_data( .map_err(|_| { SequencerBlockError::incorrect_rollup_transactions_root_length(data_iter.len()) })?; - let rollup_transactions_proof = tree.construct_proof(1).expect( + let rollup_transactions_proof = tree.construct_proof(0).expect( "the leaf must exist in the tree as `rollup_transactions_root` was created from the same \ index in `data` used to construct the tree", ); @@ -1037,11 +1058,21 @@ fn take_initial_elements_from_data( .as_ref() .try_into() .map_err(|_| SequencerBlockError::incorrect_rollup_ids_root_length(data_iter.len()))?; - let rollup_ids_proof = tree.construct_proof(2).expect( + let rollup_ids_proof = tree.construct_proof(1).expect( "the leaf must exist in the tree as `rollup_ids_root` was created from the same index in \ `data` used to construct the tree", ); + let extended_commit_info = if with_extended_commit_info { + Some( + data_iter + .next() + .ok_or(SequencerBlockError::no_extended_commit_info())?, + ) + } else { + None + }; + Ok(InitialDataElements { data_root_hash, data_iter, diff --git a/crates/astria-sequencer/src/app/mod.rs b/crates/astria-sequencer/src/app/mod.rs index 658862207..01c5fc138 100644 --- a/crates/astria-sequencer/src/app/mod.rs +++ b/crates/astria-sequencer/src/app/mod.rs @@ -37,7 +37,7 @@ use astria_core::{ Transaction, }, }, - sequencerblock::v1::block::SequencerBlock, + sequencerblock::v1::block::SequencerBlockBuilder, Protobuf as _, }; use astria_eyre::{ @@ -160,9 +160,9 @@ const INJECTED_TRANSACTIONS_COUNT_BEFORE_VOTE_EXTENSIONS_ENABLED: usize = 2; // after vote extensions are enabled. // // consists of: -// 1. encoded `ExtendedCommitInfo` for the previous block -// 2. rollup data root -// 3. rollup IDs root +// 1. rollup data root +// 2. rollup IDs root +// 3. encoded `ExtendedCommitInfo` for the previous block const INJECTED_TRANSACTIONS_COUNT_AFTER_VOTE_EXTENSIONS_ENABLED: usize = 3; // the height to set the `vote_extensions_enable_height` to in state if vote extensions are @@ -525,11 +525,11 @@ impl App { let res = generate_rollup_datas_commitment(&signed_txs_included, deposits); let txs = match encoded_extended_commit_info { - Some(encoded_extended_commit_info) => { - std::iter::once(encoded_extended_commit_info.into()) - .chain(res.into_iter().chain(included_tx_bytes)) - .collect() - } + Some(encoded_extended_commit_info) => res + .into_iter() + .chain(std::iter::once(encoded_extended_commit_info.into())) + .chain(included_tx_bytes) + .collect(), None => res.into_iter().chain(included_tx_bytes).collect(), }; @@ -599,8 +599,22 @@ impl App { .await .wrap_err("failed to get vote extensions enabled height")?; + let received_rollup_datas_root: [u8; 32] = txs + .pop_front() + .ok_or_eyre("no transaction commitment in proposal")? + .to_vec() + .try_into() + .map_err(|_| eyre!("transaction commitment must be 32 bytes"))?; + + let received_rollup_ids_root: [u8; 32] = txs + .pop_front() + .ok_or_eyre("no chain IDs commitment in proposal")? + .to_vec() + .try_into() + .map_err(|_| eyre!("chain IDs commitment must be 32 bytes"))?; + if vote_extensions_enable_height <= process_proposal.height.value() { - // if vote extensions are enabled, the first transaction in the block should be the + // if vote extensions are enabled, the third transaction in the block should be the // extended commit info let extended_commit_info_bytes = txs .pop_front() @@ -626,20 +640,6 @@ impl App { .wrap_err("failed to validate extended commit info")?; } - let received_rollup_datas_root: [u8; 32] = txs - .pop_front() - .ok_or_eyre("no transaction commitment in proposal")? - .to_vec() - .try_into() - .map_err(|_| eyre!("transaction commitment must be 32 bytes"))?; - - let received_rollup_ids_root: [u8; 32] = txs - .pop_front() - .ok_or_eyre("no chain IDs commitment in proposal")? - .to_vec() - .try_into() - .map_err(|_| eyre!("chain IDs commitment must be 32 bytes"))?; - let expected_txs_len = txs.len(); let block_data = BlockData { @@ -1109,7 +1109,8 @@ impl App { .get_vote_extensions_enable_height() .await .wrap_err("failed to get vote extensions enabled height")?; - let injected_txs_count = if vote_extensions_enable_height <= height.value() { + let vote_extensions_enabled = vote_extensions_enable_height <= height.value(); + let injected_txs_count = if vote_extensions_enabled { INJECTED_TRANSACTIONS_COUNT_AFTER_VOTE_EXTENSIONS_ENABLED } else { INJECTED_TRANSACTIONS_COUNT_BEFORE_VOTE_EXTENSIONS_ENABLED @@ -1125,15 +1126,17 @@ impl App { .extend(std::iter::repeat(ExecTxResult::default()).take(injected_txs_count)); finalize_block_tx_results.extend(tx_results); - let sequencer_block = SequencerBlock::try_from_block_info_and_data( + let sequencer_block = SequencerBlockBuilder { block_hash, chain_id, height, time, proposer_address, - txs, - deposits_in_this_block, - ) + data: txs, + deposits: deposits_in_this_block, + with_extended_commit_info: vote_extensions_enabled, + } + .try_build() .wrap_err("failed to convert block info and data to SequencerBlock")?; state_tx .put_sequencer_block(sequencer_block) @@ -1190,12 +1193,12 @@ impl App { ensure!( finalize_block.txs.len() >= INJECTED_TRANSACTIONS_COUNT_AFTER_VOTE_EXTENSIONS_ENABLED, - "block must contain at least three transactions: the extended commit info, \ - the rollup transactions commitment and rollup IDs commitment" + "block must contain at least three transactions: the rollup transactions \ + commitment, rollup IDs commitment, and the extended commit info" ); let extended_commit_info_bytes = - finalize_block.txs.first().expect("asserted length above"); + finalize_block.txs.get(2).expect("asserted length above"); let extended_commit_info = ExtendedCommitInfo::decode(extended_commit_info_bytes.as_ref()) .wrap_err("failed to decode extended commit info")? diff --git a/crates/astria-sequencer/src/app/test_utils.rs b/crates/astria-sequencer/src/app/test_utils.rs index 7f723ad23..8fdf579a5 100644 --- a/crates/astria-sequencer/src/app/test_utils.rs +++ b/crates/astria-sequencer/src/app/test_utils.rs @@ -197,13 +197,10 @@ pub(crate) fn transactions_with_extended_commit_info_and_commitments( } .into(); let commitments = generate_rollup_datas_commitment(txs, deposits.unwrap_or_default()); - let txs_with_commit_info: Vec = - std::iter::once(extended_commit_info.encode_to_vec().into()) - .chain( - commitments - .into_iter() - .chain(txs.iter().map(|tx| tx.to_raw().encode_to_vec().into())), - ) - .collect(); + let txs_with_commit_info: Vec = commitments + .into_iter() + .chain(std::iter::once(extended_commit_info.encode_to_vec().into())) + .chain(txs.iter().map(|tx| tx.to_raw().encode_to_vec().into())) + .collect(); txs_with_commit_info } diff --git a/crates/astria-sequencer/src/service/consensus.rs b/crates/astria-sequencer/src/service/consensus.rs index bbf45297f..57b1e4eb8 100644 --- a/crates/astria-sequencer/src/service/consensus.rs +++ b/crates/astria-sequencer/src/service/consensus.rs @@ -254,6 +254,7 @@ mod tests { primitive::v1::RollupId, protocol::transaction::v1::{ action::RollupDataSubmission, + Transaction, TransactionBody, }, Protobuf as _, @@ -314,18 +315,24 @@ mod tests { } } - fn new_process_proposal_request(txs: Vec) -> request::ProcessProposal { + fn new_process_proposal_request(txs: Vec) -> request::ProcessProposal { let extended_commit_info: tendermint_proto::abci::ExtendedCommitInfo = ExtendedCommitInfo { round: 0u16.into(), votes: vec![], } .into(); - let bytes = extended_commit_info.encode_to_vec(); - let mut txs_with_commit_info = vec![bytes.into()]; - txs_with_commit_info.extend(txs); + let commitments = generate_rollup_datas_commitment(&txs, HashMap::new()); + let txs = commitments + .into_iter() + .chain(vec![extended_commit_info.encode_to_vec().into()]) + .chain( + txs.into_iter() + .map(|tx| tx.into_raw().encode_to_vec().into()), + ) + .collect(); request::ProcessProposal { - txs: txs_with_commit_info, + txs, proposed_last_commit: Some(CommitInfo { round: 0u16.into(), votes: vec![], @@ -343,11 +350,9 @@ mod tests { async fn prepare_and_process_proposal() { let signing_key = SigningKey::new(OsRng); let (mut consensus_service, mempool) = - new_consensus_service(Some(signing_key.verification_key())).await; + new_consensus_service(Some(signing_key.verification_key()), 1).await; let tx = make_unsigned_tx(); let signed_tx = Arc::new(tx.sign(&signing_key)); - let tx_bytes = signed_tx.to_raw().encode_to_vec(); - let txs = vec![tx_bytes.into()]; mempool .insert( signed_tx.clone(), @@ -358,18 +363,14 @@ mod tests { .await .unwrap(); - let commitments = - generate_rollup_datas_commitment(&vec![(*signed_tx).clone()], HashMap::new()); - let prepare_proposal = new_prepare_proposal_request(); let prepare_proposal_response = consensus_service .handle_prepare_proposal(prepare_proposal) .await .unwrap(); - let commitments_and_txs: Vec = commitments.into_iter().chain(txs).collect(); - let expected_txs: Vec = std::iter::once(b"".to_vec().into()) - .chain(commitments_and_txs.clone()) - .collect(); + + let process_proposal = new_process_proposal_request(vec![(*signed_tx).clone()]); + let expected_txs: Vec = process_proposal.txs.clone(); assert_eq!( prepare_proposal_response, @@ -379,8 +380,7 @@ mod tests { ); let (mut consensus_service, _) = - new_consensus_service(Some(signing_key.verification_key())).await; - let process_proposal = new_process_proposal_request(commitments_and_txs); + new_consensus_service(Some(signing_key.verification_key()), 1).await; consensus_service .handle_process_proposal(process_proposal) .await @@ -391,14 +391,10 @@ mod tests { async fn process_proposal_ok() { let signing_key = SigningKey::new(OsRng); let (mut consensus_service, _) = - new_consensus_service(Some(signing_key.verification_key())).await; + new_consensus_service(Some(signing_key.verification_key()), 1).await; let tx = make_unsigned_tx(); let signed_tx = tx.sign(&signing_key); - let tx_bytes = signed_tx.clone().into_raw().encode_to_vec(); - let txs = vec![tx_bytes.into()]; - let commitments = generate_rollup_datas_commitment(&vec![signed_tx], HashMap::new()); - let tx_data = commitments.into_iter().chain(txs.clone()).collect(); - let process_proposal = new_process_proposal_request(tx_data); + let process_proposal = new_process_proposal_request(vec![signed_tx]); consensus_service .handle_process_proposal(process_proposal) @@ -408,8 +404,9 @@ mod tests { #[tokio::test] async fn process_proposal_fail_missing_action_commitment() { - let (mut consensus_service, _) = new_consensus_service(None).await; - let process_proposal = new_process_proposal_request(vec![]); + let (mut consensus_service, _) = new_consensus_service(None, 1).await; + let mut process_proposal = new_process_proposal_request(vec![]); + process_proposal.txs = vec![]; assert!( consensus_service .handle_process_proposal(process_proposal) @@ -423,8 +420,9 @@ mod tests { #[tokio::test] async fn process_proposal_fail_wrong_commitment_length() { - let (mut consensus_service, _) = new_consensus_service(None).await; - let process_proposal = new_process_proposal_request(vec![[0u8; 16].to_vec().into()]); + let (mut consensus_service, _) = new_consensus_service(None, 1).await; + let mut process_proposal = new_process_proposal_request(vec![]); + process_proposal.txs = vec![[0u8; 16].to_vec().into()]; let err = consensus_service .handle_process_proposal(process_proposal) .await @@ -436,11 +434,9 @@ mod tests { #[tokio::test] async fn process_proposal_fail_wrong_commitment_value() { - let (mut consensus_service, _) = new_consensus_service(None).await; - let process_proposal = new_process_proposal_request(vec![ - [99u8; 32].to_vec().into(), - [99u8; 32].to_vec().into(), - ]); + let (mut consensus_service, _) = new_consensus_service(None, 1).await; + let mut process_proposal = new_process_proposal_request(vec![]); + process_proposal.txs[0] = [99u8; 32].to_vec().into(); assert!( consensus_service .handle_process_proposal(process_proposal) @@ -454,17 +450,17 @@ mod tests { #[tokio::test] async fn prepare_proposal_empty_block() { - let (mut consensus_service, _) = new_consensus_service(None).await; - let txs = vec![]; - let commitments = generate_rollup_datas_commitment(&txs, HashMap::new()); + let (mut consensus_service, _) = new_consensus_service(None, 1).await; + let commitments = generate_rollup_datas_commitment(&[], HashMap::new()); let prepare_proposal = new_prepare_proposal_request(); let prepare_proposal_response = consensus_service .handle_prepare_proposal(prepare_proposal) .await .unwrap(); - let expected_txs = std::iter::once(b"".to_vec().into()) - .chain(commitments.into_iter()) + let expected_txs = commitments + .into_iter() + .chain(std::iter::once(b"".to_vec().into())) .collect(); assert_eq!( prepare_proposal_response, @@ -476,50 +472,18 @@ mod tests { #[tokio::test] async fn process_proposal_ok_empty_block() { - let (mut consensus_service, _) = new_consensus_service(None).await; - let txs = vec![]; - let commitments = generate_rollup_datas_commitment(&txs, HashMap::new()); - let process_proposal = new_process_proposal_request(commitments.into_iter().collect()); + let (mut consensus_service, _) = new_consensus_service(None, 1).await; + let process_proposal = new_process_proposal_request(vec![]); consensus_service .handle_process_proposal(process_proposal) .await .unwrap(); } - /// Returns a default tendermint block header for test purposes. - fn default_header() -> tendermint::block::Header { - use tendermint::{ - account, - block::{ - header::Version, - Height, - }, - chain, - hash::AppHash, - }; - - tendermint::block::Header { - version: Version { - block: 0, - app: 0, - }, - chain_id: chain::Id::try_from("test").unwrap(), - height: Height::from(1u32), - time: Time::now(), - last_block_id: None, - last_commit_hash: None, - data_hash: None, - validators_hash: Hash::Sha256([0; 32]), - next_validators_hash: Hash::Sha256([0; 32]), - consensus_hash: Hash::Sha256([0; 32]), - app_hash: AppHash::try_from([0; 32].to_vec()).unwrap(), - last_results_hash: None, - evidence_hash: None, - proposer_address: account::Id::try_from([0u8; 20].to_vec()).unwrap(), - } - } - - async fn new_consensus_service(funded_key: Option) -> (Consensus, Mempool) { + async fn new_consensus_service( + funded_key: Option, + vote_extensions_enable_height: u64, + ) -> (Consensus, Mempool) { let accounts = if let Some(funded_key) = funded_key { vec![astria_core::generated::protocol::genesis::v1::Account { address: Some( @@ -556,7 +520,7 @@ mod tests { genesis_state, vec![], "test".to_string(), - 1, + vote_extensions_enable_height, ) .await .unwrap(); @@ -568,31 +532,24 @@ mod tests { #[tokio::test] async fn block_lifecycle() { - use sha2::Digest as _; - let signing_key = SigningKey::new(OsRng); let (mut consensus_service, mempool) = - new_consensus_service(Some(signing_key.verification_key())).await; + new_consensus_service(Some(signing_key.verification_key()), 1).await; let tx = make_unsigned_tx(); - let signed_tx = Arc::new(tx.sign(&signing_key)); - let tx_bytes = signed_tx.to_raw().encode_to_vec(); - let txs = vec![tx_bytes.clone().into()]; - let commitments = - generate_rollup_datas_commitment(&vec![(*signed_tx).clone()], HashMap::new()); - - let block_data: Vec = commitments.into_iter().chain(txs.clone()).collect(); - let data_hash = - merkle::Tree::from_leaves(block_data.iter().map(sha2::Sha256::digest)).root(); - let mut header = default_header(); - header.data_hash = Some(Hash::try_from(data_hash.to_vec()).unwrap()); + let signed_tx = tx.sign(&signing_key); mempool - .insert(signed_tx, 0, mock_balances(0, 0), mock_tx_cost(0, 0, 0)) + .insert( + Arc::new(signed_tx.clone()), + 0, + mock_balances(0, 0), + mock_tx_cost(0, 0, 0), + ) .await .unwrap(); - let process_proposal = new_process_proposal_request(block_data.clone()); + let process_proposal = new_process_proposal_request(vec![signed_tx]); let txs = process_proposal.txs.clone(); consensus_service .handle_request(ConsensusRequest::ProcessProposal(process_proposal))