blvm-node 0.1.5

Bitcoin Commons BLVM: Minimal Bitcoin node implementation using blvm-protocol and blvm-consensus
//! Stress tests for block processor (concurrent processing, reorganizations)

use blvm_node::node::block_processor::{
    parse_block_from_wire, prepare_block_validation_context, store_block_with_context,
    validate_block_with_context,
};
use blvm_node::storage::Storage;
use blvm_node::{Block, BlockHeader, Hash};
use blvm_protocol::{segwit::Witness, BitcoinProtocolEngine, ProtocolVersion, UtxoSet};
use std::sync::Arc;
use tempfile::TempDir;

fn create_test_block(height: u64) -> Block {
    let mut prev_hash = [0u8; 32];
    prev_hash[0] = (height % 256) as u8;

    Block {
        header: BlockHeader {
            version: 1,
            prev_block_hash: prev_hash,
            merkle_root: [0u8; 32],
            timestamp: 1231006505 + height,
            bits: 0x1d00ffff,
            nonce: 0,
        },
        transactions: vec![].into_boxed_slice(),
    }
}

fn create_test_storage() -> (TempDir, Arc<Storage>) {
    let temp_dir = TempDir::new().unwrap();
    let storage = Arc::new(Storage::new(temp_dir.path()).unwrap());
    (temp_dir, storage)
}

#[tokio::test]
async fn test_concurrent_block_processing() {
    let (_temp_dir, storage) = create_test_storage();
    let protocol = Arc::new(BitcoinProtocolEngine::new(ProtocolVersion::BitcoinV1).unwrap());
    let blockstore = storage.blocks();

    // Create multiple blocks
    let blocks: Vec<_> = (0..10).map(|i| create_test_block(i)).collect();

    // Process blocks concurrently
    let mut handles = vec![];
    for (i, block) in blocks.iter().enumerate() {
        let blockstore_clone = Arc::clone(&blockstore);
        let protocol_clone = Arc::clone(&protocol);
        let block_clone = block.clone();
        let height = i as u64;

        handles.push(tokio::spawn(async move {
            let mut utxo_set = UtxoSet::default();
            let witnesses = vec![];

            // Store block
            store_block_with_context(&*blockstore_clone, &block_clone, &witnesses, height).ok()?;

            // Validate block
            validate_block_with_context(
                &*blockstore_clone,
                &protocol_clone,
                &block_clone,
                &witnesses,
                &mut utxo_set,
                height,
            )
            .ok()
        }));
    }

    // Wait for all blocks to be processed
    let results: Vec<_> = futures::future::join_all(handles).await;

    // All blocks should be processed (some may fail validation, which is OK)
    assert_eq!(results.len(), 10);
    // Verify all tasks completed (results may be Ok or Err, but tasks should complete)
    for result in results {
        let _ = result;
    }
}

#[tokio::test]
async fn test_block_processing_with_invalid_sequence() {
    let (_temp_dir, storage) = create_test_storage();
    let protocol = Arc::new(BitcoinProtocolEngine::new(ProtocolVersion::BitcoinV1).unwrap());
    let blockstore = storage.blocks();

    // Create blocks out of order
    let block1 = create_test_block(1);
    let block0 = create_test_block(0);

    let mut utxo_set = UtxoSet::default();
    let witnesses = vec![];

    // Try to process block 1 before block 0
    let result1 = store_block_with_context(&*blockstore, &block1, &witnesses, 1);
    let result2 = store_block_with_context(&*blockstore, &block0, &witnesses, 0);

    // Both should succeed (storage doesn't enforce ordering)
    // Validation will catch invalid sequences
    let _ = result1;
    let _ = result2;

    // Validation should handle invalid sequences gracefully
    let validation_result = validate_block_with_context(
        &*blockstore,
        &protocol,
        &block1,
        &witnesses,
        &mut utxo_set,
        1,
    );

    // May fail validation due to missing parent, which is expected
    let _ = validation_result;
}

#[tokio::test]
async fn test_block_processing_reorganization_scenario() {
    let (_temp_dir, storage) = create_test_storage();
    let protocol = Arc::new(BitcoinProtocolEngine::new(ProtocolVersion::BitcoinV1).unwrap());
    let blockstore = storage.blocks();

    // Create a chain: block0 -> block1 -> block2
    let block0 = create_test_block(0);
    let mut block1 = create_test_block(1);
    block1.header.prev_block_hash = blockstore.get_block_hash(&block0);
    let mut block2 = create_test_block(2);
    block2.header.prev_block_hash = blockstore.get_block_hash(&block1);

    // Create an alternative chain: block0 -> block1_alt -> block2_alt
    let mut block1_alt = create_test_block(1);
    block1_alt.header.prev_block_hash = blockstore.get_block_hash(&block0);
    block1_alt.header.nonce = 1; // Different nonce to make it different
    let mut block2_alt = create_test_block(2);
    block2_alt.header.prev_block_hash = blockstore.get_block_hash(&block1_alt);

    let mut utxo_set = UtxoSet::default();
    let witnesses = vec![];

    // Process first chain
    store_block_with_context(&*blockstore, &block0, &witnesses, 0).unwrap();
    store_block_with_context(&*blockstore, &block1, &witnesses, 1).unwrap();
    store_block_with_context(&*blockstore, &block2, &witnesses, 2).unwrap();

    // Process alternative chain (reorganization)
    store_block_with_context(&*blockstore, &block1_alt, &witnesses, 1).unwrap();
    store_block_with_context(&*blockstore, &block2_alt, &witnesses, 2).unwrap();

    // Both chains should be stored (reorganization handling is at higher level)
    // This test verifies storage can handle multiple blocks at same height
    assert!(true);
}

#[tokio::test]
async fn test_block_processing_large_block() {
    let (_temp_dir, storage) = create_test_storage();
    let protocol = Arc::new(BitcoinProtocolEngine::new(ProtocolVersion::BitcoinV1).unwrap());
    let blockstore = storage.blocks();

    // Create a block with many transactions (simulated with empty transactions)
    let mut block = create_test_block(0);
    // Note: In real scenario, this would have many transactions
    // For now, we just test that large blocks are handled

    let mut utxo_set = UtxoSet::default();
    let witnesses = vec![];

    // Store and validate large block
    let store_result = store_block_with_context(&*blockstore, &block, &witnesses, 0);
    assert!(store_result.is_ok());

    let validation_result = validate_block_with_context(
        &*blockstore,
        &protocol,
        &block,
        &witnesses,
        &mut utxo_set,
        0,
    );

    // Should handle large blocks gracefully
    let _ = validation_result;
}

#[tokio::test]
async fn test_block_processing_rapid_blocks() {
    let (_temp_dir, storage) = create_test_storage();
    let blockstore = storage.blocks();

    // Process many blocks rapidly
    let mut handles = vec![];
    for i in 0..50 {
        let blockstore_clone = Arc::clone(&blockstore);
        let block = create_test_block(i);
        let witnesses = vec![];

        handles.push(tokio::spawn(async move {
            store_block_with_context(&*blockstore_clone, &block, &witnesses, i).ok()
        }));
    }

    // Wait for all blocks
    let results: Vec<_> = futures::future::join_all(handles).await;

    // All should complete
    assert_eq!(results.len(), 50);
}