Created
January 12, 2024 17:22
-
-
Save philoniare/6cbadde6dd58ae1c2d56c36a230e96bb to your computer and use it in GitHub Desktop.
tests.rs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
use super::*; | |
// Helper Methods | |
fn empty_wallet() -> Wallet { | |
Wallet::new(vec![].into_iter()) | |
} | |
fn wallet_with_alice() -> Wallet { | |
Wallet::new(vec![Address::Alice].into_iter()) | |
} | |
fn wallet_with_alice_and_bob() -> Wallet { | |
Wallet::new(vec![Address::Alice, Address::Bob].into_iter()) | |
} | |
fn marker_tx() -> Transaction { | |
Transaction { | |
inputs: vec![Input::dummy()], | |
outputs: vec![Coin { | |
value: 123, | |
owner: Address::Custom(123), | |
}], | |
} | |
} | |
// Official tests included in the starter repo | |
// This will check the basic values after wallet creation. | |
#[test] | |
fn correct_genesis_values() { | |
let wallet = wallet_with_alice(); | |
assert_eq!(wallet.best_height(), 0); | |
assert_eq!(wallet.best_hash(), Block::genesis().id()); | |
assert_eq!(wallet.total_assets_of(Address::Alice), Ok(0)); | |
assert_eq!(wallet.net_worth(), 0); | |
assert_eq!(wallet.all_coins_of(Address::Alice).unwrap().len(), 0); | |
} | |
// This case ensures that the wallet will reject requests for foreign addresses. | |
#[test] | |
fn foreign_address_error() { | |
let wallet = wallet_with_alice(); | |
assert_eq!( | |
wallet.total_assets_of(Address::Bob), | |
Err(WalletError::ForeignAddress) | |
); | |
assert_eq!( | |
wallet.all_coins_of(Address::Bob), | |
Err(WalletError::ForeignAddress) | |
); | |
} | |
// Check validation for an empty wallet with no registered addresses | |
#[test] | |
fn no_address_error() { | |
const COIN_VALUE: u64 = 100; | |
let wallet = empty_wallet(); | |
assert_eq!( | |
wallet.create_manual_transaction(Vec::new(), Vec::new()), | |
Err(WalletError::NoOwnedAddresses) | |
); | |
assert_eq!( | |
wallet.create_automatic_transaction(Address::Eve, 0, 0), | |
Err(WalletError::NoOwnedAddresses) | |
); | |
} | |
// Empty transactions should be fine | |
#[test] | |
fn empty_manual_transaction() { | |
let wallet = wallet_with_alice(); | |
let result = wallet.create_automatic_transaction(Address::Eve, 0, 0); | |
assert!(result.is_ok()); | |
let transaction = result.unwrap().clone(); | |
assert_eq!(transaction.inputs.len(), 0); | |
assert_eq!(transaction.outputs.len(), 0); | |
} | |
#[test] | |
fn empty_automatic_transaction() { | |
let wallet = wallet_with_alice(); | |
let result = wallet.create_automatic_transaction(Address::Eve, 0, 0); | |
assert!(result.is_ok()); | |
let transaction = result.unwrap().clone(); | |
assert_eq!(transaction.inputs.len(), 0); | |
assert_eq!(transaction.outputs.len(), 0); | |
} | |
// Check the basic case of sync, should have correct height | |
#[test] | |
fn sync_two_blocks() { | |
// Build a mock node that has a simple two block chain | |
let mut node = MockNode::new(); | |
let b1_id = node.add_block(Block::genesis().id(), vec![]); | |
let b2_id = node.add_block_as_best(b1_id, vec![]); | |
let mut wallet = wallet_with_alice(); | |
wallet.sync(&node); | |
assert_eq!(wallet.best_height(), 2); | |
assert_eq!(wallet.best_hash(), b2_id); | |
} | |
// Should reorg to longer chain and calculate correct height | |
#[test] | |
fn short_reorg() { | |
// Create node and wallet | |
let mut node = MockNode::new(); | |
let mut wallet = wallet_with_alice(); | |
// Sync a chain to height 1 | |
let _old_b1_id = node.add_block_as_best(Block::genesis().id(), vec![]); | |
wallet.sync(&node); | |
// Reorg to longer chain of length 2 | |
let b1_id = node.add_block(Block::genesis().id(), vec![]); | |
let b2_id = node.add_block_as_best(b1_id, vec![]); | |
wallet.sync(&node); | |
assert_eq!(wallet.best_height(), 2); | |
assert_eq!(wallet.best_hash(), b2_id); | |
} | |
// Old_B2 (discard) - Old_B3 (discard) | |
// / | |
// G | |
// \ B2 (should reorg the chain here) | |
#[test] | |
fn reorg_to_shorter_chain() { | |
// Create node and wallet | |
let mut node = MockNode::new(); | |
let mut wallet = wallet_with_alice(); | |
// Sync a chain to height 3 | |
let old_b1_id = node.add_block_as_best(Block::genesis().id(), vec![]); | |
let old_b2_id = node.add_block_as_best(old_b1_id, vec![]); | |
let _old_b3_id = node.add_block_as_best(old_b2_id, vec![]); | |
wallet.sync(&node); | |
// Reorg to shorter chain of length 2 | |
let b1_id = node.add_block(Block::genesis().id(), vec![]); | |
let b2_id = node.add_block_as_best(b1_id, vec![]); | |
wallet.sync(&node); | |
assert_eq!(wallet.best_height(), 2); | |
assert_eq!(wallet.best_hash(), b2_id); | |
} | |
// Check that sync updates with internal state of the wallet with transaction UTXO | |
#[test] | |
fn tracks_single_utxo() { | |
// We have a single transaction that consumes some made up input | |
// and creates a single output to alice. | |
const COIN_VALUE: u64 = 100; | |
let coin = Coin { | |
value: COIN_VALUE, | |
owner: Address::Alice, | |
}; | |
let tx = Transaction { | |
inputs: vec![Input::dummy()], | |
outputs: vec![coin.clone()], | |
}; | |
let coin_id = tx.coin_id(0); | |
// Create a minimal chain to contain this transaction and sync it | |
let mut node = MockNode::new(); | |
node.add_block_as_best(Block::genesis().id(), vec![tx]); | |
let mut wallet = wallet_with_alice(); | |
wallet.sync(&node); | |
// Check that the coins have been correctly accounted for | |
assert_eq!(wallet.total_assets_of(Address::Alice), Ok(COIN_VALUE)); | |
assert_eq!(wallet.net_worth(), COIN_VALUE); | |
assert_eq!( | |
wallet.all_coins_of(Address::Alice), | |
Ok(vec![(coin_id, COIN_VALUE)]) | |
); | |
assert_eq!(wallet.coin_details(&coin_id), Ok(coin)); | |
} | |
// Check that user can transfer to themselves | |
#[test] | |
fn consumes_own_utxo() { | |
// All coins will be valued the same in this test | |
const COIN_VALUE: u64 = 100; | |
// We start by minting a coin to alice | |
let coin = Coin { | |
value: COIN_VALUE, | |
owner: Address::Alice, | |
}; | |
let tx_mint = Transaction { | |
inputs: vec![], | |
outputs: vec![coin.clone()], | |
}; | |
let coin_id = tx_mint.coin_id(0); | |
// Then we burn that coin | |
let input = Input { | |
coin_id, | |
// The signature is invalid to save syntax. | |
// The wallet doesn't check validity anyway. | |
// This transaction is in a block, so the wallet syncs it. | |
signature: Signature::Invalid, | |
}; | |
let tx_burn = Transaction { | |
inputs: vec![input], | |
outputs: vec![], | |
}; | |
// Apply this all to a blockchain and sync the wallet. | |
// We apply in two separate blocks although that shouldn't be necessary. | |
let mut node = MockNode::new(); | |
let b1_id = node.add_block_as_best(Block::genesis().id(), vec![tx_mint]); | |
let _b2_id = node.add_block_as_best(b1_id, vec![tx_burn]); | |
let mut wallet = wallet_with_alice(); | |
wallet.sync(&node); | |
// Make sure the UTXO is consumed | |
assert_eq!(wallet.total_assets_of(Address::Alice), Ok(0)); | |
assert_eq!(wallet.net_worth(), 0); | |
assert_eq!(wallet.all_coins_of(Address::Alice), Ok(vec![])); | |
// Pedagogy: It is reasonable that the wallet could provide details about | |
// the coin even after it was spent. But requiring that gives away the trick of | |
// tracking spent coins so you can revert them later. | |
assert_eq!(wallet.coin_details(&coin_id), Err(WalletError::UnknownCoin)); | |
} | |
// Check that transaction between users correctly updates wallet state | |
#[test] | |
fn tracks_utxo_from_two_tx_in_one_block_to_multiple_users() { | |
// We have a single transaction that consumes some made up input | |
// and creates a single output to alice. | |
const COIN_VALUE: u64 = 100; | |
let coin1 = Coin { | |
value: COIN_VALUE, | |
owner: Address::Alice, | |
}; | |
let coin2 = Coin { | |
value: COIN_VALUE * 2, | |
owner: Address::Bob, | |
}; | |
let tx1 = Transaction { | |
inputs: vec![Input::dummy()], | |
outputs: vec![coin1.clone()], | |
}; | |
let tx2 = Transaction { | |
inputs: vec![Input::dummy()], | |
outputs: vec![coin2.clone()], | |
}; | |
let coin_id1: CoinId = tx1.coin_id(0); | |
let coin_id2: CoinId = tx2.coin_id(0); | |
// Create a minimal chain to contain this transaction and sync it | |
let mut node = MockNode::new(); | |
node.add_block_as_best(Block::genesis().id(), vec![tx1, tx2]); | |
let mut wallet = wallet_with_alice_and_bob(); | |
wallet.sync(&node); | |
// Check that the coins have been correctly accounted for | |
assert_eq!(wallet.total_assets_of(Address::Alice), Ok(COIN_VALUE)); | |
assert_eq!(wallet.total_assets_of(Address::Bob), Ok(COIN_VALUE * 2)); | |
assert_eq!(wallet.net_worth(), COIN_VALUE * 3); | |
assert_eq!( | |
wallet.all_coins_of(Address::Alice), | |
Ok(vec![(coin_id1, COIN_VALUE)]) | |
); | |
assert_eq!( | |
wallet.all_coins_of(Address::Bob), | |
Ok(vec![(coin_id2, COIN_VALUE * 2)]) | |
); | |
assert_eq!(wallet.coin_details(&coin_id1), Ok(coin1)); | |
assert_eq!(wallet.coin_details(&coin_id2), Ok(coin2)); | |
} | |
// Should be able to create a manual transaction by providing inputs and outputs | |
#[test] | |
fn manual_tx_should_succeed() { | |
const COIN_VALUE: u64 = 100; | |
let coin = Coin { | |
value: COIN_VALUE, | |
owner: Address::Alice, | |
}; | |
let tx = Transaction { | |
inputs: vec![Input::dummy()], | |
outputs: vec![coin.clone()], | |
}; | |
let coin_id = tx.coin_id(0); | |
// Create a minimal chain to contain this transaction and sync it | |
let mut node = MockNode::new(); | |
node.add_block_as_best(Block::genesis().id(), vec![tx]); | |
let mut wallet = wallet_with_alice_and_bob(); | |
wallet.sync(&node); | |
let input_coin_ids = vec![coin_id]; | |
let coin_bob = Coin { | |
value: COIN_VALUE, | |
owner: Address::Bob, | |
}; | |
let output_coins = vec![coin_bob]; | |
let tx_result = wallet.create_manual_transaction(input_coin_ids, output_coins.clone()); | |
assert!(&tx_result.is_ok()); | |
let result = tx_result.unwrap().clone(); | |
assert_eq!(result.outputs, output_coins); | |
assert_eq!(result.inputs[0].signature, Signature::Valid(Address::Alice)); | |
} | |
// Should return an error when unknown coin is provided to create a manual transaction | |
#[test] | |
fn manual_tx_missing_input() { | |
const COIN_VALUE: u64 = 100; | |
// Create a minimal chain to contain this transaction and sync it | |
let mut node = MockNode::new(); | |
node.add_block_as_best(Block::genesis().id(), Vec::new()); | |
let mut wallet = wallet_with_alice_and_bob(); | |
wallet.sync(&node); | |
let dummy_input = Input::dummy(); | |
let input_coin_ids = vec![dummy_input.coin_id]; | |
let coin_bob = Coin { | |
value: COIN_VALUE, | |
owner: Address::Bob, | |
}; | |
let output_coins = vec![coin_bob]; | |
let tx_result = wallet.create_manual_transaction(input_coin_ids, output_coins.clone()); | |
assert!(tx_result.is_err()); | |
assert_eq!(tx_result.unwrap_err(), WalletError::UnknownCoin); | |
} | |
// Should return an error if the output value is greater than input value | |
#[test] | |
fn manual_tx_too_much_output() { | |
const COIN_VALUE: u64 = 100; | |
let coin = Coin { | |
value: COIN_VALUE, | |
owner: Address::Alice, | |
}; | |
let tx = Transaction { | |
inputs: vec![Input::dummy()], | |
outputs: vec![coin.clone()], | |
}; | |
let coin_id = tx.coin_id(0); | |
// Create a minimal chain to contain this transaction and sync it | |
let mut node = MockNode::new(); | |
node.add_block_as_best(Block::genesis().id(), vec![tx]); | |
let mut wallet = wallet_with_alice_and_bob(); | |
wallet.sync(&node); | |
let input_coin_ids = vec![coin_id]; | |
let coin_bob = Coin { | |
value: COIN_VALUE, | |
owner: Address::Bob, | |
}; | |
let coin_alice = Coin { | |
value: COIN_VALUE, | |
owner: Address::Alice, | |
}; | |
let output_coins = vec![coin_bob, coin_alice]; | |
let tx_result = wallet.create_manual_transaction(input_coin_ids, output_coins.clone()); | |
assert!(tx_result.is_err()); | |
assert_eq!(tx_result.unwrap_err(), WalletError::OutputsExceedInputs); | |
} | |
// Should be able to create an automatic transaction | |
#[test] | |
fn automatic_tx_should_succeed_zero_tip() { | |
const COIN_VALUE: u64 = 100; | |
let coin = Coin { | |
value: COIN_VALUE, | |
owner: Address::Alice, | |
}; | |
let tx = Transaction { | |
inputs: vec![Input::dummy()], | |
outputs: vec![coin.clone()], | |
}; | |
let coin_id = tx.coin_id(0); | |
// Create a minimal chain to contain this transaction and sync it | |
let mut node = MockNode::new(); | |
node.add_block_as_best(Block::genesis().id(), vec![tx]); | |
let mut wallet = wallet_with_alice_and_bob(); | |
wallet.sync(&node); | |
let coin_charlie = Coin { | |
value: COIN_VALUE, | |
owner: Address::Charlie, | |
}; | |
let output_coins = vec![coin_charlie]; | |
let tx_result = wallet.create_automatic_transaction(Address::Charlie, COIN_VALUE, 0); | |
assert!(tx_result.is_ok()); | |
let results = tx_result.unwrap(); | |
assert_eq!(results.outputs, output_coins); | |
assert_eq!(results.inputs[0].coin_id, coin_id); | |
assert_eq!( | |
results.inputs[0].signature, | |
Signature::Valid(Address::Alice) | |
); | |
} | |
// Should be able to create an automatic transaction with a tip | |
#[test] | |
fn automatic_tx_should_succeed_with_tip() { | |
const COIN_VALUE: u64 = 100; | |
let coin = Coin { | |
value: COIN_VALUE * 2, | |
owner: Address::Alice, | |
}; | |
let tx = Transaction { | |
inputs: vec![Input::dummy()], | |
outputs: vec![coin.clone()], | |
}; | |
let coin_id = tx.coin_id(0); | |
// Create a minimal chain to contain this transaction and sync it | |
let mut node = MockNode::new(); | |
node.add_block_as_best(Block::genesis().id(), vec![tx]); | |
let mut wallet = wallet_with_alice_and_bob(); | |
wallet.sync(&node); | |
let coin_charlie = Coin { | |
value: COIN_VALUE, | |
owner: Address::Charlie, | |
}; | |
let output_coins = vec![coin_charlie]; | |
let tx_result = wallet.create_automatic_transaction(Address::Charlie, COIN_VALUE, 100); | |
assert!(tx_result.is_ok()); | |
let results = tx_result.unwrap(); | |
assert_eq!(results.outputs, output_coins); | |
assert_eq!(results.inputs[0].coin_id, coin_id); | |
assert_eq!( | |
results.inputs[0].signature, | |
Signature::Valid(Address::Alice) | |
); | |
} | |
// Should be able to validate when output value exceeds available inputs | |
#[test] | |
fn automatic_tx_too_much_output() { | |
const COIN_VALUE: u64 = 100; | |
let coin = Coin { | |
value: COIN_VALUE, | |
owner: Address::Alice, | |
}; | |
let tx = Transaction { | |
inputs: vec![Input::dummy()], | |
outputs: vec![coin.clone()], | |
}; | |
// Create a minimal chain to contain this transaction and sync it | |
let mut node = MockNode::new(); | |
node.add_block_as_best(Block::genesis().id(), vec![tx]); | |
let mut wallet = wallet_with_alice_and_bob(); | |
wallet.sync(&node); | |
let tx_result = wallet.create_automatic_transaction(Address::Charlie, 2 * COIN_VALUE, 0); | |
assert!(tx_result.is_err()); | |
} | |
// Reorgs with utxos in the chain history | |
#[test] | |
fn reorg_with_utxos_input() { | |
// Create node and wallet | |
let mut node = MockNode::new(); | |
let mut wallet = wallet_with_alice(); | |
// We have a single transaction that consumes some made up input | |
// and creates a single output to alice. | |
const COIN_VALUE: u64 = 100; | |
let coin = Coin { | |
value: COIN_VALUE, | |
owner: Address::Alice, | |
}; | |
let tx = Transaction { | |
inputs: vec![Input::dummy()], | |
outputs: vec![coin.clone()], | |
}; | |
let coin_id = tx.coin_id(0); | |
// Sync a chain to height 3 | |
let old_b1_id = node.add_block_as_best(Block::genesis().id(), vec![]); | |
let old_b2_id = node.add_block_as_best(old_b1_id, vec![]); | |
let _old_b3_id = node.add_block_as_best(old_b2_id, vec![tx]); | |
wallet.sync(&node); | |
// Reorg to shorter chain of length 2 | |
let b1_id = node.add_block(Block::genesis().id(), vec![]); | |
let b2_id = node.add_block_as_best(b1_id, vec![]); | |
wallet.sync(&node); | |
assert_eq!(wallet.best_height(), 2); | |
assert_eq!(wallet.best_hash(), b2_id); | |
// Check that the calculation is right | |
assert_eq!(wallet.total_assets_of(Address::Alice), Ok(0)); | |
assert_eq!(wallet.net_worth(), 0); | |
assert_eq!(wallet.all_coins_of(Address::Alice), Ok(vec![])); | |
assert_eq!(wallet.coin_details(&coin_id), Err(WalletError::UnknownCoin)); | |
} | |
// Reorgs with utxos in the chain history | |
#[test] | |
fn reorg_with_utxos_output() { | |
// Create node and wallet | |
let mut node = MockNode::new(); | |
let mut wallet = wallet_with_alice(); | |
const COIN_VALUE: u64 = 100; | |
let coin = Coin { | |
value: COIN_VALUE, | |
owner: Address::Alice, | |
}; | |
let mint_tx = Transaction { | |
inputs: vec![Input::dummy()], | |
outputs: vec![coin.clone()], | |
}; | |
let coin_id = mint_tx.coin_id(0); | |
let spend_tx = Transaction { | |
inputs: vec![Input { | |
coin_id: coin_id.clone(), | |
signature: Signature::Invalid, | |
}], | |
outputs: vec![], | |
}; | |
// Sync a chain to height 5 | |
let old_b1_id = node.add_block_as_best(Block::genesis().id(), vec![mint_tx]); | |
let old_b2_id = node.add_block_as_best(old_b1_id, vec![]); | |
let old_b3_id = node.add_block_as_best(old_b2_id, vec![]); | |
let old_b4_id = node.add_block_as_best(old_b3_id, vec![spend_tx]); | |
let _old_b5_id = node.add_block_as_best(old_b4_id, vec![]); | |
wallet.sync(&node); | |
// Check that the coins have been correctly accounted for | |
assert_eq!(wallet.total_assets_of(Address::Alice), Ok(0)); | |
assert_eq!(wallet.net_worth(), 0); | |
assert_eq!(wallet.all_coins_of(Address::Alice), Ok(vec![])); | |
assert_eq!(wallet.coin_details(&coin_id), Err(WalletError::UnknownCoin)); | |
// Reorg to longer chain of same length | |
let b4_id = node.add_block_as_best(old_b3_id, vec![]); | |
let b5_id = node.add_block_as_best(b4_id, vec![]); | |
wallet.sync(&node); | |
assert_eq!(wallet.best_height(), 5); | |
assert_eq!(wallet.best_hash(), b5_id); | |
// Check that the coins have been correctly accounted for | |
assert_eq!(wallet.total_assets_of(Address::Alice), Ok(COIN_VALUE)); | |
assert_eq!(wallet.net_worth(), COIN_VALUE); | |
assert_eq!( | |
wallet.all_coins_of(Address::Alice), | |
Ok(vec![(coin_id, COIN_VALUE)]) | |
); | |
assert_eq!(wallet.coin_details(&coin_id), Ok(coin)); | |
} | |
// Reorgs with utxos in the chain history | |
#[test] | |
fn reorg_with_utxos_complete() { | |
// Create node and wallet | |
let mut node = MockNode::new(); | |
let mut wallet = wallet_with_alice_and_bob(); | |
const COIN_VALUE: u64 = 100; | |
let coin = Coin { | |
value: COIN_VALUE, | |
owner: Address::Alice, | |
}; | |
let mint_tx = Transaction { | |
inputs: vec![Input::dummy()], | |
outputs: vec![coin.clone()], | |
}; | |
let coin_id = mint_tx.coin_id(0); | |
let spend_tx = Transaction { | |
inputs: vec![Input { | |
coin_id: coin_id.clone(), | |
signature: Signature::Invalid, | |
}], | |
outputs: vec![Coin { | |
value: COIN_VALUE, | |
owner: Address::Bob, | |
}], | |
}; | |
// Sync a chain to height 5 | |
let old_b1_id = node.add_block_as_best(Block::genesis().id(), vec![mint_tx]); | |
let old_b2_id = node.add_block_as_best(old_b1_id, vec![]); | |
let old_b3_id = node.add_block_as_best(old_b2_id, vec![]); | |
let old_b4_id = node.add_block_as_best(old_b3_id, vec![spend_tx]); | |
let _old_b5_id = node.add_block_as_best(old_b4_id, vec![]); | |
wallet.sync(&node); | |
// Check that the coins have been correctly accounted for | |
assert_eq!(wallet.total_assets_of(Address::Alice), Ok(0)); | |
assert_eq!(wallet.total_assets_of(Address::Bob), Ok(COIN_VALUE)); | |
assert_eq!(wallet.net_worth(), COIN_VALUE); | |
assert_eq!(wallet.all_coins_of(Address::Alice), Ok(vec![])); | |
assert_eq!(wallet.coin_details(&coin_id), Err(WalletError::UnknownCoin)); | |
// Reorg to longer chain of same length | |
let b4_id = node.add_block_as_best(old_b3_id, vec![]); | |
let b5_id = node.add_block_as_best(b4_id, vec![]); | |
wallet.sync(&node); | |
assert_eq!(wallet.best_height(), 5); | |
assert_eq!(wallet.best_hash(), b5_id); | |
// Check that the coins have been correctly accounted for | |
assert_eq!(wallet.total_assets_of(Address::Alice), Ok(COIN_VALUE)); | |
assert_eq!(wallet.total_assets_of(Address::Bob), Ok(0)); | |
assert_eq!(wallet.net_worth(), COIN_VALUE); | |
assert_eq!( | |
wallet.all_coins_of(Address::Alice), | |
Ok(vec![(coin_id, COIN_VALUE)]) | |
); | |
assert_eq!(wallet.all_coins_of(Address::Bob), Ok(vec![])); | |
assert_eq!(wallet.coin_details(&coin_id), Ok(coin)); | |
} | |
// Reorg performance tests to make sure they aren't just syncing from genesis each time. | |
#[test] | |
fn reorg_performance_test() { | |
// Create node and wallet | |
let mut node = MockNode::new(); | |
let mut wallet = wallet_with_alice(); | |
// Sync a chain to height 5 | |
let old_b1_id = node.add_block_as_best(Block::genesis().id(), vec![]); | |
let old_b2_id = node.add_block_as_best(old_b1_id, vec![]); | |
let old_b3_id = node.add_block_as_best(old_b2_id, vec![]); | |
let old_b4_id = node.add_block_as_best(old_b3_id, vec![]); | |
let _old_b5_id = node.add_block_as_best(old_b4_id, vec![]); | |
wallet.sync(&node); | |
let regular_sync_calls = node.how_many_queries(); | |
// Reorg to longer chain of same length | |
let b4_id = node.add_block_as_best(old_b3_id, vec![]); | |
let b5_id = node.add_block_as_best(b4_id, vec![]); | |
wallet.sync(&node); | |
let reorg_calls = node.how_many_queries() - regular_sync_calls; | |
assert!(reorg_calls < regular_sync_calls); | |
} | |
// Check the reorg case until the genesis block | |
// B2 (discard) - B3 (discard) | |
// / | |
// G | |
// \ | |
// C2 - C3 - C4 - C5 (new wallet state) | |
#[test] | |
fn deep_reorg() { | |
let mut node = MockNode::new(); | |
let mut wallet = wallet_with_alice(); | |
// Sync a chain to height 3 | |
let b1_id = node.add_block_as_best(Block::genesis().id(), vec![]); | |
let b2_id = node.add_block_as_best(b1_id, vec![]); | |
let b3_id = node.add_block_as_best(b2_id, vec![]); | |
wallet.sync(&node); | |
let c1_id = node.add_block(Block::genesis().id(), vec![marker_tx()]); | |
let c2_id = node.add_block_as_best(c1_id, vec![]); | |
let c3_id = node.add_block_as_best(c2_id, vec![]); | |
let c4_id = node.add_block_as_best(c3_id, vec![]); | |
let c5_id = node.add_block_as_best(c4_id, vec![]); | |
wallet.sync(&node); | |
assert_eq!(wallet.best_height(), 5); | |
assert_eq!(wallet.best_hash(), c5_id); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment