Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Functional tests for floresta-wire/sync-node #200

Merged
merged 1 commit into from
Aug 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions crates/floresta-wire/src/p2p_wire/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -97,3 +97,6 @@ pub mod running_node;
pub mod socks;
pub mod stream_reader;
pub mod sync_node;
#[cfg(test)]
#[doc(hidden)]
pub mod tests;
lla-dane marked this conversation as resolved.
Show resolved Hide resolved
1 change: 1 addition & 0 deletions crates/floresta-wire/src/p2p_wire/tests/mod.rs
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
mod sync_node;
mod utils;
170 changes: 170 additions & 0 deletions crates/floresta-wire/src/p2p_wire/tests/sync_node.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
#[cfg(test)]
mod tests_utils {
use std::collections::HashMap;
use std::mem::ManuallyDrop;
use std::sync::Arc;
use std::time::Duration;

use async_std::future;
use async_std::sync::RwLock;
use bitcoin::block::Header;
use bitcoin::BlockHash;
use floresta_chain::pruned_utreexo::UpdatableChainstate;
use floresta_chain::AssumeValidArg;
use floresta_chain::ChainState;
use floresta_chain::KvChainStore;
use floresta_chain::UtreexoBlock;

use crate::mempool::Mempool;
use crate::node::UtreexoNode;
use crate::p2p_wire::sync_node::SyncNode;
use crate::p2p_wire::tests::utils::create_peer;
use crate::p2p_wire::tests::utils::get_node_config;
use crate::p2p_wire::tests::utils::get_test_headers;

pub async fn setup_node(
peers: Vec<(
Vec<Header>,
HashMap<BlockHash, UtreexoBlock>,
HashMap<BlockHash, Vec<u8>>,
)>,
pow_fraud_proofs: bool,
network: floresta_chain::Network,
) -> Arc<ChainState<KvChainStore<'static>>> {
let datadir = format!("./data/{}.sync_node", rand::random::<u32>());
let chainstore = KvChainStore::new(datadir.clone()).unwrap();
let mempool = Arc::new(RwLock::new(Mempool::new()));
let chain = ChainState::new(chainstore, network, AssumeValidArg::Disabled);
let chain = Arc::new(chain);

// Adding 9 signet headers in the chain-state prior validation
let mut headers = get_test_headers();
headers.remove(0);
headers.truncate(9);
for header in headers {
chain.accept_header(header).unwrap();
}

let config = get_node_config(datadir, network, pow_fraud_proofs);

let mut node = UtreexoNode::<SyncNode, Arc<ChainState<KvChainStore>>>::new(
config,
chain.clone(),
mempool,
None,
);

for (i, peer) in peers.into_iter().enumerate() {
let (sender, receiver) = async_std::channel::bounded(10);
let peer = create_peer(
peer.0,
peer.1,
peer.2,
node.node_tx.clone(),
sender.clone(),
receiver,
i as u32,
);

let _peer = peer.clone();

node.peers.insert(i as u32, peer);
}
let mut node = ManuallyDrop::new(Box::new(node));

let kill_signal = Arc::new(RwLock::new(false));
// FIXME: This doesn't look very safe, but we need to coerce a &mut reference of the node
// to live for the static lifetime, or it can't be spawn-ed by async-std::task
let _node: &'static mut UtreexoNode<SyncNode, Arc<ChainState<KvChainStore>>> =
unsafe { std::mem::transmute(&mut **node) };

future::timeout(Duration::from_secs(10), _node.run(kill_signal, |_| {}))
.await
.unwrap();

chain
}
}

#[cfg(test)]
mod tests {
use std::collections::HashMap;

use floresta_chain::pruned_utreexo::BlockchainInterface;
use floresta_chain::UtreexoBlock;

use crate::p2p_wire::tests::sync_node::tests_utils::setup_node;
use crate::p2p_wire::tests::utils::get_essentials;

#[async_std::test]
async fn test_sync_valid_blocks() {
let (headers, blocks, _, _, _) = get_essentials();
let chain = setup_node(
vec![(Vec::new(), blocks.clone(), HashMap::new())],
false,
floresta_chain::Network::Signet,
)
.await;

assert_eq!(chain.get_validation_index().unwrap(), 9);
assert_eq!(chain.get_best_block().unwrap().1, headers[9].block_hash());
assert_eq!(chain.is_in_idb(), false);
}

#[async_std::test]
async fn test_sync_invalid_block() {
// 7th BLOCK IS SET AS INVALID. WHILE CONNECTING THE BLOCKS, 7th BLOCK WILL BE INVALIDATED.
// HENCE THE CHAIN WILL HAVE A HEIGHT OF 6.

// THIS SIMULATION WILL TEST:
// 1) SENDING BLOCK WITH A BADMERKLEROOT: 7TH BLOCK WILL BE INVALIDATED.

let (headers, mut blocks, _, _, invalid_block) = get_essentials();
blocks.insert(headers[7].block_hash(), invalid_block);

let peer = vec![(Vec::new(), blocks.clone(), HashMap::new())];
let chain = setup_node(peer, false, floresta_chain::Network::Signet).await;

assert_eq!(chain.get_validation_index().unwrap(), 6);
assert_eq!(chain.get_best_block().unwrap().1, headers[6].block_hash());
assert_eq!(chain.is_in_idb(), false);
}

#[async_std::test]
async fn test_sync_block_without_udata() {
// THIS SIMUATION WILL TEST 2 THINGS:
//
// 1) SENDING IN THE 3RD BLOCK WITHOUT PROOF: THIS WILL BANN THE PEER
//
// 2) SENDING BLOCKS OUT OF ORDER: AFTER ALL THE BLOCKS ARE RECEIVED FROM THE DISHONEST
// PEER, WE WILL AGAIN REQUEST FOR THE 3RD BLOCK TO A RANDOM PEER. THE SYNC-NODE WILL
// HANDLE IT FINE.
//
// SO FINALLY THE LAST VALIDATED BLOCK WILL BE 9.

let (headers, mut blocks, _, _, _) = get_essentials();
let v_blocks = blocks.clone();

let u_block = blocks.get(&headers[3].block_hash().clone()).unwrap();
let block = UtreexoBlock {
block: u_block.block.clone(),
udata: None,
};
blocks.insert(headers[3].block_hash(), block);

let liar = (Vec::new(), blocks, HashMap::new());
let honest1 = (Vec::new(), v_blocks.clone(), HashMap::new());
let honest2 = (Vec::new(), v_blocks, HashMap::new());

let chain = setup_node(
vec![liar, honest1, honest2],
false,
floresta_chain::Network::Signet,
)
.await;

assert_eq!(chain.get_validation_index().unwrap(), 9);
assert_eq!(chain.get_best_block().unwrap().1, headers[9].block_hash());
assert_eq!(chain.is_in_idb(), false);
}
}
5 changes: 4 additions & 1 deletion crates/floresta-wire/src/p2p_wire/tests/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,7 @@ pub fn get_node_config(
proxy: None,
assume_utreexo: None,
backfill: false,
filter_start_height: None,
}
}

Expand Down Expand Up @@ -262,15 +263,17 @@ pub fn get_essentials() -> (
HashMap<BlockHash, UtreexoBlock>,
HashMap<BlockHash, Vec<u8>>,
BlockHash,
UtreexoBlock,
) {
let headers = get_test_headers();
let blocks = get_test_blocks().unwrap();
let true_filters = get_test_filters().unwrap();
let invalid_block = generate_invalid_block();

// BlockHash of chain_tip: 0000035f0e5513b26bba7cead874fdf06241a934e4bc4cf7a0381c60e4cdd2bb (119)
let tip_hash =
BlockHash::from_str("0000035f0e5513b26bba7cead874fdf06241a934e4bc4cf7a0381c60e4cdd2bb")
.unwrap();

(headers, blocks, true_filters, tip_hash)
(headers, blocks, true_filters, tip_hash, invalid_block)
}
Loading