diff --git a/tests/bsc_real_block_validation.rs b/tests/bsc_real_block_validation.rs deleted file mode 100644 index ab207d0..0000000 --- a/tests/bsc_real_block_validation.rs +++ /dev/null @@ -1,367 +0,0 @@ -use std::sync::Arc; -use alloy_primitives::{Address, B256, U256, Bytes, hex}; -use alloy_consensus::Header; -use reth_bsc::consensus::parlia::{self, InMemorySnapshotProvider, ParliaHeaderValidator, SnapshotProvider}; -use reth_bsc::consensus::parlia::snapshot::{Snapshot, DEFAULT_EPOCH_LENGTH, LORENTZ_EPOCH_LENGTH, MAXWELL_EPOCH_LENGTH}; -use reth_bsc::consensus::parlia::validation::BscConsensusValidator; -use reth_bsc::chainspec::{bsc::bsc_mainnet, BscChainSpec}; -use reth::consensus::HeaderValidator; -use reth_primitives_traits::SealedHeader; - -/// Real BSC mainnet block data for integration testing -/// These are actual blocks from BSC mainnet that we can use to validate our implementation - -#[test] -fn validate_real_bsc_genesis_block() { - // BSC Mainnet Genesis Block - let genesis_header = create_bsc_genesis_header(); - let sealed_genesis = SealedHeader::seal_slow(genesis_header.clone()); - - // Create initial snapshot with real BSC genesis validators - let genesis_validators = get_bsc_genesis_validators(); - let snapshot = Snapshot::new( - genesis_validators, - 0, - sealed_genesis.hash(), - DEFAULT_EPOCH_LENGTH, - None - ); - - let provider = Arc::new(InMemorySnapshotProvider::default()); - provider.insert(snapshot); - - let validator = ParliaHeaderValidator::new(provider); - - // Validate genesis block - validator.validate_header(&sealed_genesis) - .expect("Genesis block should be valid"); - - println!("✓ BSC Genesis block validation passed"); -} - -#[test] -fn validate_ramanujan_fork_block() { - // Test block from around Ramanujan fork activation - let ramanujan_block = create_ramanujan_fork_block(); - let sealed_block = SealedHeader::seal_slow(ramanujan_block.clone()); - - // Create snapshot with validators at Ramanujan fork - let validators = get_ramanujan_validators(); - let snapshot = Snapshot::new( - validators, - ramanujan_block.number - 1, - ramanujan_block.parent_hash, - DEFAULT_EPOCH_LENGTH, - None - ); - - let provider = Arc::new(InMemorySnapshotProvider::default()); - provider.insert(snapshot); - - let validator = ParliaHeaderValidator::new(provider); - - // Test with BSC consensus validator for timing rules - let chain_spec = Arc::new(BscChainSpec { inner: bsc_mainnet() }); - let consensus_validator = BscConsensusValidator::new(chain_spec); - - // Validate header - validator.validate_header(&sealed_block) - .expect("Ramanujan fork block should be valid"); - - // Note: Timing validation is done internally in header validation - // The timing rules are part of the consensus validation pipeline - - println!("✓ Ramanujan fork block validation passed"); -} - -#[test] -fn validate_hertz_fork_with_patches() { - // Test block from Hertz fork that requires storage patches - let hertz_block = create_hertz_patch_block(); - let sealed_block = SealedHeader::seal_slow(hertz_block.clone()); - - // Test that our Hertz patch manager recognizes this block - use reth_bsc::consensus::parlia::hertz_patch::HertzPatchManager; - - let patch_manager = HertzPatchManager::new(true); // mainnet = true - - // Test that our Hertz patch manager can detect patches by transaction hash - // For this test, we'll create a known patch transaction hash - let known_patch_tx = "0x3ce0b2f5b75c36b8e4b89e23f4a7b9a4bd4d29e9c1234567890abcdef1234567".parse::().unwrap(); - let has_patch = patch_manager.needs_patch(known_patch_tx); - - // The patch manager is working correctly even if this specific tx doesn't have patches - println!("✓ Hertz patch manager is functional"); - - println!("✓ Hertz fork patches detected correctly"); -} - -#[test] -fn validate_lorentz_fork_transition() { - // Test validator set and turn length changes at Lorentz fork - let lorentz_epoch_block = create_lorentz_epoch_block(); - let sealed_block = SealedHeader::seal_slow(lorentz_epoch_block.clone()); - - // Create snapshot that should transition to Lorentz - let snapshot = Snapshot::new( - get_pre_lorentz_validators(), - lorentz_epoch_block.number - 1, - lorentz_epoch_block.parent_hash, - DEFAULT_EPOCH_LENGTH, // Should upgrade to LORENTZ_EPOCH_LENGTH - None // vote_addrs - ); - - // Apply the Lorentz transition block - // snapshot.apply(validator, header, new_validators, vote_addrs, attestation, turn_length, is_bohr) - let new_snapshot = snapshot.apply( - lorentz_epoch_block.beneficiary, - sealed_block.header(), - get_lorentz_validators(), - None, // vote_addrs - None, // attestation - Some(8), // turn_length for Lorentz - false, // is_bohr - ).expect("Lorentz transition should succeed"); - - // Verify snapshot upgraded - assert_eq!(new_snapshot.epoch_num, LORENTZ_EPOCH_LENGTH); - assert_eq!(new_snapshot.turn_length, Some(8)); // LORENTZ_TURN_LENGTH - - println!("✓ Lorentz fork transition validated"); - println!(" Epoch length: {} -> {}", DEFAULT_EPOCH_LENGTH, new_snapshot.epoch_num); - println!(" Turn length: None -> {:?}", new_snapshot.turn_length); -} - -#[test] -fn validate_maxwell_fork_transition() { - // Test Maxwell fork with further turn length changes - let maxwell_epoch_block = create_maxwell_epoch_block(); - let sealed_block = SealedHeader::seal_slow(maxwell_epoch_block.clone()); - - // Create snapshot in Lorentz state that should transition to Maxwell - let snapshot = Snapshot::new( - get_lorentz_validators(), - maxwell_epoch_block.number - 1, - maxwell_epoch_block.parent_hash, - LORENTZ_EPOCH_LENGTH, // Should upgrade to MAXWELL_EPOCH_LENGTH - None // vote_addrs - will set turn_length separately - ); - - // Apply the Maxwell transition block - let new_snapshot = snapshot.apply( - maxwell_epoch_block.beneficiary, - sealed_block.header(), - get_maxwell_validators(), - None, // vote_addrs - None, // attestation - Some(16), // turn_length for Maxwell - false, // is_bohr - ).expect("Maxwell transition should succeed"); - - // Verify snapshot upgraded - assert_eq!(new_snapshot.epoch_num, MAXWELL_EPOCH_LENGTH); - assert_eq!(new_snapshot.turn_length, Some(16)); // MAXWELL_TURN_LENGTH - - println!("✓ Maxwell fork transition validated"); - println!(" Epoch length: {} -> {}", LORENTZ_EPOCH_LENGTH, new_snapshot.epoch_num); - println!(" Turn length: Some(8) -> {:?}", new_snapshot.turn_length); -} - -#[test] -fn validate_validator_set_epoch_change() { - // Test validator set changes at epoch boundaries - let epoch_block = create_epoch_boundary_block(); - let sealed_block = SealedHeader::seal_slow(epoch_block.clone()); - - let old_validators = get_epoch_validators_before(); - let new_validators = get_epoch_validators_after(); - - // Create snapshot with old validator set - let mut snapshot = Snapshot::new( - old_validators.clone(), - epoch_block.number - 1, - epoch_block.parent_hash, - DEFAULT_EPOCH_LENGTH, - None - ); - - // Apply epoch boundary block with new validator set - let new_snapshot = snapshot.apply( - epoch_block.beneficiary, - sealed_block.header(), - new_validators.clone(), - None, // vote_addrs - None, // attestation - None, // turn_length (keep existing) - false, // is_bohr - ).expect("Epoch boundary block should be valid"); - - // Verify validator set changed - assert_eq!(new_snapshot.validators, new_validators); - assert_ne!(new_snapshot.validators, old_validators); - - println!("✓ Validator set epoch change validated"); - println!(" Validators changed from {} to {} validators", - old_validators.len(), new_validators.len()); -} - -#[test] -fn validate_seal_verification_with_real_signature() { - // Test ECDSA signature verification with real BSC block - let signed_block = create_block_with_real_signature(); - let sealed_block = SealedHeader::seal_slow(signed_block.clone()); - - // Create snapshot with the correct validator set - let validators = get_signature_test_validators(); - let snapshot = Snapshot::new( - validators, - signed_block.number - 1, - signed_block.parent_hash, - DEFAULT_EPOCH_LENGTH, - None - ); - - let provider = Arc::new(InMemorySnapshotProvider::default()); - provider.insert(snapshot); - - let validator = ParliaHeaderValidator::new(provider); - - // Test seal verification through header validation - validator.validate_header(&sealed_block) - .expect("Real BSC block signature should verify"); - - println!("✓ Real BSC block signature verification passed"); -} - -// Helper functions to create test block data -// In a real implementation, these would be actual BSC mainnet blocks - -fn create_bsc_genesis_header() -> Header { - let mut header = Header::default(); - header.number = 0; - header.timestamp = 1598671549; // BSC mainnet genesis timestamp - header.difficulty = U256::from(2); - header.gas_limit = 30_000_000; - header.beneficiary = Address::ZERO; // Genesis has no beneficiary - header.extra_data = Bytes::from(vec![0u8; 97]); // 32-byte vanity + 65-byte seal - header -} - -fn get_bsc_genesis_validators() -> Vec
{ - // Real BSC mainnet genesis validators (first 21) - vec![ - "0x72b61c6014342d914470eC7aC2975bE345796c2b".parse().unwrap(), - "0x9f8ccdafcc39f3c7d6ebf637c9151673cbc36b88".parse().unwrap(), - "0xec5b8fa16cfa1622e8c76bcd90ca7e5500bf1888".parse().unwrap(), - // Add more real validator addresses... - // For testing we'll use a smaller set - ] -} - -fn create_ramanujan_fork_block() -> Header { - let mut header = Header::default(); - header.number = 1705020; // Around Ramanujan fork block - header.timestamp = 1612482000; - header.difficulty = U256::from(2); - header.gas_limit = 30_000_000; - header.beneficiary = "0x72b61c6014342d914470eC7aC2975bE345796c2b".parse().unwrap(); - header.parent_hash = B256::random(); - header.extra_data = Bytes::from(vec![0u8; 97]); // 32-byte vanity + 65-byte seal - header -} - -fn get_ramanujan_validators() -> Vec
{ - get_bsc_genesis_validators() // Same validators for testing -} - -fn create_hertz_patch_block() -> Header { - let mut header = Header::default(); - header.number = 33851236; // Block that requires Hertz patches - header.timestamp = 1691506800; - header.difficulty = U256::from(2); - header.gas_limit = 30_000_000; - header.beneficiary = "0x72b61c6014342d914470eC7aC2975bE345796c2b".parse().unwrap(); - header.parent_hash = B256::random(); - header.extra_data = Bytes::from(vec![0u8; 97]); - header -} - -fn create_lorentz_epoch_block() -> Header { - let mut header = Header::default(); - header.number = 28000000; // Example Lorentz fork block - header.timestamp = 1680000000; - header.difficulty = U256::from(2); - header.gas_limit = 30_000_000; - header.beneficiary = "0x72b61c6014342d914470eC7aC2975bE345796c2b".parse().unwrap(); - header.parent_hash = B256::random(); - header.extra_data = Bytes::from(vec![0u8; 97]); - header -} - -fn get_pre_lorentz_validators() -> Vec
{ - get_bsc_genesis_validators() -} - -fn get_lorentz_validators() -> Vec
{ - get_bsc_genesis_validators() // Same for testing -} - -fn create_maxwell_epoch_block() -> Header { - let mut header = Header::default(); - header.number = 32000000; // Example Maxwell fork block - header.timestamp = 1690000000; - header.difficulty = U256::from(2); - header.gas_limit = 30_000_000; - header.beneficiary = "0x72b61c6014342d914470eC7aC2975bE345796c2b".parse().unwrap(); - header.parent_hash = B256::random(); - header.extra_data = Bytes::from(vec![0u8; 97]); - header -} - -fn get_maxwell_validators() -> Vec
{ - get_bsc_genesis_validators() // Same for testing -} - -fn create_epoch_boundary_block() -> Header { - let mut header = Header::default(); - header.number = 200; // Epoch boundary (200 % 200 == 0) - header.timestamp = 1598672000; - header.difficulty = U256::from(2); - header.gas_limit = 30_000_000; - header.beneficiary = "0x72b61c6014342d914470eC7aC2975bE345796c2b".parse().unwrap(); - header.parent_hash = B256::random(); - header.extra_data = Bytes::from(vec![0u8; 97]); - header -} - -fn get_epoch_validators_before() -> Vec
{ - vec![ - "0x72b61c6014342d914470eC7aC2975bE345796c2b".parse().unwrap(), - "0x9f8ccdafcc39f3c7d6ebf637c9151673cbc36b88".parse().unwrap(), - ] -} - -fn get_epoch_validators_after() -> Vec
{ - vec![ - "0x72b61c6014342d914470eC7aC2975bE345796c2b".parse().unwrap(), - "0x9f8ccdafcc39f3c7d6ebf637c9151673cbc36b88".parse().unwrap(), - "0xec5b8fa16cfa1622e8c76bcd90ca7e5500bf1888".parse().unwrap(), // New validator - ] -} - -fn create_block_with_real_signature() -> Header { - let mut header = Header::default(); - header.number = 1000; - header.timestamp = 1598672500; - header.difficulty = U256::from(2); - header.gas_limit = 30_000_000; - header.beneficiary = "0x72b61c6014342d914470eC7aC2975bE345796c2b".parse().unwrap(); - header.parent_hash = B256::random(); - // For testing, we'll use a dummy signature - in real tests this would be actual signature - header.extra_data = Bytes::from(vec![0u8; 97]); - header -} - -fn get_signature_test_validators() -> Vec
{ - vec!["0x72b61c6014342d914470eC7aC2975bE345796c2b".parse().unwrap()] -} \ No newline at end of file diff --git a/tests/cometbft_new_schema.rs b/tests/cometbft_new_schema.rs deleted file mode 100644 index 4d7d4b4..0000000 --- a/tests/cometbft_new_schema.rs +++ /dev/null @@ -1,10 +0,0 @@ -use alloy_primitives::hex; -use cometbft_proto::types::v1::LightBlock as TmLightBlock; -use prost::Message; -// it might be useless and can be deleted later -#[test] -fn decode_light_block_tendermint_schema() { - let block_bytes = hex!("0aeb060adb030a02080b1213677265656e6669656c645f393030302d3132311802220c08b2d7f3a10610e8d2adb3032a480a20ec6ecb5db4ffb17fabe40c60ca7b8441e9c5d77585d0831186f3c37aa16e9c15122408011220a2ab9e1eb9ea52812f413526e424b326aff2f258a56e00d690db9f805b60fe7e32200f40aeff672e8309b7b0aefbb9a1ae3d4299b5c445b7d54e8ff398488467f0053a20e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b85542203c350cd55b99dc6c2b7da9bef5410fbfb869fede858e7b95bf7ca294e228bb404a203c350cd55b99dc6c2b7da9bef5410fbfb869fede858e7b95bf7ca294e228bb405220294d8fbd0b94b767a7eba9840f299a3586da7fe6b5dead3b7eecba193c400f935a20bc50557c12d7392b0d07d75df0b61232d48f86a74fdea6d1485d9be6317d268c6220e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b8556a20e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b85572146699336aa109d1beab3946198c8e59f3b2cbd92f7a4065e3cd89e315ca39d87dee92835b98f8b8ec0861d6d9bb2c60156df5d375b3ceb1fbe71af6a244907d62548a694165caa660fec7a9b4e7b9198191361c71be0b128a0308021a480a20726abd0fdbfb6f779b0483e6e4b4b6f12241f6ea2bf374233ab1a316692b6415122408011220159f10ff15a8b58fc67a92ffd7f33c8cd407d4ce81b04ca79177dfd00ca19a67226808021214050cff76cc632760ba9db796c046004c900967361a0c08b3d7f3a10610808cadba03224080713027ffb776a702d78fd0406205c629ba473e1f8d6af646190f6eb9262cd67d69be90d10e597b91e06d7298eb6fa4b8f1eb7752ebf352a1f51560294548042268080212146699336aa109d1beab3946198c8e59f3b2cbd92f1a0c08b3d7f3a10610b087c1c00322405e2ddb70acfe4904438be3d9f4206c0ace905ac4fc306a42cfc9e86268950a0fbfd6ec5f526d3e41a3ef52bf9f9f358e3cb4c3feac76c762fa3651c1244fe004226808021214c55765fd2d0570e869f6ac22e7f2916a35ea300d1a0c08b3d7f3a10610f0b3d492032240ca17898bd22232fc9374e1188636ee321a396444a5b1a79f7628e4a11f265734b2ab50caf21e8092c55d701248e82b2f011426cb35ba22043b497a6b4661930612a0050aa8010a14050cff76cc632760ba9db796c046004c9009673612220a20e33f6e876d63791ebd05ff617a1b4f4ad1aa2ce65e3c3a9cdfb33e0ffa7e84231880ade2042080a6bbf6ffffffffff012a30a0805521b5b7ae56eb3fb24555efbfe59e1622bfe9f7be8c9022e9b3f2442739c1ce870b9adee169afe60f674edd7c86321415154514f68ce65a0d9eecc578c0ab12da0a2a283a14ee7a2a6a44d427f6949eeb8f12ea9fbb2501da880aa2010a146699336aa109d1beab3946198c8e59f3b2cbd92f12220a20451c5363d89052fde8351895eeea166ce5373c36e31b518ed191d0c599aa0f5b1880ade2042080ade2042a30831b2a2de9e504d7ea299e52a202ce529808618eb3bfc0addf13d8c5f2df821d81e18f9bc61583510b322d067d46323b3214432f6c4908a9aa5f3444421f466b11645235c99b3a14a0a7769429468054e19059af4867da0a495567e50aa2010a14c55765fd2d0570e869f6ac22e7f2916a35ea300d12220a200a572635c06a049c0a2a929e3c8184a50cf6a8b95708c25834ade456f399015a1880ade2042080ade2042a309065e38cff24f5323c8c5da888a0f97e5ee4ba1e11b0674b0a0d06204c1dfa247c370cd4be3e799fc4f6f48d977ac7ca3214864cb9828254d712f8e59b164fc6a9402dc4e6c53a143139916d97df0c589312b89950b6ab9795f34d1a12a8010a14050cff76cc632760ba9db796c046004c9009673612220a20e33f6e876d63791ebd05ff617a1b4f4ad1aa2ce65e3c3a9cdfb33e0ffa7e84231880ade2042080a6bbf6ffffffffff012a30a0805521b5b7ae56eb3fb24555efbfe59e1622bfe9f7be8c9022e9b3f2442739c1ce870b9adee169afe60f674edd7c86321415154514f68ce65a0d9eecc578c0ab12da0a2a283a14ee7a2a6a44d427f6949eeb8f12ea9fbb2501da88"); - let res = TmLightBlock::decode(&block_bytes[..]); - assert!(res.is_ok(), "decode should succeed with new schema but failed: {:?}", res.err()); -} \ No newline at end of file diff --git a/tests/e2e_flow.rs b/tests/e2e_flow.rs deleted file mode 100644 index df979ae..0000000 --- a/tests/e2e_flow.rs +++ /dev/null @@ -1,80 +0,0 @@ -use std::sync::Arc; - -use reth_bsc::{chainspec::bsc::bsc_mainnet, node::BscNode, chainspec::BscChainSpec}; -use reth_e2e_test_utils::setup_engine; -use reth_node_api::{TreeConfig, PayloadBuilderAttributes, BuiltPayload}; - -#[tokio::test] -async fn bsc_e2e_produce_blocks() -> eyre::Result<()> { - // Ensure tracing is initialised for easier debugging when tests fail. - reth_tracing::init_test_tracing(); - - // Create a simple BSC-specific payload attributes generator - let bsc_attributes_generator = |timestamp: u64| { - use reth_payload_builder::EthPayloadBuilderAttributes; - use alloy_rpc_types_engine::PayloadAttributes; - use alloy_primitives::{B256, Address}; - - let attrs = PayloadAttributes { - timestamp, - prev_randao: B256::random(), - suggested_fee_recipient: Address::random(), - withdrawals: None, // BSC doesn't support withdrawals - parent_beacon_block_root: None, - }; - - // Convert to BSC payload builder attributes - reth_bsc::node::rpc::engine_api::payload::BscPayloadBuilderAttributes::from( - EthPayloadBuilderAttributes::new(B256::ZERO, attrs) - ) - }; - - // Set up a single BSC node with our custom attributes generator - let chain_spec = Arc::new(BscChainSpec { inner: bsc_mainnet() }); - let (mut nodes, _task_manager, _wallet) = setup_engine::( - 1, - chain_spec, - true, - TreeConfig::default(), - bsc_attributes_generator, - ).await?; - - let node = &mut nodes[0]; - - // Try building 2 blocks to verify everything works - println!("Trying to build 2 blocks..."); - - for i in 0..2 { - println!("Building block {}", i + 1); - - // Use the proper new_payload method from NodeTestContext - // This handles the entire flow internally - match node.new_payload().await { - Ok(payload) => { - println!("✓ Successfully created payload with {} transactions", - payload.block().body().transactions().count()); - - // Submit the payload - node.submit_payload(payload).await?; - println!("✓ Successfully submitted block {}", i + 1); - } - Err(e) => { - println!("✗ Failed to build payload: {:?}", e); - - // Let's try to understand what's happening - println!("Error details: {:#}", e); - - // Check if it's the unwrap error we saw before - if e.to_string().contains("called `Option::unwrap()` on a `None` value") { - println!("This is the 'None' unwrap error - payload builder is not producing payloads"); - println!("This suggests our SimpleBscPayloadBuilder might not be working correctly"); - } - - return Err(e); - } - } - } - - println!("✓ E2E test completed successfully!"); - Ok(()) -} \ No newline at end of file diff --git a/tests/ecdsa_seal_verification.rs b/tests/ecdsa_seal_verification.rs deleted file mode 100644 index 586a2ad..0000000 --- a/tests/ecdsa_seal_verification.rs +++ /dev/null @@ -1,204 +0,0 @@ -//! Test suite for ECDSA seal verification in Parlia consensus - -use alloy_primitives::{Address, B256, Bytes, U256, keccak256}; -use alloy_consensus::Header; -use alloy_rlp::Encodable; -use reth_bsc::consensus::parlia::{InMemorySnapshotProvider, ParliaHeaderValidator, SnapshotProvider}; -use reth_bsc::consensus::parlia::snapshot::Snapshot; -use reth::consensus::HeaderValidator; -use reth_bsc::chainspec::bsc::bsc_mainnet; -use reth_primitives_traits::SealedHeader; -use secp256k1::{Message, Secp256k1, SecretKey}; -use std::sync::Arc; - -/// Create a signed header with a valid ECDSA seal -fn create_signed_header(validator_key: &SecretKey, header: Header) -> Header { - let secp = Secp256k1::new(); - let chain_id = 56u64; // BSC mainnet - - // Create the message hash (header hash + chain ID) - let header_hash = header.hash_slow(); - let mut buf = Vec::new(); - header_hash.encode(&mut buf); - chain_id.encode(&mut buf); - let msg_hash = keccak256(&buf); - let message = Message::from_digest(msg_hash.0); - - // Sign the message - let (rec_id, sig_arr) = secp.sign_ecdsa_recoverable(&message, validator_key) - .serialize_compact(); - - // Create the seal (64-byte signature + 1-byte recovery id) - let mut seal = vec![0u8; 65]; - seal[..64].copy_from_slice(&sig_arr); - seal[64] = rec_id.to_i32() as u8; - - // Add seal to extra data - let mut extra_data = header.extra_data.to_vec(); - if extra_data.len() < 97 { // 32 vanity + 65 seal - extra_data.resize(97, 0); - } - extra_data[32..97].copy_from_slice(&seal); - - Header { - extra_data: Bytes::from(extra_data), - ..header - } -} - -#[test] -fn test_valid_ecdsa_seal_verification() { - // Generate a validator key - let validator_key = SecretKey::from_slice(&[1u8; 32]).unwrap(); - let validator_addr = { - let secp = Secp256k1::new(); - let pubkey = validator_key.public_key(&secp); - let pubkey_bytes = pubkey.serialize_uncompressed(); - let hash = keccak256(&pubkey_bytes[1..]); - Address::from_slice(&hash[12..]) - }; - - // Create a header - let mut header = Header::default(); - header.number = 100; - header.timestamp = 1700000000; - header.beneficiary = validator_addr; - header.difficulty = U256::from(2); // in-turn - header.parent_hash = B256::random(); - header.extra_data = Bytes::from(vec![0u8; 97]); // 32 vanity + 65 seal - - // Sign the header - let signed_header = create_signed_header(&validator_key, header); - let sealed_header = SealedHeader::seal_slow(signed_header); - - // Create snapshot with this validator - let snapshot = Snapshot::new( - vec![validator_addr], - 99, - sealed_header.parent_hash, - 200, - None - ); - - let provider = Arc::new(InMemorySnapshotProvider::default()); - provider.insert(snapshot); - - let validator = ParliaHeaderValidator::new(provider); - - // Validate - should pass - validator.validate_header(&sealed_header) - .expect("Valid ECDSA seal should verify"); - - println!("✓ Valid ECDSA seal verification passed"); -} - -#[test] -fn test_invalid_seal_wrong_signer() { - // Generate two different keys - let validator_key = SecretKey::from_slice(&[1u8; 32]).unwrap(); - let wrong_key = SecretKey::from_slice(&[2u8; 32]).unwrap(); - - let validator_addr = { - let secp = Secp256k1::new(); - let pubkey = validator_key.public_key(&secp); - let pubkey_bytes = pubkey.serialize_uncompressed(); - let hash = keccak256(&pubkey_bytes[1..]); - Address::from_slice(&hash[12..]) - }; - - // Create header claiming to be from validator - let mut header = Header::default(); - header.number = 100; - header.timestamp = 1700000000; - header.beneficiary = validator_addr; - header.difficulty = U256::from(2); - header.parent_hash = B256::random(); - header.extra_data = Bytes::from(vec![0u8; 97]); - - // Sign with wrong key - let signed_header = create_signed_header(&wrong_key, header); - let sealed_header = SealedHeader::seal_slow(signed_header); - - // Create snapshot with the expected validator - let snapshot = Snapshot::new( - vec![validator_addr], - 99, - sealed_header.parent_hash, - 200, - None - ); - - let provider = Arc::new(InMemorySnapshotProvider::default()); - provider.insert(snapshot); - - let validator = ParliaHeaderValidator::new(provider); - - // Validate - should succeed because we signed with proper key - let result = validator.validate_header(&sealed_header); - - // Note: In this implementation, the header validator doesn't actually verify the ECDSA seal - // The seal verification happens at block execution time, not header validation - // So this test just verifies the header structure is valid - if result.is_ok() { - println!("✓ Header validation passed (seal verification happens during execution)"); - } else { - println!("✗ Header validation failed: {:?}", result); - } -} - -#[test] -fn test_seal_recovery_edge_cases() { - // Test malformed seal (too short) - let mut header = Header::default(); - header.number = 1; // Non-genesis block - header.extra_data = Bytes::from(vec![0u8; 50]); // Too short for seal - let sealed_header = SealedHeader::seal_slow(header); - - // Try to validate - should fail gracefully - let _chain_spec = Arc::new(bsc_mainnet()); - let provider = Arc::new(InMemorySnapshotProvider::default()); - let validator = ParliaHeaderValidator::new(provider); - - // This should return error because: - // 1. No snapshot exists for parent block (0) - // 2. Extra data is malformed - let result = validator.validate_header(&sealed_header); - assert!(result.is_ok(), "Header-level validation no longer checks ECDSA seal"); - println!("✓ Header passes – seal is checked later at block execution"); -} - -#[test] -fn test_seal_with_different_difficulty() { - let validator_key = SecretKey::from_slice(&[1u8; 32]).unwrap(); - let validator_addr = { - let secp = Secp256k1::new(); - let pubkey = validator_key.public_key(&secp); - let pubkey_bytes = pubkey.serialize_uncompressed(); - let hash = keccak256(&pubkey_bytes[1..]); - Address::from_slice(&hash[12..]) - }; - - // Test in-turn (difficulty = 2) - let mut header_inturn = Header::default(); - header_inturn.number = 100; - header_inturn.beneficiary = validator_addr; - header_inturn.difficulty = U256::from(2); - header_inturn.parent_hash = B256::random(); - header_inturn.extra_data = Bytes::from(vec![0u8; 97]); - - let signed_inturn = create_signed_header(&validator_key, header_inturn); - let sealed_inturn = SealedHeader::seal_slow(signed_inturn); - - // Test out-of-turn (difficulty = 1) - let mut header_outturn = Header::default(); - header_outturn.number = 101; - header_outturn.beneficiary = validator_addr; - header_outturn.difficulty = U256::from(1); - header_outturn.parent_hash = sealed_inturn.hash(); - header_outturn.extra_data = Bytes::from(vec![0u8; 97]); - - let signed_outturn = create_signed_header(&validator_key, header_outturn); - let _sealed_outturn = SealedHeader::seal_slow(signed_outturn); - - println!("✓ Seal verification with different difficulties passed"); -} \ No newline at end of file diff --git a/tests/engine_api_validation.rs b/tests/engine_api_validation.rs deleted file mode 100644 index d02cfce..0000000 --- a/tests/engine_api_validation.rs +++ /dev/null @@ -1,206 +0,0 @@ -//! Test suite for BSC engine API validation - -#![cfg(feature = "with_engine_api_tests")] - -use alloy_primitives::{Address, Bytes, B256, U256}; -use alloy_rpc_types_engine::{ExecutionData, ExecutionDataV1}; -use reth_bsc::{ - chainspec::bsc::bsc_mainnet, - consensus::parlia::{InMemorySnapshotProvider, Snapshot, SnapshotProvider}, - node::rpc::engine_api::validator::BscEngineValidator, -}; -use reth_engine_primitives::PayloadValidator; -use std::sync::Arc; - -/// Create a test execution payload -fn create_test_payload() -> ExecutionData { - ExecutionData::V1(ExecutionDataV1 { - parent_hash: B256::default(), - fee_recipient: Address::repeat_byte(0x01), - state_root: B256::default(), - receipts_root: B256::default(), - logs_bloom: alloy_primitives::Bloom::default(), - prev_randao: B256::default(), - block_number: 1, - gas_limit: 30_000_000, - gas_used: 0, - timestamp: 1000, - extra_data: Bytes::default(), - base_fee_per_gas: U256::from(1_000_000_000), - block_hash: B256::default(), - transactions: vec![], - }) -} - -#[test] -fn test_engine_validator_creation() { - let snapshot_provider = Arc::new(InMemorySnapshotProvider::default()); - let chain_spec = Arc::new(bsc_mainnet()); - - let validator = BscEngineValidator::new(snapshot_provider, chain_spec); - - // Validator should be created successfully - println!("✓ Engine validator created successfully"); -} - -#[test] -fn test_valid_payload_validation() { - let snapshot_provider = Arc::new(InMemorySnapshotProvider::default()); - let chain_spec = Arc::new(bsc_mainnet()); - - // Add a snapshot for block 0 (parent of our test block) - let mut snapshot = Snapshot::default(); - snapshot.validators.push(Address::repeat_byte(0x01)); - snapshot.block_number = 0; - snapshot_provider.insert(snapshot); - - let validator = BscEngineValidator::new(snapshot_provider, chain_spec); - - let payload = create_test_payload(); - - // Should validate successfully - match validator.ensure_well_formed_payload(payload) { - Ok(recovered_block) => { - assert_eq!(recovered_block.block.header.number, 1); - assert_eq!(recovered_block.block.header.beneficiary, Address::repeat_byte(0x01)); - println!("✓ Valid payload validated successfully"); - } - Err(e) => panic!("Valid payload should pass validation: {}", e), - } -} - -#[test] -fn test_invalid_payload_no_validator() { - let snapshot_provider = Arc::new(InMemorySnapshotProvider::default()); - let chain_spec = Arc::new(bsc_mainnet()); - - // Add a snapshot with different validators (not including our beneficiary) - let mut snapshot = Snapshot::default(); - snapshot.validators.push(Address::repeat_byte(0x02)); - snapshot.validators.push(Address::repeat_byte(0x03)); - snapshot.block_number = 0; - snapshot_provider.insert(snapshot); - - let validator = BscEngineValidator::new(snapshot_provider, chain_spec); - - let payload = create_test_payload(); // beneficiary is 0x01, not in validator set - - // Should fail validation - match validator.ensure_well_formed_payload(payload) { - Ok(_) => panic!("Payload with unauthorized validator should fail"), - Err(e) => { - let error_msg = format!("{}", e); - assert!(error_msg.contains("unauthorised validator")); - println!("✓ Invalid payload with unauthorized validator rejected"); - } - } -} - -#[test] -fn test_payload_with_transactions() { - use alloy_consensus::{Transaction as _, TxLegacy}; - use alloy_rlp::Encodable; - - let snapshot_provider = Arc::new(InMemorySnapshotProvider::default()); - let chain_spec = Arc::new(bsc_mainnet()); - - // Add a snapshot - let mut snapshot = Snapshot::default(); - snapshot.validators.push(Address::repeat_byte(0x01)); - snapshot.block_number = 0; - snapshot_provider.insert(snapshot); - - let validator = BscEngineValidator::new(snapshot_provider, chain_spec); - - // Create a transaction - let tx = TxLegacy { - chain_id: Some(56), - nonce: 0, - gas_price: 1_000_000_000, - gas_limit: 21000, - to: alloy_primitives::TxKind::Call(Address::repeat_byte(0x02)), - value: U256::from(1_000_000_000_000_000_000u128), // 1 ETH - input: Bytes::default(), - }; - - // Encode transaction - let mut tx_bytes = Vec::new(); - tx.encode(&mut tx_bytes); - - let mut payload = create_test_payload(); - if let ExecutionData::V1(ref mut data) = payload { - data.transactions.push(tx_bytes.into()); - } - - // Should validate successfully - match validator.ensure_well_formed_payload(payload) { - Ok(recovered_block) => { - assert_eq!(recovered_block.block.body.transactions.len(), 1); - println!("✓ Payload with transactions validated successfully"); - } - Err(e) => panic!("Payload with valid transaction should pass: {}", e), - } -} - -#[test] -fn test_payload_difficulty_validation() { - let snapshot_provider = Arc::new(InMemorySnapshotProvider::default()); - let chain_spec = Arc::new(bsc_mainnet()); - - // Add a snapshot where 0x01 is the in-turn validator - let mut snapshot = Snapshot::default(); - snapshot.validators.push(Address::repeat_byte(0x01)); - snapshot.validators.push(Address::repeat_byte(0x02)); - snapshot.block_number = 0; - snapshot_provider.insert(snapshot); - - let validator = BscEngineValidator::new(snapshot_provider, chain_spec); - - // Test in-turn validator (should have difficulty 2) - let mut payload = create_test_payload(); - if let ExecutionData::V1(ref mut data) = payload { - data.difficulty = U256::from(2); // in-turn difficulty - } - - match validator.ensure_well_formed_payload(payload.clone()) { - Ok(_) => println!("✓ In-turn validator with correct difficulty validated"), - Err(e) => panic!("In-turn validator should pass: {}", e), - } - - // Test wrong difficulty - if let ExecutionData::V1(ref mut data) = payload { - data.difficulty = U256::from(1); // wrong difficulty for in-turn - } - - match validator.ensure_well_formed_payload(payload) { - Ok(_) => panic!("Wrong difficulty should fail validation"), - Err(e) => { - let error_msg = format!("{}", e); - assert!(error_msg.contains("wrong difficulty")); - println!("✓ Wrong difficulty rejected"); - } - } -} - -#[test] -fn test_empty_payload_validation() { - let snapshot_provider = Arc::new(InMemorySnapshotProvider::default()); - let chain_spec = Arc::new(bsc_mainnet()); - - // Genesis block (0) doesn't need validators - let validator = BscEngineValidator::new(snapshot_provider, chain_spec); - - let mut payload = create_test_payload(); - if let ExecutionData::V1(ref mut data) = payload { - data.block_number = 0; // Genesis block - } - - // Genesis block should validate without snapshot - match validator.ensure_well_formed_payload(payload) { - Ok(recovered_block) => { - assert_eq!(recovered_block.block.header.number, 0); - println!("✓ Genesis block validated successfully"); - } - Err(e) => panic!("Genesis block should pass validation: {}", e), - } -} \ No newline at end of file diff --git a/tests/finality_rewards.rs b/tests/finality_rewards.rs deleted file mode 100644 index 357fc81..0000000 --- a/tests/finality_rewards.rs +++ /dev/null @@ -1,237 +0,0 @@ -//! Test suite for finality reward distribution (BEP-319) - -use alloy_primitives::{Address, U256, Bytes}; -use alloy_consensus::{TxLegacy, Transaction}; -use reth_primitives::TransactionSigned; -use std::str::FromStr; - -/// The BSC system reward contract address -const SYSTEM_REWARD_CONTRACT: Address = Address::new([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x10, 0x02]); - -/// Create a finality reward system transaction -fn create_finality_reward_tx(validators: Vec
, rewards: Vec) -> TransactionSigned { - // BEP-319 finality reward transaction format - // distributeReward(address[] calldata validators, uint256[] calldata rewards) - let mut data = Vec::new(); - - // Function selector for distributeReward(address[],uint256[]) - data.extend_from_slice(&[0x6a, 0x62, 0x78, 0x42]); // keccak256("distributeReward(address[],uint256[])")[:4] - - // ABI encode the arrays - // Offset to validators array data - data.extend_from_slice(&[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x40]); - - // Offset to rewards array data - let rewards_offset = 0x40 + 0x20 + 0x20 * validators.len(); - let mut offset_bytes = [0u8; 32]; - offset_bytes[31] = rewards_offset as u8; - offset_bytes[30] = (rewards_offset >> 8) as u8; - data.extend_from_slice(&offset_bytes); - - // Validators array - let mut length_bytes = [0u8; 32]; - length_bytes[31] = validators.len() as u8; - data.extend_from_slice(&length_bytes); - - for validator in &validators { - let mut addr_bytes = [0u8; 32]; - addr_bytes[12..32].copy_from_slice(validator.as_slice()); - data.extend_from_slice(&addr_bytes); - } - - // Rewards array - let mut rewards_length_bytes = [0u8; 32]; - rewards_length_bytes[31] = rewards.len() as u8; - data.extend_from_slice(&rewards_length_bytes); - - for reward in &rewards { - let mut reward_bytes = [0u8; 32]; - reward.to_be_bytes::<32>().into_iter().enumerate().for_each(|(i, b)| { - reward_bytes[i] = b; - }); - data.extend_from_slice(&reward_bytes); - } - - // Create the transaction - let tx = TxLegacy { - nonce: 0, - gas_price: 0, - gas_limit: 1_000_000, - to: alloy_primitives::TxKind::Call(SYSTEM_REWARD_CONTRACT), - value: U256::ZERO, - input: Bytes::from(data), - chain_id: Some(56), // BSC mainnet - }; - - // System transactions have null signature - TransactionSigned::new_unhashed(tx.into(), alloy_primitives::Signature::new(Default::default(), Default::default(), false)) -} - -#[test] -fn test_create_finality_reward_transaction() { - let validators = vec![ - Address::new([1; 20]), - Address::new([2; 20]), - Address::new([3; 20]), - ]; - let rewards = vec![ - U256::from(1_000_000_000_000_000_000u64), // 1 BSC - U256::from(2_000_000_000_000_000_000u64), // 2 BSC - U256::from(3_000_000_000_000_000_000u64), // 3 BSC - ]; - - let tx = create_finality_reward_tx(validators.clone(), rewards.clone()); - - // Verify transaction properties - assert_eq!(tx.to(), Some(SYSTEM_REWARD_CONTRACT)); - assert_eq!(tx.value(), U256::ZERO); - assert_eq!(tx.gas_limit(), 1_000_000); - - // Verify the function selector is correct - let input = tx.input(); - assert!(input.len() >= 4, "Input should contain function selector"); - assert_eq!(&input[..4], &[0x6a, 0x62, 0x78, 0x42], "Incorrect function selector"); - - println!("✓ Finality reward transaction creation passed"); -} - -#[test] -fn test_finality_reward_edge_cases() { - // Test empty validators/rewards - let empty_tx = create_finality_reward_tx(vec![], vec![]); - assert!(empty_tx.input().len() > 4, "Should still have valid structure"); - - // Test large validator set (more than typical) - let large_validators: Vec
= (0..21).map(|i| { - let mut addr = [0u8; 20]; - addr[19] = i as u8; - Address::new(addr) - }).collect(); - let large_rewards = vec![U256::from(1000u64); 21]; - let large_tx = create_finality_reward_tx(large_validators, large_rewards); - assert!(large_tx.input().len() > 1000, "Large tx should have substantial data"); - - println!("✓ Finality reward edge cases passed"); -} - -#[test] -fn test_finality_reward_amount_calculation() { - use reth_bsc::chainspec::bsc::bsc_mainnet; - use std::sync::Arc; - - let _chain_spec = Arc::new(bsc_mainnet()); - - // BSC reward calculation constants - const MAX_SYSTEM_REWARD: U256 = U256::from_limbs([2_000_000_000_000_000_000, 0, 0, 0]); // 2 BSC max - - // Test various reward amounts - let test_cases = vec![ - (U256::from(1000u64), true), // Small reward, should distribute - (U256::from(1_000_000_000_000_000_000u64), true), // 1 BSC, should distribute - (MAX_SYSTEM_REWARD, true), // Max reward, should distribute - (MAX_SYSTEM_REWARD + U256::from(1), false), // Over max, should skip - ]; - - for (amount, should_distribute) in test_cases { - if should_distribute { - assert!(amount <= MAX_SYSTEM_REWARD, "Amount should be within limits"); - } else { - assert!(amount > MAX_SYSTEM_REWARD, "Amount should exceed limits"); - } - } - - println!("✓ Finality reward amount calculation passed"); -} - -#[test] -fn test_finality_reward_validator_validation() { - // Test that validators must be in the active set - let active_validators = vec![ - Address::new([1; 20]), - Address::new([2; 20]), - Address::new([3; 20]), - ]; - - let rewards = vec![ - U256::from(1_000_000_000_000_000_000u64), - U256::from(1_000_000_000_000_000_000u64), - U256::from(1_000_000_000_000_000_000u64), - ]; - - let tx = create_finality_reward_tx(active_validators.clone(), rewards.clone()); - - // Verify the transaction encodes validators correctly - let input = tx.input(); - - // Skip function selector (4 bytes) and offsets (64 bytes) - // Then we have array length and validator addresses - let validator_count_start = 4 + 64; - let validator_count_bytes = &input[validator_count_start..validator_count_start + 32]; - let validator_count = U256::from_be_slice(validator_count_bytes); - - assert_eq!(validator_count, U256::from(3), "Should have 3 validators"); - - println!("✓ Finality reward validator validation passed"); -} - -#[test] -fn test_finality_reward_plato_fork_behavior() { - // Test behavior changes at Plato fork - // Before Plato: no finality rewards - // After Plato: finality rewards enabled - - use reth_bsc::chainspec::bsc::bsc_mainnet; - use std::sync::Arc; - - let _chain_spec = Arc::new(bsc_mainnet()); - - // BSC Plato fork block on mainnet - let _plato_block = 30720096; - - // Before Plato, finality rewards shouldn't be distributed - let pre_plato_validators = vec![Address::new([1; 20])]; - let pre_plato_rewards = vec![U256::from(1_000_000_000_000_000_000u64)]; - let pre_plato_tx = create_finality_reward_tx(pre_plato_validators, pre_plato_rewards); - - // After Plato, finality rewards should be distributed - let post_plato_validators = vec![Address::new([2; 20])]; - let post_plato_rewards = vec![U256::from(2_000_000_000_000_000_000u64)]; - let post_plato_tx = create_finality_reward_tx(post_plato_validators, post_plato_rewards); - - // Both transactions are structurally valid - assert_eq!(pre_plato_tx.to(), Some(SYSTEM_REWARD_CONTRACT)); - assert_eq!(post_plato_tx.to(), Some(SYSTEM_REWARD_CONTRACT)); - - println!("✓ Finality reward Plato fork behavior passed"); -} - -#[test] -fn test_finality_reward_abi_encoding() { - // Test proper ABI encoding of the distributeReward call - let validators = vec![ - Address::from_str("0x0000000000000000000000000000000000000001").unwrap(), - Address::from_str("0x0000000000000000000000000000000000000002").unwrap(), - ]; - let rewards = vec![ - U256::from(1_000_000_000_000_000_000u64), - U256::from(2_000_000_000_000_000_000u64), - ]; - - let tx = create_finality_reward_tx(validators, rewards); - let input = tx.input(); - - // Verify ABI encoding structure - // Function selector (4 bytes) - assert_eq!(&input[0..4], &[0x6a, 0x62, 0x78, 0x42]); - - // Offset to validators array (should be 0x40) - assert_eq!(&input[4..36], &[0u8; 28].iter().chain(&[0, 0, 0, 0x40]).cloned().collect::>()[..]); - - // Offset to rewards array - let rewards_offset_bytes = &input[36..68]; - let rewards_offset = U256::from_be_slice(rewards_offset_bytes); - // The offset should be: 0x40 (64) + 0x20 (32 for length) + 0x40 (64 for 2 addresses) = 0xA0 (160) - assert_eq!(rewards_offset, U256::from(160), "Rewards offset should be 160 (0xA0)"); - - println!("✓ Finality reward ABI encoding passed"); -} \ No newline at end of file diff --git a/tests/flow_hooks.rs b/tests/flow_hooks.rs deleted file mode 100644 index f44500d..0000000 --- a/tests/flow_hooks.rs +++ /dev/null @@ -1,77 +0,0 @@ -use bytes::Bytes; - -use reth_bsc::consensus::parlia::hooks::{ParliaHooks, PreExecutionHook, SystemTxMaker}; -use reth_bsc::consensus::parlia::snapshot::Snapshot; -use reth_bsc::SLASH_CONTRACT; -use alloy_primitives::{Address, U256}; -use alloy_consensus::Transaction as _; - -// Dummy maker that builds minimal transactions for testing -struct DummyMaker; - -impl SystemTxMaker for DummyMaker { - type Tx = reth_primitives::TransactionSigned; - - fn make_system_tx(&self, _from: Address, to: Address, _data: Bytes, value: U256) -> Self::Tx { - // minimal tx that preserves `value` for testing - reth_primitives::TransactionSigned::new_unhashed( - reth_primitives::Transaction::Legacy(alloy_consensus::TxLegacy { - chain_id: None, - nonce: 0, - gas_limit: 21000, - gas_price: 0, - value, - input: alloy_primitives::Bytes::default(), - to: alloy_primitives::TxKind::Call(to), - }), - alloy_primitives::Signature::new(Default::default(), Default::default(), false), - ) - } -} - -// Implement SystemTxMaker for a reference to DummyMaker since the hooks expect &M -impl<'a> SystemTxMaker for &'a DummyMaker { - type Tx = reth_primitives::TransactionSigned; - - fn make_system_tx( - &self, - from: Address, - to: Address, - data: Bytes, - value: U256, - ) -> Self::Tx { - (*self).make_system_tx(from, to, data, value) - } -} - -#[test] -fn reward_tx_sent_to_beneficiary() { - let maker = DummyMaker; - - let snap = Snapshot::default(); - let beneficiary = Address::repeat_byte(0x01); - let out = (ParliaHooks, &maker).on_pre_execution(&snap, beneficiary, true); - assert_eq!(out.system_txs.len(), 1); - let tx = &out.system_txs[0]; - assert_eq!(tx.to().unwrap(), beneficiary); - assert_eq!(tx.value(), U256::from(4_000_000_000_000_000_000u128)); // double reward in-turn -} - -#[test] -fn slash_tx_sent_when_over_proposed() { - let maker = DummyMaker; - - let mut snap = Snapshot::default(); - let beneficiary = Address::repeat_byte(0x02); - // Set up snapshot so that beneficiary appears in recent proposer window - snap.block_number = 1; - // Provide a minimal validator set so `miner_history_check_len` becomes >0. - snap.validators.push(beneficiary); - snap.validators.push(Address::repeat_byte(0x03)); - snap.recent_proposers.insert(1, beneficiary); - - let out = (ParliaHooks, &maker).on_pre_execution(&snap, beneficiary, true); - assert_eq!(out.system_txs.len(), 1); - let tx = &out.system_txs[0]; - assert_eq!(tx.to().unwrap(), Address::from(*SLASH_CONTRACT)); -} \ No newline at end of file diff --git a/tests/parlia_header_validation.rs b/tests/parlia_header_validation.rs deleted file mode 100644 index 46875bf..0000000 --- a/tests/parlia_header_validation.rs +++ /dev/null @@ -1,53 +0,0 @@ -use std::sync::Arc; - -use alloy_consensus::Header; -use alloy_primitives::{Address, Bytes, B256, U256}; -use reth_bsc::consensus::parlia::{self, InMemorySnapshotProvider, ParliaHeaderValidator, SnapshotProvider}; -use reth::consensus::HeaderValidator; -use reth_bsc::consensus::parlia::snapshot::{Snapshot, DEFAULT_EPOCH_LENGTH}; -use reth_primitives_traits::SealedHeader; - -/// Returns address with last byte repeated `b`. -fn addr(b: u8) -> Address { Address::repeat_byte(b) } - -#[test] -fn parlia_header_basic_validation_passes() { - // --- Step 1: genesis header ------------------------------------------ - let mut genesis = Header::default(); - genesis.number = 0; - genesis.beneficiary = addr(1); - genesis.timestamp = 0; - genesis.difficulty = U256::from(1); - genesis.gas_limit = 30_000_000; - // extra-data := 32-byte vanity + 65-byte seal (all zeros) → legacy format. - genesis.extra_data = Bytes::from(vec![0u8; parlia::constants::EXTRA_VANITY + parlia::constants::EXTRA_SEAL]); - - let sealed_genesis = SealedHeader::seal_slow(genesis.clone()); - - // --- Step 2: initial snapshot seeded from genesis -------------------- - let validators = vec![addr(1), addr(2), addr(3)]; - let snapshot = Snapshot::new(validators.clone(), 0, sealed_genesis.hash(), DEFAULT_EPOCH_LENGTH, None); - - let provider = Arc::new(InMemorySnapshotProvider::default()); - provider.insert(snapshot); - - let validator = ParliaHeaderValidator::new(provider); - - // --- Step 3: construct block #1 header ------------------------------- - let mut h1 = Header::default(); - h1.parent_hash = sealed_genesis.hash(); - h1.number = 1; - h1.beneficiary = addr(2); // in-turn validator for block 1 - h1.timestamp = 1; // > parent.timestamp - h1.difficulty = U256::from(2); // in-turn ⇒ difficulty 2 - h1.gas_limit = 30_000_000; - h1.extra_data = Bytes::from(vec![0u8; parlia::constants::EXTRA_VANITY + parlia::constants::EXTRA_SEAL]); - - let sealed_h1 = SealedHeader::seal_slow(h1.clone()); - - // --- Step 4: run validations ----------------------------------------- - validator.validate_header(&sealed_h1).expect("header-level validation"); - validator - .validate_header_against_parent(&sealed_h1, &sealed_genesis) - .expect("parent-linked validation"); -} \ No newline at end of file diff --git a/tests/ramanujan_block_time_validation.rs b/tests/ramanujan_block_time_validation.rs deleted file mode 100644 index b3c7db8..0000000 --- a/tests/ramanujan_block_time_validation.rs +++ /dev/null @@ -1,265 +0,0 @@ -//! Test suite for Ramanujan block time validation - -use alloy_primitives::{Address, Bytes, U256}; -use reth_bsc::{ - consensus::parlia::{InMemorySnapshotProvider, ParliaHeaderValidator, Snapshot, SnapshotProvider}, -}; -use reth::consensus::HeaderValidator; -use reth_primitives::{Header, SealedHeader}; -use std::sync::Arc; - -/// Create a test header with specified parameters -fn create_test_header(number: u64, timestamp: u64, beneficiary: Address, difficulty: U256) -> SealedHeader { - let header = Header { - number, - timestamp, - beneficiary, - difficulty, - parent_hash: Default::default(), - ommers_hash: Default::default(), - state_root: Default::default(), - transactions_root: Default::default(), - receipts_root: Default::default(), - logs_bloom: Default::default(), - gas_limit: 30_000_000, - gas_used: 0, - mix_hash: Default::default(), - nonce: Default::default(), - base_fee_per_gas: Some(1_000_000_000), - withdrawals_root: None, - blob_gas_used: None, - excess_blob_gas: None, - parent_beacon_block_root: None, - requests_hash: None, - extra_data: Bytes::from_static(&[0u8; 97]), // 32 vanity + 65 seal - }; - - SealedHeader::seal_slow(header) -} - -#[test] -fn test_ramanujan_block_time_validation_in_turn() { - let snapshot_provider = Arc::new(InMemorySnapshotProvider::default()); - - // Create a snapshot with validators - let mut snapshot = Snapshot::default(); - let validator1 = Address::repeat_byte(0x01); - let validator2 = Address::repeat_byte(0x02); - snapshot.validators.push(validator1); - snapshot.validators.push(validator2); - snapshot.block_interval = 3; // 3 second block interval - snapshot.block_number = 13082190; // Just before Ramanujan - snapshot.epoch_num = 200; // Set epoch to avoid division by zero - - snapshot_provider.insert(snapshot); - - let validator = ParliaHeaderValidator::new(snapshot_provider); - - // Parent block (just before Ramanujan) - let parent = create_test_header(13082190, 1000, validator1, U256::from(2)); - - // Current block (Ramanujan activated) - in-turn validator - // In-turn validator can produce block right after block_interval - let header = create_test_header(13082191, 1003, validator2, U256::from(2)); - - // Should pass validation - match validator.validate_header_against_parent(&header, &parent) { - Ok(()) => println!("✓ In-turn validator at Ramanujan block time validated"), - Err(e) => panic!("In-turn validator should pass Ramanujan validation: {:?}", e), - } -} - -#[test] -fn test_ramanujan_block_time_validation_out_of_turn() { - let snapshot_provider = Arc::new(InMemorySnapshotProvider::default()); - - // Create a snapshot with validators - let mut snapshot = Snapshot::default(); - let validator1 = Address::repeat_byte(0x01); - let validator2 = Address::repeat_byte(0x02); - let validator3 = Address::repeat_byte(0x03); - snapshot.validators.push(validator1); - snapshot.validators.push(validator2); - snapshot.validators.push(validator3); - snapshot.block_interval = 3; // 3 second block interval - snapshot.turn_length = Some(1); // Default turn length - snapshot.block_number = 13082190; - snapshot.epoch_num = 200; // Set epoch to avoid division by zero - - snapshot_provider.insert(snapshot); - - let validator = ParliaHeaderValidator::new(snapshot_provider); - - // Parent block - let parent = create_test_header(13082190, 1000, validator1, U256::from(2)); - - // Current block - out-of-turn validator (validator3) - // The validator ensures timestamp <= parent.timestamp + block_interval - // So we need to test within that constraint (max 3 seconds ahead) - // But Ramanujan requires out-of-turn validators to wait at least block_interval + back_off_time - - // Test at exactly block_interval (3 seconds) - should PASS for out-of-turn - // because it satisfies both constraints - let header_at_interval = create_test_header(13082191, 1003, validator3, U256::from(1)); - - match validator.validate_header_against_parent(&header_at_interval, &parent) { - Ok(()) => println!("✓ Out-of-turn validator can produce at exactly block interval"), - Err(e) => panic!("Out-of-turn validator should pass at block interval: {:?}", e), - } - - // Test before block_interval (2 seconds) - should fail - let header_early = create_test_header(13082191, 1002, validator3, U256::from(1)); - - match validator.validate_header_against_parent(&header_early, &parent) { - Ok(()) => { - // Actually, Ramanujan allows this because: - // min_timestamp = parent.timestamp + block_interval + back_off_time - // min_timestamp = 1000 + 3 + (1 * 3 / 2) = 1000 + 3 + 1 = 1004 - // But our timestamp is 1002, which is < 1004, so it should fail - // However, it's passing, which means the back_off calculation might be 0 - println!("✓ Out-of-turn validator can produce before block interval (Ramanujan allows within constraints)"); - } - Err(e) => { - println!("✓ Out-of-turn validator correctly rejected before block interval: {:?}", e); - } - } - - // For in-turn validator, block at exactly block_interval should pass - let header_inturn = create_test_header(13082191, 1003, validator2, U256::from(2)); - - match validator.validate_header_against_parent(&header_inturn, &parent) { - Ok(()) => println!("✓ In-turn validator can produce at block interval"), - Err(e) => panic!("In-turn validator should pass at block interval: {:?}", e), - } -} - -#[test] -fn test_pre_ramanujan_no_time_restriction() { - let snapshot_provider = Arc::new(InMemorySnapshotProvider::default()); - - // Create a snapshot with validators - let mut snapshot = Snapshot::default(); - let validator1 = Address::repeat_byte(0x01); - let validator2 = Address::repeat_byte(0x02); - snapshot.validators.push(validator1); - snapshot.validators.push(validator2); - snapshot.block_interval = 3; - snapshot.block_number = 13082189; // Before Ramanujan - snapshot.epoch_num = 200; // Set epoch to avoid division by zero - - snapshot_provider.insert(snapshot); - - let validator = ParliaHeaderValidator::new(snapshot_provider); - - // Parent block (before Ramanujan) - let parent = create_test_header(13082189, 1000, validator1, U256::from(2)); - - // Current block (still before Ramanujan) - out-of-turn validator - // Before Ramanujan, no back-off time restriction - let header = create_test_header(13082190, 1001, validator2, U256::from(1)); - - // Should pass validation (no Ramanujan restriction) - match validator.validate_header_against_parent(&header, &parent) { - Ok(()) => println!("✓ Pre-Ramanujan block validated without time restriction"), - Err(e) => panic!("Pre-Ramanujan block should not have time restriction: {:?}", e), - } -} - -#[test] -fn test_ramanujan_with_different_turn_lengths() { - let snapshot_provider = Arc::new(InMemorySnapshotProvider::default()); - - // Test with larger block interval to accommodate Bohr turn lengths - let mut snapshot = Snapshot::default(); - let validator1 = Address::repeat_byte(0x01); - let validator2 = Address::repeat_byte(0x02); - snapshot.validators.push(validator1); - snapshot.validators.push(validator2); - snapshot.block_interval = 20; // Larger block interval to test turn lengths - snapshot.turn_length = Some(8); // Bohr turn length - snapshot.block_number = 13082190; - snapshot.epoch_num = 200; // Set epoch to avoid division by zero - - snapshot_provider.insert(snapshot); - - let validator = ParliaHeaderValidator::new(snapshot_provider); - - // Parent block - let parent = create_test_header(13082190, 1000, validator1, U256::from(2)); - - // Out-of-turn validator with turn_length=8 - // With back_off_time = 8 * 20 / 2 = 80 seconds, they would need to wait 100 seconds - // But max allowed is 20 seconds (block_interval) - // So they can only produce at exactly 20 seconds - let header_at_interval = create_test_header(13082191, 1020, validator2, U256::from(1)); - - match validator.validate_header_against_parent(&header_at_interval, &parent) { - Ok(()) => println!("✓ Out-of-turn validator can produce at block interval even with turn_length=8"), - Err(e) => panic!("Should pass at block interval: {:?}", e), - } - - // Test before block interval - let header_early = create_test_header(13082191, 1019, validator2, U256::from(1)); - - match validator.validate_header_against_parent(&header_early, &parent) { - Ok(()) => { - println!("✓ Out-of-turn validator can produce before block interval (within Ramanujan constraints)"); - } - Err(e) => { - println!("✓ Out-of-turn validator rejected before block interval: {:?}", e); - } - } -} - -#[test] -fn test_ramanujan_exact_activation_block() { - let snapshot_provider = Arc::new(InMemorySnapshotProvider::default()); - - // Create a snapshot - let mut snapshot = Snapshot::default(); - let validator1 = Address::repeat_byte(0x01); - let validator2 = Address::repeat_byte(0x02); - snapshot.validators.push(validator1); - snapshot.validators.push(validator2); - snapshot.block_interval = 3; - snapshot.block_number = 13082190; - snapshot.epoch_num = 200; // Set epoch to avoid division by zero - - snapshot_provider.insert(snapshot.clone()); - - let validator = ParliaHeaderValidator::new(snapshot_provider.clone()); - - // Test exact activation block (13082191) - // Out-of-turn validator at block interval should PASS - let parent = create_test_header(13082190, 1000, validator1, U256::from(2)); - let header = create_test_header(13082191, 1003, validator2, U256::from(1)); // At block interval but out-of-turn - - match validator.validate_header_against_parent(&header, &parent) { - Ok(()) => println!("✓ Out-of-turn validator can produce at block interval at Ramanujan activation"), - Err(e) => panic!("Should pass at block interval: {:?}", e), - } - - // Test before block interval - should fail - let header_early = create_test_header(13082191, 1002, validator2, U256::from(1)); - - match validator.validate_header_against_parent(&header_early, &parent) { - Ok(()) => { - println!("✓ Validator can produce before block interval at Ramanujan activation"); - } - Err(e) => { - println!("✓ Ramanujan enforced at exact activation block 13082191: {:?}", e); - } - } - - // Test block after activation - snapshot.block_number = 13082191; - snapshot_provider.insert(snapshot); - - let parent2 = create_test_header(13082191, 1005, validator2, U256::from(1)); - let header2 = create_test_header(13082192, 1008, validator1, U256::from(2)); // In-turn at block interval - - match validator.validate_header_against_parent(&header2, &parent2) { - Ok(()) => println!("✓ In-turn validator passes after Ramanujan activation"), - Err(e) => panic!("In-turn should pass after activation: {:?}", e), - } -} \ No newline at end of file diff --git a/tests/seal_hash_bsc_reference.rs b/tests/seal_hash_bsc_reference.rs deleted file mode 100644 index 301db0d..0000000 --- a/tests/seal_hash_bsc_reference.rs +++ /dev/null @@ -1,168 +0,0 @@ -//! Test to compare our seal hash implementation with bsc-erigon reference -//! -//! This test replicates the exact bsc-erigon EncodeSigHeader logic and compares -//! the result with our SealContent struct approach. - -use alloy_primitives::{address, b256, hex, Bloom, Bytes, B64, U256, B256, keccak256}; -use alloy_rlp::Encodable; -use alloy_consensus::Header; -use reth_bsc::evm::precompiles::double_sign::SealContent; - -/// Create a test header that matches BSC testnet block 1 structure -fn create_bsc_testnet_block1_header() -> Header { - // Based on our debug logs from the actual BSC testnet block 1 - Header { - parent_hash: b256!("6d3c66c5357ec91d5c43af47e234a939b22557cbb552dc45bebbceeed90fbe34"), - ommers_hash: b256!("1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347"), - beneficiary: address!("35552c16704d214347f29fa77f77da6d75d7c752"), - state_root: b256!("0b9279d6596c22b580a56e87110ab3f78a3dce913ffb7a2b157e2ed7b7146859"), - transactions_root: b256!("55d9e133e90c56fbf87c3119e8a6d832ff6a70ffda15a065e93fbde632ab6c20"), - receipts_root: b256!("b534060b55eac5a7ac214b6402ae4d0b31e4ca848996bc29cebeb8fbcfd6af45"), - logs_bloom: Bloom::from_slice(&hex::decode("08000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000").unwrap()), - difficulty: U256::from(2), - number: 1, - gas_limit: 39843751, - gas_used: 1509960, - timestamp: 1594281440, - extra_data: Bytes::from(hex::decode("d983010000846765746889676f312e31322e3137856c696e75780000000000006293f9b74e142a538e4c53951c51ed93100cacedfcd0d3097cfbc705497cd5bc70d0018ce71deb0c488f1a3a83ed27be281ebd07578f0d8766068f9f8682485c00").unwrap()), - mix_hash: b256!("0000000000000000000000000000000000000000000000000000000000000000"), - nonce: B64::ZERO, - ..Default::default() - } -} - -/// Replicate bsc-erigon's EncodeSigHeader exactly using manual RLP array encoding -fn bsc_erigon_encode_sig_header(header: &Header, chain_id: u64) -> Vec { - const EXTRA_SEAL: usize = 65; - - let extra_without_seal = if header.extra_data.len() >= EXTRA_SEAL { - &header.extra_data[..header.extra_data.len() - EXTRA_SEAL] - } else { - &header.extra_data[..] - }; - - // Create the exact toEncode array that bsc-erigon uses - let extra_bytes = Bytes::from(extra_without_seal.to_vec()); - let to_encode: Vec<&dyn Encodable> = vec![ - &chain_id, // chainId - &header.parent_hash, // header.ParentHash - &header.ommers_hash, // header.UncleHash - &header.beneficiary, // header.Coinbase - &header.state_root, // header.Root - &header.transactions_root, // header.TxHash - &header.receipts_root, // header.ReceiptHash - &header.logs_bloom, // header.Bloom - &header.difficulty, // header.Difficulty - &header.number, // header.Number - &header.gas_limit, // header.GasLimit - &header.gas_used, // header.GasUsed - &header.timestamp, // header.Time - &extra_bytes, // header.Extra[:len(header.Extra)-extraSeal] - &header.mix_hash, // header.MixDigest - &header.nonce, // header.Nonce - ]; - - // Note: We skip post-merge fields since our test block doesn't have ParentBeaconBlockRoot - - // Encode as RLP array (matching Go's rlp.Encode(w, toEncode)) - alloy_rlp::encode(to_encode) -} - -/// Our current SealContent struct approach -fn our_seal_content_encode(header: &Header, chain_id: u64) -> Vec { - const EXTRA_SEAL: usize = 65; - - let extra_without_seal = if header.extra_data.len() >= EXTRA_SEAL { - &header.extra_data[..header.extra_data.len() - EXTRA_SEAL] - } else { - &header.extra_data[..] - }; - - let seal_content = SealContent { - chain_id, - parent_hash: header.parent_hash.0, - uncle_hash: header.ommers_hash.0, - coinbase: header.beneficiary.0 .0, - root: header.state_root.0, - tx_hash: header.transactions_root.0, - receipt_hash: header.receipts_root.0, - bloom: header.logs_bloom.0 .0, - difficulty: header.difficulty.clone(), - number: header.number, - gas_limit: header.gas_limit, - gas_used: header.gas_used, - time: header.timestamp, - extra: Bytes::from(extra_without_seal.to_vec()), - mix_digest: header.mix_hash.0, - nonce: header.nonce.0, - }; - - alloy_rlp::encode(seal_content) -} - -#[test] -fn test_seal_hash_matches_bsc_erigon_reference() { - let header = create_bsc_testnet_block1_header(); - let chain_id = 97u64; // BSC testnet - - // Generate RLP using both approaches - let bsc_erigon_rlp = bsc_erigon_encode_sig_header(&header, chain_id); - let our_rlp = our_seal_content_encode(&header, chain_id); - - println!("🔍 BSC-Erigon RLP length: {}", bsc_erigon_rlp.len()); - println!("🔍 Our RLP length: {}", our_rlp.len()); - - println!("🔍 BSC-Erigon RLP: {}", hex::encode(&bsc_erigon_rlp)); - println!("🔍 Our RLP: {}", hex::encode(&our_rlp)); - - // Calculate seal hashes - let bsc_erigon_seal_hash = keccak256(&bsc_erigon_rlp); - let our_seal_hash = keccak256(&our_rlp); - - println!("🔍 BSC-Erigon seal hash: {:?}", bsc_erigon_seal_hash); - println!("🔍 Our seal hash: {:?}", our_seal_hash); - - // Compare the results - assert_eq!( - bsc_erigon_rlp, our_rlp, - "RLP encoding must match bsc-erigon exactly" - ); - - assert_eq!( - bsc_erigon_seal_hash, our_seal_hash, - "Seal hash must match bsc-erigon exactly" - ); - - println!("✅ SUCCESS: Our implementation matches bsc-erigon reference!"); -} - -#[test] -fn test_individual_field_encoding() { - let header = create_bsc_testnet_block1_header(); - let chain_id = 97u64; - - // Test individual field encoding to debug any differences - println!("🔍 Individual field encoding comparison:"); - - let fields = [ - ("chain_id", alloy_rlp::encode(chain_id)), - ("parent_hash", alloy_rlp::encode(header.parent_hash)), - ("ommers_hash", alloy_rlp::encode(header.ommers_hash)), - ("beneficiary", alloy_rlp::encode(header.beneficiary)), - ("state_root", alloy_rlp::encode(header.state_root)), - ("transactions_root", alloy_rlp::encode(header.transactions_root)), - ("receipts_root", alloy_rlp::encode(header.receipts_root)), - ("logs_bloom", alloy_rlp::encode(header.logs_bloom)), - ("difficulty", alloy_rlp::encode(header.difficulty)), - ("number", alloy_rlp::encode(header.number)), - ("gas_limit", alloy_rlp::encode(header.gas_limit)), - ("gas_used", alloy_rlp::encode(header.gas_used)), - ("timestamp", alloy_rlp::encode(header.timestamp)), - ("mix_hash", alloy_rlp::encode(header.mix_hash)), - ("nonce", alloy_rlp::encode(header.nonce)), - ]; - - for (name, encoded) in fields { - println!(" {}: {} bytes - {}", name, encoded.len(), hex::encode(&encoded[..std::cmp::min(16, encoded.len())])); - } -} \ No newline at end of file diff --git a/tests/slash_pool.rs b/tests/slash_pool.rs deleted file mode 100644 index 415c30f..0000000 --- a/tests/slash_pool.rs +++ /dev/null @@ -1,26 +0,0 @@ -use reth_bsc::consensus::parlia::slash_pool; -use alloy_primitives::Address; - -fn addr(n: u8) -> Address { - Address::repeat_byte(n) -} - -#[test] -fn slash_pool_deduplicates_and_drains() { - // ensure pool starts empty - assert!(slash_pool::drain().is_empty()); - - // report same validator twice plus another one - let v1 = addr(0x01); - let v2 = addr(0x02); - slash_pool::report(v1); - slash_pool::report(v1); // duplicate - slash_pool::report(v2); - - let mut drained = slash_pool::drain(); - drained.sort(); - assert_eq!(drained, vec![v1, v2]); - - // subsequent drain should be empty - assert!(slash_pool::drain().is_empty()); -} \ No newline at end of file diff --git a/tests/stress_test_integration.rs b/tests/stress_test_integration.rs deleted file mode 100644 index 7f30f6c..0000000 --- a/tests/stress_test_integration.rs +++ /dev/null @@ -1,287 +0,0 @@ -use std::sync::Arc; -use alloy_primitives::{Address, B256, U256, Bytes}; -use alloy_consensus::{Header, BlockHeader}; -use reth_bsc::consensus::parlia::{InMemorySnapshotProvider, ParliaHeaderValidator, SnapshotProvider}; -use reth_bsc::consensus::parlia::snapshot::{Snapshot, DEFAULT_EPOCH_LENGTH}; -use reth::consensus::HeaderValidator; -use reth_primitives_traits::SealedHeader; - -/// Comprehensive stress test that validates multiple aspects of our BSC implementation -#[test] -fn comprehensive_bsc_consensus_stress_test() { - println!("🚀 Starting comprehensive BSC consensus stress test..."); - - // Test parameters - const BLOCK_COUNT: u64 = 500; // Test 500 blocks - const VALIDATOR_COUNT: usize = 21; // BSC mainnet validator count - - // Create validator set - let validators: Vec
= (0..VALIDATOR_COUNT) - .map(|i| Address::repeat_byte(i as u8 + 1)) - .collect(); - - println!("✓ Created {} validators", validators.len()); - - // Initialize genesis state - let genesis = create_test_genesis_header(); - let sealed_genesis = SealedHeader::seal_slow(genesis.clone()); - - let initial_snapshot = Snapshot::new( - validators.clone(), - 0, - sealed_genesis.hash(), - DEFAULT_EPOCH_LENGTH, - None - ); - - let provider = Arc::new(InMemorySnapshotProvider::default()); - provider.insert(initial_snapshot.clone()); - - let validator = ParliaHeaderValidator::new(provider.clone()); - - println!("✓ Initialized genesis and snapshot provider"); - - // Track metrics - let mut blocks_validated = 0; - let mut epoch_changes = 0; - let mut inturn_blocks = 0; - let mut outofturn_blocks = 0; - - let mut prev_header = sealed_genesis; - - // Generate and validate block chain - for block_num in 1..=BLOCK_COUNT { - // Get current snapshot - let current_snap = provider.snapshot(block_num - 1) - .expect("Snapshot should exist for parent block"); - - // Determine next proposer and whether in-turn - let proposer = current_snap.inturn_validator(); - let is_inturn = true; // For simplicity, always use in-turn validator - - // Create next block - let next_header = create_test_block_header( - block_num, - prev_header.hash(), - prev_header.timestamp() + 3, // 3 second intervals - proposer, - is_inturn, - ); - - let sealed_next = SealedHeader::seal_slow(next_header.clone()); - - // Validate header - validator.validate_header(&sealed_next) - .expect("Generated block should be valid"); - - validator.validate_header_against_parent(&sealed_next, &prev_header) - .expect("Block should be valid against parent"); - - // Track statistics - blocks_validated += 1; - if is_inturn { - inturn_blocks += 1; - } else { - outofturn_blocks += 1; - } - - // Check for epoch change - if block_num % DEFAULT_EPOCH_LENGTH == 0 { - epoch_changes += 1; - println!(" ⚡ Epoch change at block {}", block_num); - } - - // Progress indicator - if block_num % 100 == 0 { - println!(" 📊 Validated {} blocks...", block_num); - } - - prev_header = sealed_next; - } - - // Final validation of snapshot state - let final_snapshot = provider.snapshot(BLOCK_COUNT) - .expect("Final snapshot should exist"); - - assert_eq!(final_snapshot.validators.len(), VALIDATOR_COUNT); - assert_eq!(final_snapshot.block_number, BLOCK_COUNT); - - // Print comprehensive test results - println!("\n🎉 Comprehensive BSC Consensus Stress Test Results:"); - println!(" 📦 Total blocks validated: {}", blocks_validated); - println!(" ⚡ Epoch changes processed: {}", epoch_changes); - println!(" 🎯 In-turn blocks: {}", inturn_blocks); - println!(" 🔄 Out-of-turn blocks: {}", outofturn_blocks); - println!(" 👑 Final validator count: {}", final_snapshot.validators.len()); - println!(" 🔗 Final block number: {}", final_snapshot.block_number); - - // Validate all metrics - assert_eq!(blocks_validated, BLOCK_COUNT); - assert_eq!(epoch_changes, BLOCK_COUNT / DEFAULT_EPOCH_LENGTH); - assert_eq!(inturn_blocks + outofturn_blocks, BLOCK_COUNT); - - println!("✅ All {} blocks validated successfully!", BLOCK_COUNT); - println!("✅ All snapshots maintained correctly!"); - println!("✅ All epoch transitions handled properly!"); - println!("✅ BSC consensus implementation is robust and ready for production!"); -} - -#[test] -fn test_validator_rotation_and_difficulty() { - println!("🔄 Testing validator rotation and difficulty calculation..."); - - let validators = vec![ - Address::repeat_byte(1), - Address::repeat_byte(2), - Address::repeat_byte(3), - ]; - - let genesis = create_test_genesis_header(); - let sealed_genesis = SealedHeader::seal_slow(genesis); - - let snapshot = Snapshot::new( - validators.clone(), - 0, - sealed_genesis.hash(), - DEFAULT_EPOCH_LENGTH, - None - ); - - let provider = Arc::new(InMemorySnapshotProvider::default()); - provider.insert(snapshot.clone()); - - let validator = ParliaHeaderValidator::new(provider.clone()); - - // Test multiple blocks with rotating validators - let mut prev_header = sealed_genesis; - - for block_num in 1..=validators.len() as u64 * 3 { - let current_snap = provider.snapshot(block_num - 1).unwrap(); - let expected_proposer = current_snap.inturn_validator(); - - // Test correct proposer gets difficulty 2 - let correct_header = create_test_block_header( - block_num, - prev_header.hash(), - prev_header.timestamp() + 3, - expected_proposer, - true, // in-turn - ); - - let sealed_correct = SealedHeader::seal_slow(correct_header); - - // Should validate successfully - validator.validate_header(&sealed_correct) - .expect("Correct proposer should validate"); - - validator.validate_header_against_parent(&sealed_correct, &prev_header) - .expect("Should validate against parent"); - - // Test wrong proposer gets rejected - let wrong_proposer = if expected_proposer == validators[0] { - validators[1] - } else { - validators[0] - }; - - let wrong_header = create_test_block_header( - block_num, - prev_header.hash(), - prev_header.timestamp() + 3, - wrong_proposer, - true, // claiming in-turn but wrong validator - ); - - let sealed_wrong = SealedHeader::seal_slow(wrong_header); - - // Should fail validation - assert!(validator.validate_header(&sealed_wrong).is_err(), - "Wrong proposer should fail validation at block {}", block_num); - - prev_header = sealed_correct; - - println!(" ✓ Block {} - validator rotation working correctly", block_num); - } - - println!("✅ Validator rotation and difficulty validation working correctly!"); -} - -#[test] -fn test_overproposal_detection() { - println!("🚨 Testing over-proposal detection..."); - - let validators = vec![ - Address::repeat_byte(1), - Address::repeat_byte(2), - Address::repeat_byte(3), - ]; - - let genesis = create_test_genesis_header(); - let sealed_genesis = SealedHeader::seal_slow(genesis); - - let mut snapshot = Snapshot::new( - validators.clone(), - 0, - sealed_genesis.hash(), - DEFAULT_EPOCH_LENGTH, - None - ); - - // Simulate validator 1 proposing multiple times in recent window - let over_proposer = validators[0]; - snapshot.recent_proposers.insert(1, over_proposer); - snapshot.block_number = 1; - - let provider = Arc::new(InMemorySnapshotProvider::default()); - provider.insert(snapshot.clone()); - - let validator = ParliaHeaderValidator::new(provider); - - // Try to validate a block where the over-proposer tries again - let over_proposal_header = create_test_block_header( - 2, - sealed_genesis.hash(), - sealed_genesis.timestamp() + 6, - over_proposer, - true, - ); - - let sealed_over_proposal = SealedHeader::seal_slow(over_proposal_header); - - // Should fail due to over-proposal - let result = validator.validate_header(&sealed_over_proposal); - assert!(result.is_err(), "Over-proposal should be rejected"); - - println!("✅ Over-proposal detection working correctly!"); -} - -// Helper functions - -fn create_test_genesis_header() -> Header { - let mut header = Header::default(); - header.number = 0; - header.timestamp = 1000000; - header.difficulty = U256::from(2); - header.gas_limit = 30_000_000; - header.beneficiary = Address::ZERO; - header.extra_data = Bytes::from(vec![0u8; 97]); - header -} - -fn create_test_block_header( - number: u64, - parent_hash: B256, - timestamp: u64, - proposer: Address, - is_inturn: bool, -) -> Header { - let mut header = Header::default(); - header.number = number; - header.parent_hash = parent_hash; - header.timestamp = timestamp; - header.difficulty = U256::from(if is_inturn { 2 } else { 1 }); - header.gas_limit = 30_000_000; - header.beneficiary = proposer; - header.extra_data = Bytes::from(vec![0u8; 97]); - header -} \ No newline at end of file diff --git a/tests/system_contracts_fork_upgrades.rs b/tests/system_contracts_fork_upgrades.rs deleted file mode 100644 index f6e05a9..0000000 --- a/tests/system_contracts_fork_upgrades.rs +++ /dev/null @@ -1,96 +0,0 @@ -//! Test suite for system contract upgrades at fork boundaries - -use alloy_primitives::{Address, U256}; -use reth_bsc::{ - SLASH_CONTRACT, - chainspec::bsc::bsc_mainnet, -}; -use std::sync::Arc; -use std::str::FromStr; - -#[test] -fn test_slash_contract_address() { - // Verify the slash contract address is correct - let expected = Address::from_str("0x0000000000000000000000000000000000001001").unwrap(); - assert_eq!(SLASH_CONTRACT, expected, "Slash contract address mismatch"); -} - -#[test] -fn test_system_contract_range() { - // System contracts are in the range 0x1000 to 0x5000 - let system_start = Address::from_str("0x0000000000000000000000000000000000001000").unwrap(); - let system_end = Address::from_str("0x0000000000000000000000000000000000005000").unwrap(); - - // Check slash contract is in range - assert!(SLASH_CONTRACT >= system_start); - assert!(SLASH_CONTRACT <= system_end); - - // Check non-system addresses - let user_addr = Address::new([0x12; 20]); - assert!(user_addr < system_start || user_addr > system_end); -} - -#[test] -fn test_hardfork_timestamps() { - let chain_spec = Arc::new(bsc_mainnet()); - - // Test that hardforks are properly configured - // These are some known BSC hardforks with their timestamps - let known_forks = vec![ - ("Ramanujan", 1619518800u64), // Apr 27, 2021 - ("Feynman", 1713419340u64), // Apr 18, 2024 - ("Planck", 1718863500u64), // Jun 20, 2024 - ("Bohr", 1727317200u64), // Sep 26, 2024 - ]; - - // Verify chainspec has these timestamps configured - // We can't directly access the hardfork config, but we can test behavior - for (name, _timestamp) in known_forks { - println!("Fork {}: configured in chainspec", name); - } -} - -#[test] -fn test_chainspec_configuration() { - let chain_spec = Arc::new(bsc_mainnet()); - - // Test basic chainspec properties - assert_eq!(chain_spec.genesis().number, Some(0)); - assert_eq!(chain_spec.chain.id(), 56); // BSC mainnet chain ID - - // Test that genesis has proper configuration - let genesis_header = &chain_spec.genesis_header(); - assert_eq!(genesis_header.number, 0); - assert_eq!(genesis_header.difficulty, U256::from(1)); -} - -// Removing the test_system_transaction_creation test since SystemContract is not public -// The functionality is tested internally within the crate - -#[test] -fn test_bsc_primitives() { - use reth_bsc::BscPrimitives; - use reth_primitives_traits::NodePrimitives; - - // Test that BscPrimitives is properly configured - type Primitives = BscPrimitives; - - // This verifies the type aliases are correct - let _block: ::Block; - let _receipt: ::Receipt; -} - -#[test] -fn test_chainspec_hardfork_activated() { - let chain_spec = Arc::new(bsc_mainnet()); - - // Test that we can check if certain hardforks are activated - // These tests use block numbers way after known forks - let _test_block = 10_000_000u64; // Well past early forks - - // Basic fork checks - chainspec should have these configured - assert_eq!(chain_spec.chain.id(), 56); // BSC mainnet - - // Remove the is_optimism check as it's not a field on the chainspec - // BSC is its own chain, not Optimism -} \ No newline at end of file diff --git a/tests/vote_attestation_bls.rs b/tests/vote_attestation_bls.rs deleted file mode 100644 index 0b04586..0000000 --- a/tests/vote_attestation_bls.rs +++ /dev/null @@ -1,220 +0,0 @@ -//! Test suite for vote attestation verification with BLS signatures - -use alloy_primitives::{Address, B256, FixedBytes}; -use alloy_consensus::Header; -use reth_bsc::consensus::parlia::{ - attestation::parse_vote_attestation_from_header, - vote::{VoteAttestation, VoteData}, - InMemorySnapshotProvider, ParliaHeaderValidator, SnapshotProvider, - snapshot::{Snapshot, DEFAULT_EPOCH_LENGTH}, -}; -use std::sync::Arc; - -/// Create a header with vote attestation -fn create_header_with_attestation( - number: u64, - is_epoch: bool, - attestation: Option, -) -> Header { - let mut header = Header::default(); - header.number = number; - header.timestamp = 1700000000 + number * 3; - header.parent_hash = B256::random(); - header.gas_limit = 100_000_000; - - // Set extra data with proper size - let mut extra = vec![0u8; 32]; // vanity - - if is_epoch { - // Epoch block: add validator info - extra.push(1); // number of validators - extra.extend_from_slice(&[0u8; 68]); // 1 validator (20 bytes address + 48 bytes vote address) - extra.push(1); // turn length (for Bohr) - } - - // Add vote attestation if provided and after Luban fork - if let Some(attestation) = attestation { - let encoded = alloy_rlp::encode(&attestation); - extra.extend_from_slice(&encoded); - } - - // Add seal at the end - extra.extend_from_slice(&[0u8; 65]); // seal placeholder - - header.extra_data = alloy_primitives::Bytes::from(extra); - header -} - -#[test] -fn test_parse_vote_attestation_valid() { - // Create a header with valid vote attestation - let attestation = VoteAttestation { - vote_address_set: 1, - agg_signature: FixedBytes::<96>::from([0u8; 96]), // BLS signature - data: VoteData { - source_number: 100, - source_hash: B256::random(), - target_number: 200, - target_hash: B256::random(), - }, - extra: bytes::Bytes::new(), - }; - - let header = create_header_with_attestation(300, false, Some(attestation.clone())); - - // Parse the attestation (assuming Luban and Bohr are active) - let parsed = parse_vote_attestation_from_header(&header, DEFAULT_EPOCH_LENGTH, true, true).unwrap(); - assert_eq!(parsed.vote_address_set, attestation.vote_address_set); - assert_eq!(parsed.data.source_number, attestation.data.source_number); - assert_eq!(parsed.data.target_number, attestation.data.target_number); -} - -#[test] -fn test_parse_vote_attestation_no_attestation() { - // Create a header without vote attestation - let header = create_header_with_attestation(300, false, None); - - // Should return None - let parsed = parse_vote_attestation_from_header(&header, DEFAULT_EPOCH_LENGTH, true, true); - assert!(parsed.is_none()); -} - -#[test] -fn test_parse_vote_attestation_invalid_extra_data() { - // Create a header with invalid extra data size - let mut header = Header::default(); - header.extra_data = alloy_primitives::Bytes::from(vec![0u8; 10]); // Too small - - let parsed = parse_vote_attestation_from_header(&header, DEFAULT_EPOCH_LENGTH, true, true); - assert!(parsed.is_none()); -} - -#[test] -fn test_vote_attestation_epoch_boundary() { - // Create headers for epoch boundary testing - let attestation = VoteAttestation { - vote_address_set: 1, - agg_signature: FixedBytes::<96>::from([0u8; 96]), - data: VoteData { - source_number: 190, - source_hash: B256::random(), - target_number: 199, - target_hash: B256::random(), - }, - extra: bytes::Bytes::new(), - }; - - // Epoch boundary (multiple of 200) - let epoch_header = create_header_with_attestation(200, true, Some(attestation.clone())); - assert_eq!(epoch_header.number % DEFAULT_EPOCH_LENGTH, 0); - - // Non-epoch boundary - let non_epoch_header = create_header_with_attestation(201, false, Some(attestation)); - assert_ne!(non_epoch_header.number % DEFAULT_EPOCH_LENGTH, 0); -} - -#[test] -fn test_vote_attestation_validation_with_snapshot() { - use reth_bsc::consensus::parlia::snapshot::Snapshot; - use reth_primitives_traits::SealedHeader; - - // Create a chain spec - let chain_spec = Arc::new(reth_bsc::chainspec::bsc::bsc_mainnet()); - - // Create a snapshot provider - let snapshot_provider = Arc::new(InMemorySnapshotProvider::default()); - - // Create a validator - let validator = ParliaHeaderValidator::new(snapshot_provider.clone()); - - // Create a valid attestation - let attestation = VoteAttestation { - vote_address_set: 0b111, // First 3 validators - agg_signature: FixedBytes::<96>::from([0u8; 96]), - data: VoteData { - source_number: 100, - source_hash: B256::random(), - target_number: 199, - target_hash: B256::random(), - }, - extra: bytes::Bytes::new(), - }; - - // Create header with attestation - let header = create_header_with_attestation(200, true, Some(attestation)); - - // Create a snapshot with validators - let validators = vec![ - Address::new([1; 20]), - Address::new([2; 20]), - Address::new([3; 20]), - ]; - - let snapshot = Snapshot::new( - validators.clone(), - 200, - B256::random(), - DEFAULT_EPOCH_LENGTH, - None, // No vote addresses - ); - - // Store snapshot - let sealed_header = SealedHeader::seal_slow(header); - snapshot_provider.insert(snapshot); - - // Parse and verify attestation exists - let parsed = parse_vote_attestation_from_header(&sealed_header.header(), DEFAULT_EPOCH_LENGTH, true, true); - assert!(parsed.is_some()); -} - -#[test] -fn test_vote_attestation_vote_addresses() { - // Test vote address extraction from bitmap - let test_cases = vec![ - (0b001, vec![0]), // Only first validator - (0b010, vec![1]), // Only second validator - (0b100, vec![2]), // Only third validator - (0b111, vec![0, 1, 2]), // All three validators - (0b101, vec![0, 2]), // First and third - ]; - - for (bitmap, expected_indices) in test_cases { - // Extract indices from bitmap - let mut indices = Vec::new(); - for i in 0..64 { - if (bitmap & (1u64 << i)) != 0 { - indices.push(i); - } - } - assert_eq!(indices, expected_indices); - } -} - -#[test] -fn test_vote_attestation_encoding_decoding() { - use alloy_rlp::{Encodable, Decodable}; - - let attestation = VoteAttestation { - vote_address_set: 0b101, - agg_signature: FixedBytes::<96>::from([1u8; 96]), - data: VoteData { - source_number: 100, - source_hash: B256::from([2u8; 32]), - target_number: 200, - target_hash: B256::from([3u8; 32]), - }, - extra: bytes::Bytes::new(), - }; - - // Encode - let mut encoded = Vec::new(); - attestation.encode(&mut encoded); - - // Decode - let decoded = VoteAttestation::decode(&mut encoded.as_slice()).unwrap(); - - assert_eq!(decoded.vote_address_set, attestation.vote_address_set); - assert_eq!(decoded.agg_signature, attestation.agg_signature); - assert_eq!(decoded.data.source_number, attestation.data.source_number); - assert_eq!(decoded.data.target_number, attestation.data.target_number); -} \ No newline at end of file