synchronization_chain.rs 47.7 KiB
Newer Older
Svyatoslav Nikolsky's avatar
Svyatoslav Nikolsky committed
		]), HeadersIntersection::DbAllBlocksKnown);
		assert_eq!(chain.intersect_with_blocks_headers(&vec![
			test_data::block_h2().hash(),
Svyatoslav Nikolsky's avatar
Svyatoslav Nikolsky committed
			hashes1[0].clone(),
		], &vec![
			test_data::block_h2().block_header,
			headers1[0].clone(),
		]), HeadersIntersection::DbForkNewBlocks(1));

	#[test]
	fn chain_transaction_state() {
		let mut chain = Chain::new(Arc::new(db::TestStorage::with_genesis_block()));
		let genesis_block = test_data::genesis();
		let block1 = test_data::block_h1();
		let tx1: Transaction = test_data::TransactionBuilder::with_version(1).into();
		let tx2: Transaction = test_data::TransactionBuilder::with_version(2).into();
		let tx1_hash = tx1.hash();
		let tx2_hash = tx2.hash();
		chain.verify_transaction(tx1_hash.clone(), tx1);
		chain.insert_verified_transaction(tx2);

		assert_eq!(chain.transaction_state(&genesis_block.transactions[0].hash()), TransactionState::Stored);
		assert_eq!(chain.transaction_state(&block1.transactions[0].hash()), TransactionState::Unknown);
		assert_eq!(chain.transaction_state(&tx1_hash), TransactionState::Verifying);
		assert_eq!(chain.transaction_state(&tx2_hash), TransactionState::InMemory);
	}

	#[test]
	fn chain_block_transaction_is_removed_from_on_block_insert() {
		let b0 = test_data::block_builder().header().build().build();
		let b1 = test_data::block_builder().header().parent(b0.hash()).build()
			.transaction().coinbase()
				.output().value(10).build()
				.build()
			.transaction()
				.input().hash(H256::from(1)).index(1).build()
				.build()
			.build();
		let tx1 = b1.transactions[0].clone();
		let tx1_hash = tx1.hash();
		let tx2 = b1.transactions[1].clone();
		let tx2_hash = tx2.hash();

		let mut chain = Chain::new(Arc::new(db::TestStorage::with_blocks(&vec![b0])));
		chain.verify_transaction(tx1_hash.clone(), tx1);
		chain.insert_verified_transaction(tx2);

		// only one transaction is in the memory pool
		assert_eq!(chain.information().transactions.transactions_count, 1);

		// when block is inserted to the database => all accepted transactions are removed from mempool && verifying queue
NikVolf's avatar
NikVolf committed
		chain.insert_best_block(b1.hash(), &b1.into()).expect("block accepted");

		assert_eq!(chain.information().transactions.transactions_count, 0);
		assert!(!chain.forget_verifying_transaction(&tx1_hash));
		assert!(!chain.forget_verifying_transaction(&tx2_hash));
	}

	#[test]
	fn chain_forget_verifying_transaction_with_children() {
		let test_chain = &mut test_data::ChainBuilder::new();
		test_data::TransactionBuilder::with_output(100).store(test_chain)	// t1
			.into_input(0).add_output(200).store(test_chain)				// t1 -> t2
			.into_input(0).add_output(300).store(test_chain)				// t1 -> t2 -> t3
			.set_default_input(0).set_output(400).store(test_chain);		// t4

		let mut chain = Chain::new(Arc::new(db::TestStorage::with_genesis_block()));
		chain.verify_transaction(test_chain.at(0).hash(), test_chain.at(0));
		chain.verify_transaction(test_chain.at(1).hash(), test_chain.at(1));
		chain.verify_transaction(test_chain.at(2).hash(), test_chain.at(2));
		chain.verify_transaction(test_chain.at(3).hash(), test_chain.at(3));

		chain.forget_verifying_transaction_with_children(&test_chain.at(0).hash());
		assert!(!chain.forget_verifying_transaction(&test_chain.at(0).hash()));
		assert!(!chain.forget_verifying_transaction(&test_chain.at(1).hash()));
		assert!(!chain.forget_verifying_transaction(&test_chain.at(2).hash()));
		assert!(chain.forget_verifying_transaction(&test_chain.at(3).hash()));
	}

	#[test]
	fn chain_transactions_hashes_with_state() {
		let test_chain = &mut test_data::ChainBuilder::new();
		test_data::TransactionBuilder::with_output(100).store(test_chain)	// t1
			.into_input(0).add_output(200).store(test_chain)				// t1 -> t2
			.into_input(0).add_output(300).store(test_chain)				// t1 -> t2 -> t3
			.set_default_input(0).set_output(400).store(test_chain);		// t4

		let mut chain = Chain::new(Arc::new(db::TestStorage::with_genesis_block()));
		chain.insert_verified_transaction(test_chain.at(0));
		chain.insert_verified_transaction(test_chain.at(1));
		chain.insert_verified_transaction(test_chain.at(2));
		chain.insert_verified_transaction(test_chain.at(3));

		let chain_transactions = chain.transactions_hashes_with_state(TransactionState::InMemory);
		assert!(chain_transactions.contains(&test_chain.at(0).hash()));
		assert!(chain_transactions.contains(&test_chain.at(1).hash()));
		assert!(chain_transactions.contains(&test_chain.at(2).hash()));
		assert!(chain_transactions.contains(&test_chain.at(3).hash()));
	}
Svyatoslav Nikolsky's avatar
Svyatoslav Nikolsky committed
	fn memory_pool_transactions_are_reverified_after_reorganization() {
		let b0 = test_data::block_builder().header().build().build();
		let b1 = test_data::block_builder().header().nonce(1).parent(b0.hash()).build().build();
		let b2 = test_data::block_builder().header().nonce(2).parent(b0.hash()).build().build();
		let b3 = test_data::block_builder().header().parent(b2.hash()).build().build();

		let tx1: Transaction = test_data::TransactionBuilder::with_version(1).into();
		let tx1_hash = tx1.hash();
		let tx2: Transaction = test_data::TransactionBuilder::with_version(2).into();
		let tx2_hash = tx2.hash();

		let path = RandomTempPath::create_dir();
		let storage = Arc::new(db::Storage::new(path.as_path()).unwrap());
		storage.insert_block(&b0).expect("no db error");

		let mut chain = Chain::new(storage);
		chain.verify_transaction(tx1_hash.clone(), tx1);
		chain.insert_verified_transaction(tx2);

		// no reorg
NikVolf's avatar
NikVolf committed
		let result = chain.insert_best_block(b1.hash(), &b1.into()).expect("no error");
		assert_eq!(result.transactions_to_reverify.len(), 0);

		// no reorg
NikVolf's avatar
NikVolf committed
		let result = chain.insert_best_block(b2.hash(), &b2.into()).expect("no error");
		assert_eq!(result.transactions_to_reverify.len(), 0);

		// reorg
NikVolf's avatar
NikVolf committed
		let result = chain.insert_best_block(b3.hash(), &b3.into()).expect("no error");
		assert_eq!(result.transactions_to_reverify.len(), 2);
		assert!(result.transactions_to_reverify.iter().any(|&(ref h, _)| h == &tx1_hash));
		assert!(result.transactions_to_reverify.iter().any(|&(ref h, _)| h == &tx2_hash));
	}

	#[test]
	fn fork_chain_block_transaction_is_removed_from_on_block_insert() {
		let genesis = test_data::genesis();
		let b0 = test_data::block_builder().header().parent(genesis.hash()).build().build(); // genesis -> b0
		let b1 = test_data::block_builder().header().nonce(1).parent(b0.hash()).build()
			.transaction().output().value(10).build().build()
			.build(); // genesis -> b0 -> b1[tx1]
		let b2 = test_data::block_builder().header().parent(b1.hash()).build()
			.transaction().output().value(20).build().build()
			.build(); // genesis -> b0 -> b1[tx1] -> b2[tx2]
		let b3 = test_data::block_builder().header().nonce(2).parent(b0.hash()).build()
			.transaction().output().value(30).build().build()
			.build(); // genesis -> b0 -> b3[tx3]
		let b4 = test_data::block_builder().header().parent(b3.hash()).build()
			.transaction().output().value(40).build().build()
			.build(); // genesis -> b0 -> b3[tx3] -> b4[tx4]
		let b5 = test_data::block_builder().header().parent(b4.hash()).build()
			.transaction().output().value(50).build().build()
			.build(); // genesis -> b0 -> b3[tx3] -> b4[tx4] -> b5[tx5]

		let tx1 = b1.transactions[0].clone();
		let tx1_hash = tx1.hash();
		let tx2 = b2.transactions[0].clone();
		let tx2_hash = tx2.hash();
		let tx3 = b3.transactions[0].clone();
		let tx4 = b4.transactions[0].clone();
		let tx5 = b5.transactions[0].clone();

		let path = RandomTempPath::create_dir();
		let storage = Arc::new(db::Storage::new(path.as_path()).unwrap());
		storage.insert_block(&genesis).expect("no db error");

		let mut chain = Chain::new(storage);

		chain.insert_verified_transaction(tx3);
		chain.insert_verified_transaction(tx4);
		chain.insert_verified_transaction(tx5);

NikVolf's avatar
NikVolf committed
		assert_eq!(chain.insert_best_block(b0.hash(), &b0.clone().into()).expect("block accepted"), BlockInsertionResult::with_canonized_blocks(vec![b0.hash()]));
		assert_eq!(chain.information().transactions.transactions_count, 3);
NikVolf's avatar
NikVolf committed
		assert_eq!(chain.insert_best_block(b1.hash(), &b1.clone().into()).expect("block accepted"), BlockInsertionResult::with_canonized_blocks(vec![b1.hash()]));
		assert_eq!(chain.information().transactions.transactions_count, 3);
NikVolf's avatar
NikVolf committed
		assert_eq!(chain.insert_best_block(b2.hash(), &b2.clone().into()).expect("block accepted"), BlockInsertionResult::with_canonized_blocks(vec![b2.hash()]));
		assert_eq!(chain.information().transactions.transactions_count, 3);
NikVolf's avatar
NikVolf committed
		assert_eq!(chain.insert_best_block(b3.hash(), &b3.clone().into()).expect("block accepted"), BlockInsertionResult::default());
		assert_eq!(chain.information().transactions.transactions_count, 3);
NikVolf's avatar
NikVolf committed
		assert_eq!(chain.insert_best_block(b4.hash(), &b4.clone().into()).expect("block accepted"), BlockInsertionResult::default());
		assert_eq!(chain.information().transactions.transactions_count, 3);
		// order matters
NikVolf's avatar
NikVolf committed
		let insert_result = chain.insert_best_block(b5.hash(), &b5.clone().into()).expect("block accepted");
		let transactions_to_reverify_hashes: Vec<_> = insert_result
			.transactions_to_reverify
			.into_iter()
			.map(|(h, _)| h)
			.collect();
		assert_eq!(transactions_to_reverify_hashes, vec![tx1_hash, tx2_hash]);
		assert_eq!(insert_result.canonized_blocks_hashes, vec![b3.hash(), b4.hash(), b5.hash()]);
		assert_eq!(chain.information().transactions.transactions_count, 0); // tx3, tx4, tx5 are added to the database
	}

	#[test]
	fn double_spend_transaction_is_removed_from_memory_pool_when_output_is_spent_in_block_transaction() {
		let genesis = test_data::genesis();
		let tx0 = genesis.transactions[0].clone();
		let b0 = test_data::block_builder().header().nonce(1).parent(genesis.hash()).build()
			.transaction()
				.lock_time(1)
				.input().hash(tx0.hash()).index(0).build()
				.build()
			.build(); // genesis -> b0[tx1]
		// tx1 && tx2 are spending same output
		let tx2: Transaction = test_data::TransactionBuilder::with_output(20).add_input(&tx0, 0).into();
		let tx3: Transaction = test_data::TransactionBuilder::with_output(20).add_input(&tx0, 1).into();

		// insert tx2 to memory pool
		let mut chain = Chain::new(Arc::new(db::TestStorage::with_genesis_block()));
		chain.insert_verified_transaction(tx2.clone());
		chain.insert_verified_transaction(tx3.clone());
		// insert verified block with tx1
		chain.insert_best_block(b0.hash(), &b0.into()).expect("no error");
		// => tx2 is removed from memory pool, but tx3 remains
		assert_eq!(chain.information().transactions.transactions_count, 1);
	}

	#[test]
	fn chain_dead_end() {
		let mut chain = Chain::new(Arc::new(db::TestStorage::with_genesis_block()));

		let blocks = test_data::build_n_empty_blocks_from(5, 0, &test_data::genesis().block_header);
		let headers: Vec<_> = blocks.iter().map(|b| b.block_header.clone()).collect();
		let hashes: Vec<_> = headers.iter().map(|h| h.hash()).collect();

		chain.insert_best_block(blocks[0].hash(), &blocks[0].clone().into()).expect("no error");
		chain.insert_best_block(blocks[1].hash(), &blocks[1].clone().into()).expect("no error");
		chain.mark_dead_end_block(&blocks[2].hash());

		assert_eq!(chain.intersect_with_blocks_headers(&vec![
			hashes[0].clone(),
			hashes[1].clone(),
			hashes[2].clone(),
			hashes[3].clone(),
			hashes[4].clone(),
		], &vec![
			headers[0].clone(),
			headers[1].clone(),
			headers[2].clone(),
			headers[3].clone(),
			headers[4].clone(),
		]), HeadersIntersection::DeadEnd(2));

		assert_eq!(chain.intersect_with_blocks_headers(&vec![
			hashes[2].clone(),
			hashes[3].clone(),
			hashes[4].clone(),
		], &vec![
			headers[2].clone(),
			headers[3].clone(),
			headers[4].clone(),
		]), HeadersIntersection::DeadEnd(0));

		assert_eq!(chain.intersect_with_blocks_headers(&vec![
			hashes[3].clone(),
			hashes[4].clone(),
		], &vec![
			headers[3].clone(),
			headers[4].clone(),
		]), HeadersIntersection::DeadEnd(0));
	}