Skip to content

Commit 95305b2

Browse files
authored
fix: add extra check before prague1 to ensure PoL tx is not included (#93)
add missing check that can occur pre-prague1 moved tests from consensus module to pol.rs that should have been in pol.rs in the first place
1 parent e88484e commit 95305b2

File tree

5 files changed

+368
-114
lines changed

5 files changed

+368
-114
lines changed

src/consensus/mod.rs

Lines changed: 91 additions & 114 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,14 @@ impl Consensus<BerachainBlock> for BerachainBeaconConsensus {
140140

141141
if self.chain_spec.is_prague1_active_at_timestamp(block.header().timestamp) {
142142
self.validate_pol_transaction(block)?;
143+
} else if let Some(index) = block
144+
.body()
145+
.transactions()
146+
.position(|tx| matches!(tx, BerachainTxEnvelope::Berachain(_)))
147+
{
148+
return Err(ConsensusError::Other(format!(
149+
"PoL transaction found at position {index} before Prague1 fork activation"
150+
)));
143151
}
144152
Ok(())
145153
}
@@ -170,160 +178,129 @@ mod tests {
170178
use super::*;
171179
use crate::{
172180
chainspec::BerachainChainSpec,
173-
primitives::header::BlsPublicKey,
174-
transaction::pol::{create_pol_transaction, validate_pol_transaction},
181+
primitives::{BerachainBlockBody, BerachainHeader, header::BlsPublicKey},
182+
transaction::{BerachainTxEnvelope, pol::create_pol_transaction},
175183
};
176-
use alloy_primitives::U256;
177-
use reth_chainspec::EthChainSpec;
184+
use alloy_consensus::{EMPTY_OMMER_ROOT_HASH, Signed, TxLegacy, constants::EMPTY_WITHDRAWALS};
185+
use alloy_eips::eip4895::Withdrawals;
186+
use alloy_primitives::{Address, BlockHash, TxKind, U256};
187+
use reth_primitives_traits::{BlockBody, SealedBlock, SealedHeader};
178188
use std::sync::Arc;
179189

180190
fn mock_berachain_chainspec() -> Arc<BerachainChainSpec> {
181-
Arc::new(BerachainChainSpec::default())
191+
crate::test::bepolia_chainspec()
182192
}
183193

184194
fn mock_bls_pubkey() -> BlsPublicKey {
185195
BlsPublicKey::from([1u8; 48])
186196
}
187197

188198
#[test]
189-
fn test_consensus_creation() {
190-
let chain_spec = mock_berachain_chainspec();
191-
let consensus = BerachainBeaconConsensus::new(chain_spec);
192-
193-
assert_eq!(consensus.chain_spec.chain_id(), 1);
194-
}
195-
196-
#[test]
197-
fn test_pol_transaction_creation_and_validation() {
199+
fn test_pre_prague1_pol_transaction_rejected() {
198200
let chain_spec = mock_berachain_chainspec();
201+
let consensus = BerachainBeaconConsensus::new(chain_spec.clone());
199202
let pubkey = mock_bls_pubkey();
200203
let block_number = U256::from(10);
201204
let base_fee = 1000u64;
202205

203-
let pol_tx_envelope =
204-
create_pol_transaction(chain_spec.clone(), pubkey, block_number, base_fee);
205-
206-
assert!(pol_tx_envelope.is_ok(), "PoL transaction creation should succeed");
207-
208-
let pol_tx = match pol_tx_envelope.unwrap() {
209-
crate::transaction::BerachainTxEnvelope::Berachain(sealed_tx) => sealed_tx,
210-
_ => panic!("Expected PoL transaction"),
211-
};
212-
213-
let validation_result =
214-
validate_pol_transaction(&pol_tx, chain_spec.clone(), pubkey, block_number, base_fee);
215-
216-
assert!(validation_result.is_ok(), "Valid PoL transaction should pass validation");
217-
}
218-
219-
#[test]
220-
fn test_pol_transaction_validation_wrong_pubkey() {
221-
let chain_spec = mock_berachain_chainspec();
222-
let correct_pubkey = mock_bls_pubkey();
223-
let wrong_pubkey = BlsPublicKey::from([2u8; 48]);
224-
let block_number = U256::from(10);
225-
let base_fee = 1000u64;
226-
227-
let pol_tx_envelope =
228-
create_pol_transaction(chain_spec.clone(), correct_pubkey, block_number, base_fee)
229-
.unwrap();
230-
231-
let pol_tx = match pol_tx_envelope {
232-
crate::transaction::BerachainTxEnvelope::Berachain(sealed_tx) => sealed_tx,
233-
_ => panic!("Expected PoL transaction"),
234-
};
235-
236-
let validation_result =
237-
validate_pol_transaction(&pol_tx, chain_spec, wrong_pubkey, block_number, base_fee);
238-
206+
// Verify Prague1 activation timestamp for context
239207
assert!(
240-
validation_result.is_err(),
241-
"PoL transaction with wrong pubkey should fail validation"
208+
!chain_spec.is_prague1_active_at_timestamp(0),
209+
"Timestamp 0 should be before Prague1 activation"
242210
);
243-
assert!(validation_result.unwrap_err().to_string().contains("hash mismatch"));
244-
}
245-
246-
#[test]
247-
fn test_pol_transaction_validation_wrong_base_fee() {
248-
let chain_spec = mock_berachain_chainspec();
249-
let pubkey = mock_bls_pubkey();
250-
let block_number = U256::from(10);
251-
let correct_base_fee = 1000u64;
252-
let wrong_base_fee = 2000u64;
253211

212+
// Create a PoL transaction
254213
let pol_tx_envelope =
255-
create_pol_transaction(chain_spec.clone(), pubkey, block_number, correct_base_fee)
256-
.unwrap();
214+
create_pol_transaction(chain_spec, pubkey, block_number, base_fee).unwrap();
215+
216+
// Create a block body with the PoL transaction
217+
let transactions = vec![pol_tx_envelope];
218+
let block_body = BerachainBlockBody {
219+
transactions: transactions.clone(),
220+
withdrawals: Some(Withdrawals::default()),
221+
..Default::default()
222+
};
257223

258-
let pol_tx = match pol_tx_envelope {
259-
crate::transaction::BerachainTxEnvelope::Berachain(sealed_tx) => sealed_tx,
260-
_ => panic!("Expected PoL transaction"),
224+
// Create a header with timestamp BEFORE Prague1 activation
225+
let header = BerachainHeader {
226+
number: block_number.to::<u64>(),
227+
timestamp: 0, // Pre-Prague1 timestamp (Prague1 activates at 1754496000)
228+
base_fee_per_gas: Some(base_fee),
229+
ommers_hash: EMPTY_OMMER_ROOT_HASH,
230+
transactions_root: block_body.calculate_tx_root(),
231+
withdrawals_root: Some(EMPTY_WITHDRAWALS),
232+
blob_gas_used: Some(0),
233+
..Default::default()
261234
};
262235

263-
let validation_result =
264-
validate_pol_transaction(&pol_tx, chain_spec, pubkey, block_number, wrong_base_fee);
236+
let sealed_header = SealedHeader::new(header, BlockHash::ZERO);
237+
let block = SealedBlock::from_sealed_parts(sealed_header, block_body);
238+
239+
// Validation should fail because PoL transaction exists before Prague1
240+
let result = consensus.validate_block_pre_execution(&block);
241+
assert!(result.is_err(), "Pre-Prague1 block with PoL transaction should fail validation");
265242

243+
let error_msg = result.unwrap_err().to_string();
266244
assert!(
267-
validation_result.is_err(),
268-
"PoL transaction with wrong base fee should fail validation"
245+
error_msg.contains("before Prague1 fork activation"),
246+
"Error should mention Prague1 fork activation"
269247
);
270-
assert!(validation_result.unwrap_err().to_string().contains("hash mismatch"));
248+
assert!(error_msg.contains("position 0"), "Error should indicate PoL transaction position");
271249
}
272250

273251
#[test]
274-
fn test_pol_transaction_validation_wrong_block_number() {
252+
fn test_pre_prague1_normal_transactions_accepted() {
275253
let chain_spec = mock_berachain_chainspec();
276-
let pubkey = mock_bls_pubkey();
277-
let correct_block_number = U256::from(10);
278-
let wrong_block_number = U256::from(20);
279-
let base_fee = 1000u64;
280-
281-
let pol_tx_envelope =
282-
create_pol_transaction(chain_spec.clone(), pubkey, correct_block_number, base_fee)
283-
.unwrap();
284-
285-
let pol_tx = match pol_tx_envelope {
286-
crate::transaction::BerachainTxEnvelope::Berachain(sealed_tx) => sealed_tx,
287-
_ => panic!("Expected PoL transaction"),
288-
};
289-
290-
let validation_result =
291-
validate_pol_transaction(&pol_tx, chain_spec, pubkey, wrong_block_number, base_fee);
254+
let consensus = BerachainBeaconConsensus::new(chain_spec.clone());
292255

256+
// Verify Prague1 activation timestamp for context
293257
assert!(
294-
validation_result.is_err(),
295-
"PoL transaction with wrong block number should fail validation"
258+
!chain_spec.is_prague1_active_at_timestamp(0),
259+
"Timestamp 0 should be before Prague1 activation"
296260
);
297-
assert!(validation_result.unwrap_err().to_string().contains("hash mismatch"));
298-
}
299-
300-
#[test]
301-
fn test_pol_transaction_deterministic_hashes() {
302-
let chain_spec = mock_berachain_chainspec();
303-
let pubkey = mock_bls_pubkey();
304-
let block_number = U256::from(42);
305-
let base_fee = 1337u64;
306261

307-
let pol_tx1_envelope =
308-
create_pol_transaction(chain_spec.clone(), pubkey, block_number, base_fee).unwrap();
262+
// Create normal Ethereum transaction
263+
let tx = TxLegacy {
264+
chain_id: Some(1),
265+
nonce: 0,
266+
gas_price: 1000,
267+
gas_limit: 21000,
268+
to: TxKind::Call(Address::ZERO),
269+
value: U256::ZERO,
270+
input: Default::default(),
271+
};
309272

310-
let pol_tx2_envelope =
311-
create_pol_transaction(chain_spec, pubkey, block_number, base_fee).unwrap();
273+
let signature = alloy_primitives::Signature::test_signature();
274+
let signed_tx = Signed::new_unhashed(tx, signature);
275+
let eth_tx_envelope =
276+
BerachainTxEnvelope::Ethereum(alloy_consensus::TxEnvelope::Legacy(signed_tx));
312277

313-
let pol_tx1 = match pol_tx1_envelope {
314-
crate::transaction::BerachainTxEnvelope::Berachain(sealed_tx) => sealed_tx,
315-
_ => panic!("Expected PoL transaction"),
278+
let transactions = vec![eth_tx_envelope];
279+
let block_body = BerachainBlockBody {
280+
transactions: transactions.clone(),
281+
withdrawals: Some(Withdrawals::default()),
282+
..Default::default()
316283
};
317284

318-
let pol_tx2 = match pol_tx2_envelope {
319-
crate::transaction::BerachainTxEnvelope::Berachain(sealed_tx) => sealed_tx,
320-
_ => panic!("Expected PoL transaction"),
285+
let header = BerachainHeader {
286+
number: 10,
287+
timestamp: 0, // Pre-Prague1 timestamp
288+
base_fee_per_gas: Some(1000),
289+
ommers_hash: EMPTY_OMMER_ROOT_HASH,
290+
transactions_root: block_body.calculate_tx_root(),
291+
withdrawals_root: Some(EMPTY_WITHDRAWALS),
292+
blob_gas_used: Some(0),
293+
..Default::default()
321294
};
322295

323-
assert_eq!(
324-
pol_tx1.hash(),
325-
pol_tx2.hash(),
326-
"Identical PoL transactions should have identical hashes"
296+
let sealed_header = SealedHeader::new(header, BlockHash::ZERO);
297+
let block = SealedBlock::from_sealed_parts(sealed_header, block_body);
298+
299+
// Validation should succeed for normal transactions pre-Prague1
300+
let result = consensus.validate_block_pre_execution(&block);
301+
assert!(
302+
result.is_ok(),
303+
"Pre-Prague1 block with normal transactions should pass validation"
327304
);
328305
}
329306
}

src/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,3 +12,6 @@ pub mod pool;
1212
pub mod primitives;
1313
pub mod rpc;
1414
pub mod transaction;
15+
16+
#[cfg(test)]
17+
pub mod test;

0 commit comments

Comments
 (0)