Cleanup db directory after tests (#2752)
* Cleanup db directory after tests * Fix clean output dir windows * Remove behind chain tests
This commit is contained in:
committed by
Ignotus Peverell
parent
e8c50359e4
commit
606b4652f8
@@ -34,87 +34,93 @@ fn test_transaction_pool_block_building() {
|
||||
|
||||
let db_root = ".grin_block_building".to_string();
|
||||
clean_output_dir(db_root.clone());
|
||||
let mut chain = ChainAdapter::init(db_root.clone()).unwrap();
|
||||
|
||||
let verifier_cache = Arc::new(RwLock::new(LruVerifierCache::new()));
|
||||
|
||||
// Initialize the chain/txhashset with an initial block
|
||||
// so we have a non-empty UTXO set.
|
||||
let add_block = |prev_header: BlockHeader, txs: Vec<Transaction>, chain: &mut ChainAdapter| {
|
||||
let height = prev_header.height + 1;
|
||||
let key_id = ExtKeychain::derive_key_id(1, height as u32, 0, 0, 0);
|
||||
let fee = txs.iter().map(|x| x.fee()).sum();
|
||||
let reward = libtx::reward::output(&keychain, &key_id, fee).unwrap();
|
||||
let mut block = Block::new(&prev_header, txs, Difficulty::min(), reward).unwrap();
|
||||
|
||||
// Set the prev_root to the prev hash for testing purposes (no MMR to obtain a root from).
|
||||
block.header.prev_root = prev_header.hash();
|
||||
|
||||
chain.update_db_for_block(&block);
|
||||
block
|
||||
};
|
||||
|
||||
let block = add_block(BlockHeader::default(), vec![], &mut chain);
|
||||
let header = block.header;
|
||||
|
||||
// Now create tx to spend that first coinbase (now matured).
|
||||
// Provides us with some useful outputs to test with.
|
||||
let initial_tx = test_transaction_spending_coinbase(&keychain, &header, vec![10, 20, 30, 40]);
|
||||
|
||||
// Mine that initial tx so we can spend it with multiple txs
|
||||
let block = add_block(header, vec![initial_tx], &mut chain);
|
||||
let header = block.header;
|
||||
|
||||
// Initialize a new pool with our chain adapter.
|
||||
let pool = RwLock::new(test_setup(Arc::new(chain.clone()), verifier_cache));
|
||||
|
||||
let root_tx_1 = test_transaction(&keychain, vec![10, 20], vec![24]);
|
||||
let root_tx_2 = test_transaction(&keychain, vec![30], vec![28]);
|
||||
let root_tx_3 = test_transaction(&keychain, vec![40], vec![38]);
|
||||
|
||||
let child_tx_1 = test_transaction(&keychain, vec![24], vec![22]);
|
||||
let child_tx_2 = test_transaction(&keychain, vec![38], vec![32]);
|
||||
|
||||
{
|
||||
let mut write_pool = pool.write();
|
||||
let mut chain = ChainAdapter::init(db_root.clone()).unwrap();
|
||||
|
||||
// Add the three root txs to the pool.
|
||||
write_pool
|
||||
.add_to_pool(test_source(), root_tx_1, false, &header)
|
||||
.unwrap();
|
||||
write_pool
|
||||
.add_to_pool(test_source(), root_tx_2, false, &header)
|
||||
.unwrap();
|
||||
write_pool
|
||||
.add_to_pool(test_source(), root_tx_3, false, &header)
|
||||
.unwrap();
|
||||
let verifier_cache = Arc::new(RwLock::new(LruVerifierCache::new()));
|
||||
|
||||
// Now add the two child txs to the pool.
|
||||
write_pool
|
||||
.add_to_pool(test_source(), child_tx_1.clone(), false, &header)
|
||||
.unwrap();
|
||||
write_pool
|
||||
.add_to_pool(test_source(), child_tx_2.clone(), false, &header)
|
||||
.unwrap();
|
||||
// Initialize the chain/txhashset with an initial block
|
||||
// so we have a non-empty UTXO set.
|
||||
let add_block =
|
||||
|prev_header: BlockHeader, txs: Vec<Transaction>, chain: &mut ChainAdapter| {
|
||||
let height = prev_header.height + 1;
|
||||
let key_id = ExtKeychain::derive_key_id(1, height as u32, 0, 0, 0);
|
||||
let fee = txs.iter().map(|x| x.fee()).sum();
|
||||
let reward = libtx::reward::output(&keychain, &key_id, fee).unwrap();
|
||||
let mut block = Block::new(&prev_header, txs, Difficulty::min(), reward).unwrap();
|
||||
|
||||
assert_eq!(write_pool.total_size(), 5);
|
||||
}
|
||||
|
||||
let txs = {
|
||||
let read_pool = pool.read();
|
||||
read_pool.prepare_mineable_transactions().unwrap()
|
||||
};
|
||||
// children should have been aggregated into parents
|
||||
assert_eq!(txs.len(), 3);
|
||||
|
||||
let block = add_block(header, txs, &mut chain);
|
||||
|
||||
// Now reconcile the transaction pool with the new block
|
||||
// and check the resulting contents of the pool are what we expect.
|
||||
{
|
||||
let mut write_pool = pool.write();
|
||||
write_pool.reconcile_block(&block).unwrap();
|
||||
|
||||
assert_eq!(write_pool.total_size(), 0);
|
||||
// Set the prev_root to the prev hash for testing purposes (no MMR to obtain a root from).
|
||||
block.header.prev_root = prev_header.hash();
|
||||
|
||||
chain.update_db_for_block(&block);
|
||||
block
|
||||
};
|
||||
|
||||
let block = add_block(BlockHeader::default(), vec![], &mut chain);
|
||||
let header = block.header;
|
||||
|
||||
// Now create tx to spend that first coinbase (now matured).
|
||||
// Provides us with some useful outputs to test with.
|
||||
let initial_tx =
|
||||
test_transaction_spending_coinbase(&keychain, &header, vec![10, 20, 30, 40]);
|
||||
|
||||
// Mine that initial tx so we can spend it with multiple txs
|
||||
let block = add_block(header, vec![initial_tx], &mut chain);
|
||||
let header = block.header;
|
||||
|
||||
// Initialize a new pool with our chain adapter.
|
||||
let pool = RwLock::new(test_setup(Arc::new(chain.clone()), verifier_cache));
|
||||
|
||||
let root_tx_1 = test_transaction(&keychain, vec![10, 20], vec![24]);
|
||||
let root_tx_2 = test_transaction(&keychain, vec![30], vec![28]);
|
||||
let root_tx_3 = test_transaction(&keychain, vec![40], vec![38]);
|
||||
|
||||
let child_tx_1 = test_transaction(&keychain, vec![24], vec![22]);
|
||||
let child_tx_2 = test_transaction(&keychain, vec![38], vec![32]);
|
||||
|
||||
{
|
||||
let mut write_pool = pool.write();
|
||||
|
||||
// Add the three root txs to the pool.
|
||||
write_pool
|
||||
.add_to_pool(test_source(), root_tx_1, false, &header)
|
||||
.unwrap();
|
||||
write_pool
|
||||
.add_to_pool(test_source(), root_tx_2, false, &header)
|
||||
.unwrap();
|
||||
write_pool
|
||||
.add_to_pool(test_source(), root_tx_3, false, &header)
|
||||
.unwrap();
|
||||
|
||||
// Now add the two child txs to the pool.
|
||||
write_pool
|
||||
.add_to_pool(test_source(), child_tx_1.clone(), false, &header)
|
||||
.unwrap();
|
||||
write_pool
|
||||
.add_to_pool(test_source(), child_tx_2.clone(), false, &header)
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(write_pool.total_size(), 5);
|
||||
}
|
||||
|
||||
let txs = {
|
||||
let read_pool = pool.read();
|
||||
read_pool.prepare_mineable_transactions().unwrap()
|
||||
};
|
||||
// children should have been aggregated into parents
|
||||
assert_eq!(txs.len(), 3);
|
||||
|
||||
let block = add_block(header, txs, &mut chain);
|
||||
|
||||
// Now reconcile the transaction pool with the new block
|
||||
// and check the resulting contents of the pool are what we expect.
|
||||
{
|
||||
let mut write_pool = pool.write();
|
||||
write_pool.reconcile_block(&block).unwrap();
|
||||
|
||||
assert_eq!(write_pool.total_size(), 0);
|
||||
}
|
||||
}
|
||||
// Cleanup db directory
|
||||
clean_output_dir(db_root.clone());
|
||||
}
|
||||
|
||||
+103
-97
@@ -40,104 +40,110 @@ fn test_block_building_max_weight() {
|
||||
let db_root = ".grin_block_building_max_weight".to_string();
|
||||
clean_output_dir(db_root.clone());
|
||||
|
||||
let mut chain = ChainAdapter::init(db_root.clone()).unwrap();
|
||||
|
||||
let verifier_cache = Arc::new(RwLock::new(LruVerifierCache::new()));
|
||||
|
||||
// Convenient was to add a new block to the chain.
|
||||
let add_block = |prev_header: BlockHeader, txs: Vec<Transaction>, chain: &mut ChainAdapter| {
|
||||
let height = prev_header.height + 1;
|
||||
let key_id = ExtKeychain::derive_key_id(1, height as u32, 0, 0, 0);
|
||||
let fee = txs.iter().map(|x| x.fee()).sum();
|
||||
let reward = libtx::reward::output(&keychain, &key_id, fee).unwrap();
|
||||
let mut block = Block::new(&prev_header, txs, Difficulty::min(), reward).unwrap();
|
||||
|
||||
// Set the prev_root to the prev hash for testing purposes (no MMR to obtain a root from).
|
||||
block.header.prev_root = prev_header.hash();
|
||||
|
||||
chain.update_db_for_block(&block);
|
||||
block
|
||||
};
|
||||
|
||||
// Initialize the chain/txhashset with an initial block
|
||||
// so we have a non-empty UTXO set.
|
||||
let block = add_block(BlockHeader::default(), vec![], &mut chain);
|
||||
let header = block.header;
|
||||
|
||||
// Now create tx to spend that first coinbase (now matured).
|
||||
// Provides us with some useful outputs to test with.
|
||||
let initial_tx = test_transaction_spending_coinbase(&keychain, &header, vec![100, 200, 300]);
|
||||
|
||||
// Mine that initial tx so we can spend it with multiple txs
|
||||
let block = add_block(header, vec![initial_tx], &mut chain);
|
||||
let header = block.header;
|
||||
|
||||
// Initialize a new pool with our chain adapter.
|
||||
let pool = RwLock::new(test_setup(Arc::new(chain.clone()), verifier_cache));
|
||||
|
||||
// Build some dependent txs to add to the txpool.
|
||||
// We will build a block from a subset of these.
|
||||
let txs = vec![
|
||||
test_transaction(&keychain, vec![100], vec![90, 1]),
|
||||
test_transaction(&keychain, vec![90], vec![80, 2]),
|
||||
test_transaction(&keychain, vec![200], vec![199]),
|
||||
test_transaction(&keychain, vec![300], vec![290, 3]),
|
||||
test_transaction(&keychain, vec![290], vec![280, 4]),
|
||||
];
|
||||
|
||||
// Populate our txpool with the txs.
|
||||
{
|
||||
let mut write_pool = pool.write();
|
||||
for tx in txs {
|
||||
write_pool
|
||||
.add_to_pool(test_source(), tx, false, &header)
|
||||
.unwrap();
|
||||
let mut chain = ChainAdapter::init(db_root.clone()).unwrap();
|
||||
|
||||
let verifier_cache = Arc::new(RwLock::new(LruVerifierCache::new()));
|
||||
|
||||
// Convenient was to add a new block to the chain.
|
||||
let add_block =
|
||||
|prev_header: BlockHeader, txs: Vec<Transaction>, chain: &mut ChainAdapter| {
|
||||
let height = prev_header.height + 1;
|
||||
let key_id = ExtKeychain::derive_key_id(1, height as u32, 0, 0, 0);
|
||||
let fee = txs.iter().map(|x| x.fee()).sum();
|
||||
let reward = libtx::reward::output(&keychain, &key_id, fee).unwrap();
|
||||
let mut block = Block::new(&prev_header, txs, Difficulty::min(), reward).unwrap();
|
||||
|
||||
// Set the prev_root to the prev hash for testing purposes (no MMR to obtain a root from).
|
||||
block.header.prev_root = prev_header.hash();
|
||||
|
||||
chain.update_db_for_block(&block);
|
||||
block
|
||||
};
|
||||
|
||||
// Initialize the chain/txhashset with an initial block
|
||||
// so we have a non-empty UTXO set.
|
||||
let block = add_block(BlockHeader::default(), vec![], &mut chain);
|
||||
let header = block.header;
|
||||
|
||||
// Now create tx to spend that first coinbase (now matured).
|
||||
// Provides us with some useful outputs to test with.
|
||||
let initial_tx =
|
||||
test_transaction_spending_coinbase(&keychain, &header, vec![100, 200, 300]);
|
||||
|
||||
// Mine that initial tx so we can spend it with multiple txs
|
||||
let block = add_block(header, vec![initial_tx], &mut chain);
|
||||
let header = block.header;
|
||||
|
||||
// Initialize a new pool with our chain adapter.
|
||||
let pool = RwLock::new(test_setup(Arc::new(chain.clone()), verifier_cache));
|
||||
|
||||
// Build some dependent txs to add to the txpool.
|
||||
// We will build a block from a subset of these.
|
||||
let txs = vec![
|
||||
test_transaction(&keychain, vec![100], vec![90, 1]),
|
||||
test_transaction(&keychain, vec![90], vec![80, 2]),
|
||||
test_transaction(&keychain, vec![200], vec![199]),
|
||||
test_transaction(&keychain, vec![300], vec![290, 3]),
|
||||
test_transaction(&keychain, vec![290], vec![280, 4]),
|
||||
];
|
||||
|
||||
// Populate our txpool with the txs.
|
||||
{
|
||||
let mut write_pool = pool.write();
|
||||
for tx in txs {
|
||||
write_pool
|
||||
.add_to_pool(test_source(), tx, false, &header)
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
// Check we added them all to the txpool successfully.
|
||||
assert_eq!(pool.read().total_size(), 5);
|
||||
|
||||
// Prepare some "mineable txs" from the txpool.
|
||||
// Note: We cannot fit all the txs from the txpool into a block.
|
||||
let txs = pool.read().prepare_mineable_transactions().unwrap();
|
||||
|
||||
// Check resulting tx aggregation is what we expect.
|
||||
// We expect to produce 2 aggregated txs based on txpool contents.
|
||||
assert_eq!(txs.len(), 2);
|
||||
|
||||
// Check the tx we built is the aggregation of the correct set of underlying txs.
|
||||
// We included 4 out of the 5 txs here.
|
||||
assert_eq!(txs[0].kernels().len(), 1);
|
||||
assert_eq!(txs[1].kernels().len(), 2);
|
||||
|
||||
// Check our weights after aggregation.
|
||||
assert_eq!(txs[0].inputs().len(), 1);
|
||||
assert_eq!(txs[0].outputs().len(), 1);
|
||||
assert_eq!(txs[0].kernels().len(), 1);
|
||||
assert_eq!(txs[0].tx_weight_as_block(), 25);
|
||||
|
||||
assert_eq!(txs[1].inputs().len(), 1);
|
||||
assert_eq!(txs[1].outputs().len(), 3);
|
||||
assert_eq!(txs[1].kernels().len(), 2);
|
||||
assert_eq!(txs[1].tx_weight_as_block(), 70);
|
||||
|
||||
let block = add_block(header, txs, &mut chain);
|
||||
|
||||
// Check contents of the block itself (including coinbase reward).
|
||||
assert_eq!(block.inputs().len(), 2);
|
||||
assert_eq!(block.outputs().len(), 5);
|
||||
assert_eq!(block.kernels().len(), 4);
|
||||
|
||||
// Now reconcile the transaction pool with the new block
|
||||
// and check the resulting contents of the pool are what we expect.
|
||||
{
|
||||
let mut write_pool = pool.write();
|
||||
write_pool.reconcile_block(&block).unwrap();
|
||||
|
||||
// We should still have 2 tx in the pool after accepting the new block.
|
||||
// This one exceeded the max block weight when building the block so
|
||||
// remained in the txpool.
|
||||
assert_eq!(write_pool.total_size(), 2);
|
||||
}
|
||||
}
|
||||
|
||||
// Check we added them all to the txpool successfully.
|
||||
assert_eq!(pool.read().total_size(), 5);
|
||||
|
||||
// Prepare some "mineable txs" from the txpool.
|
||||
// Note: We cannot fit all the txs from the txpool into a block.
|
||||
let txs = pool.read().prepare_mineable_transactions().unwrap();
|
||||
|
||||
// Check resulting tx aggregation is what we expect.
|
||||
// We expect to produce 2 aggregated txs based on txpool contents.
|
||||
assert_eq!(txs.len(), 2);
|
||||
|
||||
// Check the tx we built is the aggregation of the correct set of underlying txs.
|
||||
// We included 4 out of the 5 txs here.
|
||||
assert_eq!(txs[0].kernels().len(), 1);
|
||||
assert_eq!(txs[1].kernels().len(), 2);
|
||||
|
||||
// Check our weights after aggregation.
|
||||
assert_eq!(txs[0].inputs().len(), 1);
|
||||
assert_eq!(txs[0].outputs().len(), 1);
|
||||
assert_eq!(txs[0].kernels().len(), 1);
|
||||
assert_eq!(txs[0].tx_weight_as_block(), 25);
|
||||
|
||||
assert_eq!(txs[1].inputs().len(), 1);
|
||||
assert_eq!(txs[1].outputs().len(), 3);
|
||||
assert_eq!(txs[1].kernels().len(), 2);
|
||||
assert_eq!(txs[1].tx_weight_as_block(), 70);
|
||||
|
||||
let block = add_block(header, txs, &mut chain);
|
||||
|
||||
// Check contents of the block itself (including coinbase reward).
|
||||
assert_eq!(block.inputs().len(), 2);
|
||||
assert_eq!(block.outputs().len(), 5);
|
||||
assert_eq!(block.kernels().len(), 4);
|
||||
|
||||
// Now reconcile the transaction pool with the new block
|
||||
// and check the resulting contents of the pool are what we expect.
|
||||
{
|
||||
let mut write_pool = pool.write();
|
||||
write_pool.reconcile_block(&block).unwrap();
|
||||
|
||||
// We should still have 2 tx in the pool after accepting the new block.
|
||||
// This one exceeded the max block weight when building the block so
|
||||
// remained in the txpool.
|
||||
assert_eq!(write_pool.total_size(), 2);
|
||||
}
|
||||
// Cleanup db directory
|
||||
clean_output_dir(db_root.clone());
|
||||
}
|
||||
|
||||
+150
-144
@@ -34,153 +34,159 @@ fn test_transaction_pool_block_reconciliation() {
|
||||
|
||||
let db_root = ".grin_block_reconciliation".to_string();
|
||||
clean_output_dir(db_root.clone());
|
||||
let chain = Arc::new(ChainAdapter::init(db_root.clone()).unwrap());
|
||||
|
||||
let verifier_cache = Arc::new(RwLock::new(LruVerifierCache::new()));
|
||||
|
||||
// Initialize a new pool with our chain adapter.
|
||||
let pool = RwLock::new(test_setup(chain.clone(), verifier_cache.clone()));
|
||||
|
||||
let header = {
|
||||
let height = 1;
|
||||
let key_id = ExtKeychain::derive_key_id(1, height as u32, 0, 0, 0);
|
||||
let reward = libtx::reward::output(&keychain, &key_id, 0).unwrap();
|
||||
let genesis = BlockHeader::default();
|
||||
let mut block = Block::new(&genesis, vec![], Difficulty::min(), reward).unwrap();
|
||||
|
||||
// Set the prev_root to the prev hash for testing purposes (no MMR to obtain a root from).
|
||||
block.header.prev_root = genesis.hash();
|
||||
|
||||
chain.update_db_for_block(&block);
|
||||
|
||||
block.header
|
||||
};
|
||||
|
||||
// Now create tx to spend that first coinbase (now matured).
|
||||
// Provides us with some useful outputs to test with.
|
||||
let initial_tx = test_transaction_spending_coinbase(&keychain, &header, vec![10, 20, 30, 40]);
|
||||
|
||||
let block = {
|
||||
let key_id = ExtKeychain::derive_key_id(1, 2, 0, 0, 0);
|
||||
let fees = initial_tx.fee();
|
||||
let reward = libtx::reward::output(&keychain, &key_id, fees).unwrap();
|
||||
let mut block = Block::new(&header, vec![initial_tx], Difficulty::min(), reward).unwrap();
|
||||
|
||||
// Set the prev_root to the prev hash for testing purposes (no MMR to obtain a root from).
|
||||
block.header.prev_root = header.hash();
|
||||
|
||||
chain.update_db_for_block(&block);
|
||||
|
||||
block
|
||||
};
|
||||
|
||||
let header = block.header;
|
||||
|
||||
// Preparation: We will introduce three root pool transactions.
|
||||
// 1. A transaction that should be invalidated because it is exactly
|
||||
// contained in the block.
|
||||
// 2. A transaction that should be invalidated because the input is
|
||||
// consumed in the block, although it is not exactly consumed.
|
||||
// 3. A transaction that should remain after block reconciliation.
|
||||
let block_transaction = test_transaction(&keychain, vec![10], vec![8]);
|
||||
let conflict_transaction = test_transaction(&keychain, vec![20], vec![12, 6]);
|
||||
let valid_transaction = test_transaction(&keychain, vec![30], vec![13, 15]);
|
||||
|
||||
// We will also introduce a few children:
|
||||
// 4. A transaction that descends from transaction 1, that is in
|
||||
// turn exactly contained in the block.
|
||||
let block_child = test_transaction(&keychain, vec![8], vec![5, 1]);
|
||||
// 5. A transaction that descends from transaction 4, that is not
|
||||
// contained in the block at all and should be valid after
|
||||
// reconciliation.
|
||||
let pool_child = test_transaction(&keychain, vec![5], vec![3]);
|
||||
// 6. A transaction that descends from transaction 2 that does not
|
||||
// conflict with anything in the block in any way, but should be
|
||||
// invalidated (orphaned).
|
||||
let conflict_child = test_transaction(&keychain, vec![12], vec![2]);
|
||||
// 7. A transaction that descends from transaction 2 that should be
|
||||
// valid due to its inputs being satisfied by the block.
|
||||
let conflict_valid_child = test_transaction(&keychain, vec![6], vec![4]);
|
||||
// 8. A transaction that descends from transaction 3 that should be
|
||||
// invalidated due to an output conflict.
|
||||
let valid_child_conflict = test_transaction(&keychain, vec![13], vec![9]);
|
||||
// 9. A transaction that descends from transaction 3 that should remain
|
||||
// valid after reconciliation.
|
||||
let valid_child_valid = test_transaction(&keychain, vec![15], vec![11]);
|
||||
// 10. A transaction that descends from both transaction 6 and
|
||||
// transaction 9
|
||||
let mixed_child = test_transaction(&keychain, vec![2, 11], vec![7]);
|
||||
|
||||
let txs_to_add = vec![
|
||||
block_transaction,
|
||||
conflict_transaction,
|
||||
valid_transaction.clone(),
|
||||
block_child,
|
||||
pool_child.clone(),
|
||||
conflict_child,
|
||||
conflict_valid_child.clone(),
|
||||
valid_child_conflict.clone(),
|
||||
valid_child_valid.clone(),
|
||||
mixed_child,
|
||||
];
|
||||
|
||||
// First we add the above transactions to the pool.
|
||||
// All should be accepted.
|
||||
{
|
||||
let mut write_pool = pool.write();
|
||||
assert_eq!(write_pool.total_size(), 0);
|
||||
let chain = Arc::new(ChainAdapter::init(db_root.clone()).unwrap());
|
||||
|
||||
for tx in &txs_to_add {
|
||||
write_pool
|
||||
.add_to_pool(test_source(), tx.clone(), false, &header)
|
||||
.unwrap();
|
||||
let verifier_cache = Arc::new(RwLock::new(LruVerifierCache::new()));
|
||||
|
||||
// Initialize a new pool with our chain adapter.
|
||||
let pool = RwLock::new(test_setup(chain.clone(), verifier_cache.clone()));
|
||||
|
||||
let header = {
|
||||
let height = 1;
|
||||
let key_id = ExtKeychain::derive_key_id(1, height as u32, 0, 0, 0);
|
||||
let reward = libtx::reward::output(&keychain, &key_id, 0).unwrap();
|
||||
let genesis = BlockHeader::default();
|
||||
let mut block = Block::new(&genesis, vec![], Difficulty::min(), reward).unwrap();
|
||||
|
||||
// Set the prev_root to the prev hash for testing purposes (no MMR to obtain a root from).
|
||||
block.header.prev_root = genesis.hash();
|
||||
|
||||
chain.update_db_for_block(&block);
|
||||
|
||||
block.header
|
||||
};
|
||||
|
||||
// Now create tx to spend that first coinbase (now matured).
|
||||
// Provides us with some useful outputs to test with.
|
||||
let initial_tx =
|
||||
test_transaction_spending_coinbase(&keychain, &header, vec![10, 20, 30, 40]);
|
||||
|
||||
let block = {
|
||||
let key_id = ExtKeychain::derive_key_id(1, 2, 0, 0, 0);
|
||||
let fees = initial_tx.fee();
|
||||
let reward = libtx::reward::output(&keychain, &key_id, fees).unwrap();
|
||||
let mut block =
|
||||
Block::new(&header, vec![initial_tx], Difficulty::min(), reward).unwrap();
|
||||
|
||||
// Set the prev_root to the prev hash for testing purposes (no MMR to obtain a root from).
|
||||
block.header.prev_root = header.hash();
|
||||
|
||||
chain.update_db_for_block(&block);
|
||||
|
||||
block
|
||||
};
|
||||
|
||||
let header = block.header;
|
||||
|
||||
// Preparation: We will introduce three root pool transactions.
|
||||
// 1. A transaction that should be invalidated because it is exactly
|
||||
// contained in the block.
|
||||
// 2. A transaction that should be invalidated because the input is
|
||||
// consumed in the block, although it is not exactly consumed.
|
||||
// 3. A transaction that should remain after block reconciliation.
|
||||
let block_transaction = test_transaction(&keychain, vec![10], vec![8]);
|
||||
let conflict_transaction = test_transaction(&keychain, vec![20], vec![12, 6]);
|
||||
let valid_transaction = test_transaction(&keychain, vec![30], vec![13, 15]);
|
||||
|
||||
// We will also introduce a few children:
|
||||
// 4. A transaction that descends from transaction 1, that is in
|
||||
// turn exactly contained in the block.
|
||||
let block_child = test_transaction(&keychain, vec![8], vec![5, 1]);
|
||||
// 5. A transaction that descends from transaction 4, that is not
|
||||
// contained in the block at all and should be valid after
|
||||
// reconciliation.
|
||||
let pool_child = test_transaction(&keychain, vec![5], vec![3]);
|
||||
// 6. A transaction that descends from transaction 2 that does not
|
||||
// conflict with anything in the block in any way, but should be
|
||||
// invalidated (orphaned).
|
||||
let conflict_child = test_transaction(&keychain, vec![12], vec![2]);
|
||||
// 7. A transaction that descends from transaction 2 that should be
|
||||
// valid due to its inputs being satisfied by the block.
|
||||
let conflict_valid_child = test_transaction(&keychain, vec![6], vec![4]);
|
||||
// 8. A transaction that descends from transaction 3 that should be
|
||||
// invalidated due to an output conflict.
|
||||
let valid_child_conflict = test_transaction(&keychain, vec![13], vec![9]);
|
||||
// 9. A transaction that descends from transaction 3 that should remain
|
||||
// valid after reconciliation.
|
||||
let valid_child_valid = test_transaction(&keychain, vec![15], vec![11]);
|
||||
// 10. A transaction that descends from both transaction 6 and
|
||||
// transaction 9
|
||||
let mixed_child = test_transaction(&keychain, vec![2, 11], vec![7]);
|
||||
|
||||
let txs_to_add = vec![
|
||||
block_transaction,
|
||||
conflict_transaction,
|
||||
valid_transaction.clone(),
|
||||
block_child,
|
||||
pool_child.clone(),
|
||||
conflict_child,
|
||||
conflict_valid_child.clone(),
|
||||
valid_child_conflict.clone(),
|
||||
valid_child_valid.clone(),
|
||||
mixed_child,
|
||||
];
|
||||
|
||||
// First we add the above transactions to the pool.
|
||||
// All should be accepted.
|
||||
{
|
||||
let mut write_pool = pool.write();
|
||||
assert_eq!(write_pool.total_size(), 0);
|
||||
|
||||
for tx in &txs_to_add {
|
||||
write_pool
|
||||
.add_to_pool(test_source(), tx.clone(), false, &header)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
assert_eq!(write_pool.total_size(), txs_to_add.len());
|
||||
}
|
||||
|
||||
assert_eq!(write_pool.total_size(), txs_to_add.len());
|
||||
}
|
||||
|
||||
// Now we prepare the block that will cause the above conditions to be met.
|
||||
// First, the transactions we want in the block:
|
||||
// - Copy of 1
|
||||
let block_tx_1 = test_transaction(&keychain, vec![10], vec![8]);
|
||||
// - Conflict w/ 2, satisfies 7
|
||||
let block_tx_2 = test_transaction(&keychain, vec![20], vec![6]);
|
||||
// - Copy of 4
|
||||
let block_tx_3 = test_transaction(&keychain, vec![8], vec![5, 1]);
|
||||
// - Output conflict w/ 8
|
||||
let block_tx_4 = test_transaction(&keychain, vec![40], vec![9, 31]);
|
||||
|
||||
let block_txs = vec![block_tx_1, block_tx_2, block_tx_3, block_tx_4];
|
||||
|
||||
// Now apply this block.
|
||||
let block = {
|
||||
let key_id = ExtKeychain::derive_key_id(1, 3, 0, 0, 0);
|
||||
let fees = block_txs.iter().map(|tx| tx.fee()).sum();
|
||||
let reward = libtx::reward::output(&keychain, &key_id, fees).unwrap();
|
||||
let mut block = Block::new(&header, block_txs, Difficulty::min(), reward).unwrap();
|
||||
|
||||
// Set the prev_root to the prev hash for testing purposes (no MMR to obtain a root from).
|
||||
block.header.prev_root = header.hash();
|
||||
|
||||
chain.update_db_for_block(&block);
|
||||
block
|
||||
};
|
||||
|
||||
// Check the pool still contains everything we expect at this point.
|
||||
{
|
||||
let write_pool = pool.write();
|
||||
assert_eq!(write_pool.total_size(), txs_to_add.len());
|
||||
}
|
||||
|
||||
// And reconcile the pool with this latest block.
|
||||
{
|
||||
let mut write_pool = pool.write();
|
||||
write_pool.reconcile_block(&block).unwrap();
|
||||
|
||||
assert_eq!(write_pool.total_size(), 4);
|
||||
assert_eq!(write_pool.txpool.entries[0].tx, valid_transaction);
|
||||
assert_eq!(write_pool.txpool.entries[1].tx, pool_child);
|
||||
assert_eq!(write_pool.txpool.entries[2].tx, conflict_valid_child);
|
||||
assert_eq!(write_pool.txpool.entries[3].tx, valid_child_valid);
|
||||
// Now we prepare the block that will cause the above conditions to be met.
|
||||
// First, the transactions we want in the block:
|
||||
// - Copy of 1
|
||||
let block_tx_1 = test_transaction(&keychain, vec![10], vec![8]);
|
||||
// - Conflict w/ 2, satisfies 7
|
||||
let block_tx_2 = test_transaction(&keychain, vec![20], vec![6]);
|
||||
// - Copy of 4
|
||||
let block_tx_3 = test_transaction(&keychain, vec![8], vec![5, 1]);
|
||||
// - Output conflict w/ 8
|
||||
let block_tx_4 = test_transaction(&keychain, vec![40], vec![9, 31]);
|
||||
|
||||
let block_txs = vec![block_tx_1, block_tx_2, block_tx_3, block_tx_4];
|
||||
|
||||
// Now apply this block.
|
||||
let block = {
|
||||
let key_id = ExtKeychain::derive_key_id(1, 3, 0, 0, 0);
|
||||
let fees = block_txs.iter().map(|tx| tx.fee()).sum();
|
||||
let reward = libtx::reward::output(&keychain, &key_id, fees).unwrap();
|
||||
let mut block = Block::new(&header, block_txs, Difficulty::min(), reward).unwrap();
|
||||
|
||||
// Set the prev_root to the prev hash for testing purposes (no MMR to obtain a root from).
|
||||
block.header.prev_root = header.hash();
|
||||
|
||||
chain.update_db_for_block(&block);
|
||||
block
|
||||
};
|
||||
|
||||
// Check the pool still contains everything we expect at this point.
|
||||
{
|
||||
let write_pool = pool.write();
|
||||
assert_eq!(write_pool.total_size(), txs_to_add.len());
|
||||
}
|
||||
|
||||
// And reconcile the pool with this latest block.
|
||||
{
|
||||
let mut write_pool = pool.write();
|
||||
write_pool.reconcile_block(&block).unwrap();
|
||||
|
||||
assert_eq!(write_pool.total_size(), 4);
|
||||
assert_eq!(write_pool.txpool.entries[0].tx, valid_transaction);
|
||||
assert_eq!(write_pool.txpool.entries[1].tx, pool_child);
|
||||
assert_eq!(write_pool.txpool.entries[2].tx, conflict_valid_child);
|
||||
assert_eq!(write_pool.txpool.entries[3].tx, valid_child_valid);
|
||||
}
|
||||
}
|
||||
// Cleanup db directory
|
||||
clean_output_dir(db_root.clone());
|
||||
}
|
||||
|
||||
+220
-215
@@ -33,221 +33,226 @@ fn test_the_transaction_pool() {
|
||||
|
||||
let db_root = ".grin_transaction_pool".to_string();
|
||||
clean_output_dir(db_root.clone());
|
||||
let chain = Arc::new(ChainAdapter::init(db_root.clone()).unwrap());
|
||||
|
||||
let verifier_cache = Arc::new(RwLock::new(LruVerifierCache::new()));
|
||||
|
||||
// Initialize a new pool with our chain adapter.
|
||||
let pool = RwLock::new(test_setup(chain.clone(), verifier_cache.clone()));
|
||||
|
||||
let header = {
|
||||
let height = 1;
|
||||
let key_id = ExtKeychain::derive_key_id(1, height as u32, 0, 0, 0);
|
||||
let reward = libtx::reward::output(&keychain, &key_id, 0).unwrap();
|
||||
let block = Block::new(&BlockHeader::default(), vec![], Difficulty::min(), reward).unwrap();
|
||||
|
||||
chain.update_db_for_block(&block);
|
||||
|
||||
block.header
|
||||
};
|
||||
|
||||
// Now create tx to spend a coinbase, giving us some useful outputs for testing
|
||||
// with.
|
||||
let initial_tx = {
|
||||
test_transaction_spending_coinbase(
|
||||
&keychain,
|
||||
&header,
|
||||
vec![500, 600, 700, 800, 900, 1000, 1100, 1200, 1300, 1400],
|
||||
)
|
||||
};
|
||||
|
||||
// Add this tx to the pool (stem=false, direct to txpool).
|
||||
{
|
||||
let mut write_pool = pool.write();
|
||||
write_pool
|
||||
.add_to_pool(test_source(), initial_tx, false, &header)
|
||||
.unwrap();
|
||||
assert_eq!(write_pool.total_size(), 1);
|
||||
}
|
||||
|
||||
// Test adding a tx that "double spends" an output currently spent by a tx
|
||||
// already in the txpool. In this case we attempt to spend the original coinbase twice.
|
||||
{
|
||||
let tx = test_transaction_spending_coinbase(&keychain, &header, vec![501]);
|
||||
let mut write_pool = pool.write();
|
||||
assert!(write_pool
|
||||
.add_to_pool(test_source(), tx, false, &header)
|
||||
.is_err());
|
||||
}
|
||||
|
||||
// tx1 spends some outputs from the initial test tx.
|
||||
let tx1 = test_transaction(&keychain, vec![500, 600], vec![499, 599]);
|
||||
// tx2 spends some outputs from both tx1 and the initial test tx.
|
||||
let tx2 = test_transaction(&keychain, vec![499, 700], vec![498]);
|
||||
|
||||
// Take a write lock and add a couple of tx entries to the pool.
|
||||
{
|
||||
let mut write_pool = pool.write();
|
||||
|
||||
// Check we have a single initial tx in the pool.
|
||||
assert_eq!(write_pool.total_size(), 1);
|
||||
|
||||
// First, add a simple tx directly to the txpool (stem = false).
|
||||
write_pool
|
||||
.add_to_pool(test_source(), tx1.clone(), false, &header)
|
||||
.unwrap();
|
||||
assert_eq!(write_pool.total_size(), 2);
|
||||
|
||||
// Add another tx spending outputs from the previous tx.
|
||||
write_pool
|
||||
.add_to_pool(test_source(), tx2.clone(), false, &header)
|
||||
.unwrap();
|
||||
assert_eq!(write_pool.total_size(), 3);
|
||||
}
|
||||
|
||||
// Test adding the exact same tx multiple times (same kernel signature).
|
||||
// This will fail for stem=false during tx aggregation due to duplicate
|
||||
// outputs and duplicate kernels.
|
||||
{
|
||||
let mut write_pool = pool.write();
|
||||
assert!(write_pool
|
||||
.add_to_pool(test_source(), tx1.clone(), false, &header)
|
||||
.is_err());
|
||||
}
|
||||
|
||||
// Test adding a duplicate tx with the same input and outputs.
|
||||
// Note: not the *same* tx, just same underlying inputs/outputs.
|
||||
{
|
||||
let tx1a = test_transaction(&keychain, vec![500, 600], vec![499, 599]);
|
||||
let mut write_pool = pool.write();
|
||||
assert!(write_pool
|
||||
.add_to_pool(test_source(), tx1a, false, &header)
|
||||
.is_err());
|
||||
}
|
||||
|
||||
// Test adding a tx attempting to spend a non-existent output.
|
||||
{
|
||||
let bad_tx = test_transaction(&keychain, vec![10_001], vec![10_000]);
|
||||
let mut write_pool = pool.write();
|
||||
assert!(write_pool
|
||||
.add_to_pool(test_source(), bad_tx, false, &header)
|
||||
.is_err());
|
||||
}
|
||||
|
||||
// Test adding a tx that would result in a duplicate output (conflicts with
|
||||
// output from tx2). For reasons of security all outputs in the UTXO set must
|
||||
// be unique. Otherwise spending one will almost certainly cause the other
|
||||
// to be immediately stolen via a "replay" tx.
|
||||
{
|
||||
let tx = test_transaction(&keychain, vec![900], vec![498]);
|
||||
let mut write_pool = pool.write();
|
||||
assert!(write_pool
|
||||
.add_to_pool(test_source(), tx, false, &header)
|
||||
.is_err());
|
||||
}
|
||||
|
||||
// Confirm the tx pool correctly identifies an invalid tx (already spent).
|
||||
{
|
||||
let mut write_pool = pool.write();
|
||||
let tx3 = test_transaction(&keychain, vec![500], vec![497]);
|
||||
assert!(write_pool
|
||||
.add_to_pool(test_source(), tx3, false, &header)
|
||||
.is_err());
|
||||
assert_eq!(write_pool.total_size(), 3);
|
||||
}
|
||||
|
||||
// Now add a couple of txs to the stempool (stem = true).
|
||||
{
|
||||
let mut write_pool = pool.write();
|
||||
let tx = test_transaction(&keychain, vec![599], vec![598]);
|
||||
write_pool
|
||||
.add_to_pool(test_source(), tx, true, &header)
|
||||
.unwrap();
|
||||
let tx2 = test_transaction(&keychain, vec![598], vec![597]);
|
||||
write_pool
|
||||
.add_to_pool(test_source(), tx2, true, &header)
|
||||
.unwrap();
|
||||
assert_eq!(write_pool.total_size(), 3);
|
||||
assert_eq!(write_pool.stempool.size(), 2);
|
||||
}
|
||||
|
||||
// Check we can take some entries from the stempool and "fluff" them into the
|
||||
// txpool. This also exercises multi-kernel txs.
|
||||
{
|
||||
let mut write_pool = pool.write();
|
||||
let agg_tx = write_pool
|
||||
.stempool
|
||||
.all_transactions_aggregate()
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
assert_eq!(agg_tx.kernels().len(), 2);
|
||||
write_pool
|
||||
.add_to_pool(test_source(), agg_tx, false, &header)
|
||||
.unwrap();
|
||||
assert_eq!(write_pool.total_size(), 4);
|
||||
assert!(write_pool.stempool.is_empty());
|
||||
}
|
||||
|
||||
// Adding a duplicate tx to the stempool will result in it being fluffed.
|
||||
// This handles the case of the stem path having a cycle in it.
|
||||
{
|
||||
let mut write_pool = pool.write();
|
||||
let tx = test_transaction(&keychain, vec![597], vec![596]);
|
||||
write_pool
|
||||
.add_to_pool(test_source(), tx.clone(), true, &header)
|
||||
.unwrap();
|
||||
assert_eq!(write_pool.total_size(), 4);
|
||||
assert_eq!(write_pool.stempool.size(), 1);
|
||||
|
||||
// Duplicate stem tx so fluff, adding it to txpool and removing it from stempool.
|
||||
write_pool
|
||||
.add_to_pool(test_source(), tx.clone(), true, &header)
|
||||
.unwrap();
|
||||
assert_eq!(write_pool.total_size(), 5);
|
||||
assert!(write_pool.stempool.is_empty());
|
||||
}
|
||||
|
||||
// Now check we can correctly deaggregate a multi-kernel tx based on current
|
||||
// contents of the txpool.
|
||||
// We will do this be adding a new tx to the pool
|
||||
// that is a superset of a tx already in the pool.
|
||||
{
|
||||
let mut write_pool = pool.write();
|
||||
|
||||
let tx4 = test_transaction(&keychain, vec![800], vec![799]);
|
||||
// tx1 and tx2 are already in the txpool (in aggregated form)
|
||||
// tx4 is the "new" part of this aggregated tx that we care about
|
||||
let agg_tx = transaction::aggregate(vec![tx1.clone(), tx2.clone(), tx4]).unwrap();
|
||||
|
||||
agg_tx
|
||||
.validate(Weighting::AsTransaction, verifier_cache.clone())
|
||||
.unwrap();
|
||||
|
||||
write_pool
|
||||
.add_to_pool(test_source(), agg_tx, false, &header)
|
||||
.unwrap();
|
||||
assert_eq!(write_pool.total_size(), 6);
|
||||
let entry = write_pool.txpool.entries.last().unwrap();
|
||||
assert_eq!(entry.tx.kernels().len(), 1);
|
||||
assert_eq!(entry.src.debug_name, "deagg");
|
||||
}
|
||||
|
||||
// Check we cannot "double spend" an output spent in a previous block.
|
||||
// We use the initial coinbase output here for convenience.
|
||||
{
|
||||
let mut write_pool = pool.write();
|
||||
|
||||
let double_spend_tx =
|
||||
{ test_transaction_spending_coinbase(&keychain, &header, vec![1000]) };
|
||||
|
||||
// check we cannot add a double spend to the stempool
|
||||
assert!(write_pool
|
||||
.add_to_pool(test_source(), double_spend_tx.clone(), true, &header)
|
||||
.is_err());
|
||||
|
||||
// check we cannot add a double spend to the txpool
|
||||
assert!(write_pool
|
||||
.add_to_pool(test_source(), double_spend_tx.clone(), false, &header)
|
||||
.is_err());
|
||||
let chain = Arc::new(ChainAdapter::init(db_root.clone()).unwrap());
|
||||
|
||||
let verifier_cache = Arc::new(RwLock::new(LruVerifierCache::new()));
|
||||
|
||||
// Initialize a new pool with our chain adapter.
|
||||
let pool = RwLock::new(test_setup(chain.clone(), verifier_cache.clone()));
|
||||
|
||||
let header = {
|
||||
let height = 1;
|
||||
let key_id = ExtKeychain::derive_key_id(1, height as u32, 0, 0, 0);
|
||||
let reward = libtx::reward::output(&keychain, &key_id, 0).unwrap();
|
||||
let block =
|
||||
Block::new(&BlockHeader::default(), vec![], Difficulty::min(), reward).unwrap();
|
||||
|
||||
chain.update_db_for_block(&block);
|
||||
|
||||
block.header
|
||||
};
|
||||
|
||||
// Now create tx to spend a coinbase, giving us some useful outputs for testing
|
||||
// with.
|
||||
let initial_tx = {
|
||||
test_transaction_spending_coinbase(
|
||||
&keychain,
|
||||
&header,
|
||||
vec![500, 600, 700, 800, 900, 1000, 1100, 1200, 1300, 1400],
|
||||
)
|
||||
};
|
||||
|
||||
// Add this tx to the pool (stem=false, direct to txpool).
|
||||
{
|
||||
let mut write_pool = pool.write();
|
||||
write_pool
|
||||
.add_to_pool(test_source(), initial_tx, false, &header)
|
||||
.unwrap();
|
||||
assert_eq!(write_pool.total_size(), 1);
|
||||
}
|
||||
|
||||
// Test adding a tx that "double spends" an output currently spent by a tx
|
||||
// already in the txpool. In this case we attempt to spend the original coinbase twice.
|
||||
{
|
||||
let tx = test_transaction_spending_coinbase(&keychain, &header, vec![501]);
|
||||
let mut write_pool = pool.write();
|
||||
assert!(write_pool
|
||||
.add_to_pool(test_source(), tx, false, &header)
|
||||
.is_err());
|
||||
}
|
||||
|
||||
// tx1 spends some outputs from the initial test tx.
|
||||
let tx1 = test_transaction(&keychain, vec![500, 600], vec![499, 599]);
|
||||
// tx2 spends some outputs from both tx1 and the initial test tx.
|
||||
let tx2 = test_transaction(&keychain, vec![499, 700], vec![498]);
|
||||
|
||||
// Take a write lock and add a couple of tx entries to the pool.
|
||||
{
|
||||
let mut write_pool = pool.write();
|
||||
|
||||
// Check we have a single initial tx in the pool.
|
||||
assert_eq!(write_pool.total_size(), 1);
|
||||
|
||||
// First, add a simple tx directly to the txpool (stem = false).
|
||||
write_pool
|
||||
.add_to_pool(test_source(), tx1.clone(), false, &header)
|
||||
.unwrap();
|
||||
assert_eq!(write_pool.total_size(), 2);
|
||||
|
||||
// Add another tx spending outputs from the previous tx.
|
||||
write_pool
|
||||
.add_to_pool(test_source(), tx2.clone(), false, &header)
|
||||
.unwrap();
|
||||
assert_eq!(write_pool.total_size(), 3);
|
||||
}
|
||||
|
||||
// Test adding the exact same tx multiple times (same kernel signature).
|
||||
// This will fail for stem=false during tx aggregation due to duplicate
|
||||
// outputs and duplicate kernels.
|
||||
{
|
||||
let mut write_pool = pool.write();
|
||||
assert!(write_pool
|
||||
.add_to_pool(test_source(), tx1.clone(), false, &header)
|
||||
.is_err());
|
||||
}
|
||||
|
||||
// Test adding a duplicate tx with the same input and outputs.
|
||||
// Note: not the *same* tx, just same underlying inputs/outputs.
|
||||
{
|
||||
let tx1a = test_transaction(&keychain, vec![500, 600], vec![499, 599]);
|
||||
let mut write_pool = pool.write();
|
||||
assert!(write_pool
|
||||
.add_to_pool(test_source(), tx1a, false, &header)
|
||||
.is_err());
|
||||
}
|
||||
|
||||
// Test adding a tx attempting to spend a non-existent output.
|
||||
{
|
||||
let bad_tx = test_transaction(&keychain, vec![10_001], vec![10_000]);
|
||||
let mut write_pool = pool.write();
|
||||
assert!(write_pool
|
||||
.add_to_pool(test_source(), bad_tx, false, &header)
|
||||
.is_err());
|
||||
}
|
||||
|
||||
// Test adding a tx that would result in a duplicate output (conflicts with
|
||||
// output from tx2). For reasons of security all outputs in the UTXO set must
|
||||
// be unique. Otherwise spending one will almost certainly cause the other
|
||||
// to be immediately stolen via a "replay" tx.
|
||||
{
|
||||
let tx = test_transaction(&keychain, vec![900], vec![498]);
|
||||
let mut write_pool = pool.write();
|
||||
assert!(write_pool
|
||||
.add_to_pool(test_source(), tx, false, &header)
|
||||
.is_err());
|
||||
}
|
||||
|
||||
// Confirm the tx pool correctly identifies an invalid tx (already spent).
|
||||
{
|
||||
let mut write_pool = pool.write();
|
||||
let tx3 = test_transaction(&keychain, vec![500], vec![497]);
|
||||
assert!(write_pool
|
||||
.add_to_pool(test_source(), tx3, false, &header)
|
||||
.is_err());
|
||||
assert_eq!(write_pool.total_size(), 3);
|
||||
}
|
||||
|
||||
// Now add a couple of txs to the stempool (stem = true).
|
||||
{
|
||||
let mut write_pool = pool.write();
|
||||
let tx = test_transaction(&keychain, vec![599], vec![598]);
|
||||
write_pool
|
||||
.add_to_pool(test_source(), tx, true, &header)
|
||||
.unwrap();
|
||||
let tx2 = test_transaction(&keychain, vec![598], vec![597]);
|
||||
write_pool
|
||||
.add_to_pool(test_source(), tx2, true, &header)
|
||||
.unwrap();
|
||||
assert_eq!(write_pool.total_size(), 3);
|
||||
assert_eq!(write_pool.stempool.size(), 2);
|
||||
}
|
||||
|
||||
// Check we can take some entries from the stempool and "fluff" them into the
|
||||
// txpool. This also exercises multi-kernel txs.
|
||||
{
|
||||
let mut write_pool = pool.write();
|
||||
let agg_tx = write_pool
|
||||
.stempool
|
||||
.all_transactions_aggregate()
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
assert_eq!(agg_tx.kernels().len(), 2);
|
||||
write_pool
|
||||
.add_to_pool(test_source(), agg_tx, false, &header)
|
||||
.unwrap();
|
||||
assert_eq!(write_pool.total_size(), 4);
|
||||
assert!(write_pool.stempool.is_empty());
|
||||
}
|
||||
|
||||
// Adding a duplicate tx to the stempool will result in it being fluffed.
|
||||
// This handles the case of the stem path having a cycle in it.
|
||||
{
|
||||
let mut write_pool = pool.write();
|
||||
let tx = test_transaction(&keychain, vec![597], vec![596]);
|
||||
write_pool
|
||||
.add_to_pool(test_source(), tx.clone(), true, &header)
|
||||
.unwrap();
|
||||
assert_eq!(write_pool.total_size(), 4);
|
||||
assert_eq!(write_pool.stempool.size(), 1);
|
||||
|
||||
// Duplicate stem tx so fluff, adding it to txpool and removing it from stempool.
|
||||
write_pool
|
||||
.add_to_pool(test_source(), tx.clone(), true, &header)
|
||||
.unwrap();
|
||||
assert_eq!(write_pool.total_size(), 5);
|
||||
assert!(write_pool.stempool.is_empty());
|
||||
}
|
||||
|
||||
// Now check we can correctly deaggregate a multi-kernel tx based on current
|
||||
// contents of the txpool.
|
||||
// We will do this be adding a new tx to the pool
|
||||
// that is a superset of a tx already in the pool.
|
||||
{
|
||||
let mut write_pool = pool.write();
|
||||
|
||||
let tx4 = test_transaction(&keychain, vec![800], vec![799]);
|
||||
// tx1 and tx2 are already in the txpool (in aggregated form)
|
||||
// tx4 is the "new" part of this aggregated tx that we care about
|
||||
let agg_tx = transaction::aggregate(vec![tx1.clone(), tx2.clone(), tx4]).unwrap();
|
||||
|
||||
agg_tx
|
||||
.validate(Weighting::AsTransaction, verifier_cache.clone())
|
||||
.unwrap();
|
||||
|
||||
write_pool
|
||||
.add_to_pool(test_source(), agg_tx, false, &header)
|
||||
.unwrap();
|
||||
assert_eq!(write_pool.total_size(), 6);
|
||||
let entry = write_pool.txpool.entries.last().unwrap();
|
||||
assert_eq!(entry.tx.kernels().len(), 1);
|
||||
assert_eq!(entry.src.debug_name, "deagg");
|
||||
}
|
||||
|
||||
// Check we cannot "double spend" an output spent in a previous block.
|
||||
// We use the initial coinbase output here for convenience.
|
||||
{
|
||||
let mut write_pool = pool.write();
|
||||
|
||||
let double_spend_tx =
|
||||
{ test_transaction_spending_coinbase(&keychain, &header, vec![1000]) };
|
||||
|
||||
// check we cannot add a double spend to the stempool
|
||||
assert!(write_pool
|
||||
.add_to_pool(test_source(), double_spend_tx.clone(), true, &header)
|
||||
.is_err());
|
||||
|
||||
// check we cannot add a double spend to the txpool
|
||||
assert!(write_pool
|
||||
.add_to_pool(test_source(), double_spend_tx.clone(), false, &header)
|
||||
.is_err());
|
||||
}
|
||||
}
|
||||
// Cleanup db directory
|
||||
clean_output_dir(db_root.clone());
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user