Skip to content

Commit

Permalink
fix(WalletState): Filter out unspendable UTXOs before monitoring
Browse files Browse the repository at this point in the history
Before this commit, the wallet would add UTXOs to the database of
monitored UTXOs even if it has an unrecognized or even unsatisfiable
type script. This commit adds a filter to the process of upgrading
expected UTXOs to monitored UTXOs, guaranteeing that all type scripts
are known.

Also:
 - Add test for above refactor.
 - Improve some functions' names.
 - Factor out common test code.

Co-authored-by: Alan Szepieniec <[email protected]>
  • Loading branch information
Sword-Smith and aszepieniec committed Jan 9, 2025
1 parent 1b4cce4 commit 226a371
Show file tree
Hide file tree
Showing 9 changed files with 208 additions and 60 deletions.
2 changes: 1 addition & 1 deletion src/mine_loop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1118,7 +1118,7 @@ pub(crate) mod mine_loop_tests {
.await
.get_wallet_status_for_tip()
.await
.synced_unspent_available_amount(now)
.synced_unspent_liquid_amount(now)
.is_zero(),
"Assumed to be premine-recipient"
);
Expand Down
13 changes: 10 additions & 3 deletions src/models/blockchain/transaction/utxo.rs
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@ impl Utxo {
/// Determine whether the UTXO has coins that contain only known type
/// scripts. If other type scripts are included, then we cannot spend
/// this UTXO.
pub fn has_known_type_scripts(&self) -> bool {
pub fn all_type_scripts_are_known(&self) -> bool {
let known_type_script_hashes = [NativeCurrency.hash(), TimeLock.hash()];
self.coins
.iter()
Expand All @@ -175,7 +175,7 @@ impl Utxo {
pub fn can_spend_at(&self, timestamp: Timestamp) -> bool {
crate::macros::log_slow_scope!();
// unknown type script
if !self.has_known_type_scripts() {
if !self.all_type_scripts_are_known() {
return false;
}

Expand Down Expand Up @@ -204,7 +204,7 @@ impl Utxo {
/// Determine whether the only thing preventing the UTXO from being spendable
/// is the timelock whose according release date is in the future.
pub fn is_timelocked_but_otherwise_spendable_at(&self, timestamp: Timestamp) -> bool {
if !self.has_known_type_scripts() {
if !self.all_type_scripts_are_known() {
return false;
}

Expand Down Expand Up @@ -307,6 +307,13 @@ mod test {
(lock_script_hash, coins).into()
}

impl Utxo {
pub(crate) fn with_coin(mut self, coin: Coin) -> Self {
self.coins.push(coin);
self
}
}

#[test]
fn hash_utxo_test() {
let output = make_random_utxo();
Expand Down
12 changes: 6 additions & 6 deletions src/models/state/archival_state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1963,15 +1963,15 @@ mod archival_state_tests {
.await
.get_wallet_status_for_tip()
.await
.synced_unspent_available_amount(in_seven_months)
.synced_unspent_liquid_amount(in_seven_months)
);
assert_eq!(
NeptuneCoins::new(5),
bob.lock_guard()
.await
.get_wallet_status_for_tip()
.await
.synced_unspent_available_amount(in_seven_months)
.synced_unspent_liquid_amount(in_seven_months)
);

let block_subsidy = Block::block_subsidy(block_1.header().height);
Expand All @@ -1988,7 +1988,7 @@ mod archival_state_tests {
.await
.get_wallet_status_for_tip()
.await
.synced_unspent_available_amount(in_seven_months)
.synced_unspent_liquid_amount(in_seven_months)
);

let after_cb_timelock_expiration = block_1.header().timestamp + Timestamp::months(37);
Expand All @@ -1999,7 +1999,7 @@ mod archival_state_tests {
.await
.get_wallet_status_for_tip()
.await
.synced_unspent_available_amount(after_cb_timelock_expiration)
.synced_unspent_liquid_amount(after_cb_timelock_expiration)
);

println!("Transactions were received in good order.");
Expand Down Expand Up @@ -2177,14 +2177,14 @@ mod archival_state_tests {
.await
.get_wallet_status_for_tip()
.await
.synced_unspent_available_amount(in_seven_months)
.synced_unspent_liquid_amount(in_seven_months)
.is_zero());
assert!(bob
.lock_guard()
.await
.get_wallet_status_for_tip()
.await
.synced_unspent_available_amount(in_seven_months)
.synced_unspent_liquid_amount(in_seven_months)
.is_zero());

// Verify that all ingoing UTXOs are recorded in wallet of receiver of genesis UTXO
Expand Down
16 changes: 8 additions & 8 deletions src/models/state/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1820,7 +1820,7 @@ mod global_state_tests {
assert!(!alice
.get_wallet_status_for_tip()
.await
.synced_unspent_available_amount(launch + seven_months)
.synced_unspent_liquid_amount(launch + seven_months)
.is_zero());

// Verify that this is unsynced with mock_block_1a
Expand Down Expand Up @@ -2338,15 +2338,15 @@ mod global_state_tests {
.await
.get_wallet_status_for_tip()
.await
.synced_unspent_available_amount(in_seven_months)
.synced_unspent_liquid_amount(in_seven_months)
);
assert_eq!(
NeptuneCoins::new(7),
bob.lock_guard()
.await
.get_wallet_status_for_tip()
.await
.synced_unspent_available_amount(in_seven_months)
.synced_unspent_liquid_amount(in_seven_months)
);
// TODO: No idea why this isn't working.
// {
Expand Down Expand Up @@ -3088,7 +3088,7 @@ mod global_state_tests {
let alice_initial_balance = alice_state_mut
.get_wallet_status_for_tip()
.await
.synced_unspent_available_amount(seven_months_post_launch);
.synced_unspent_liquid_amount(seven_months_post_launch);
assert_eq!(alice_initial_balance, NeptuneCoins::new(20));

// create change key for alice. change_key_type is a test param.
Expand Down Expand Up @@ -3185,7 +3185,7 @@ mod global_state_tests {
alice_state_mut
.get_wallet_status_for_tip()
.await
.synced_unspent_available_amount(seven_months_post_launch)
.synced_unspent_liquid_amount(seven_months_post_launch)
);

block_1
Expand All @@ -3204,7 +3204,7 @@ mod global_state_tests {
bob_state_mut
.get_wallet_status_for_tip()
.await
.synced_unspent_available_amount(seven_months_post_launch)
.synced_unspent_liquid_amount(seven_months_post_launch)
);
}

Expand Down Expand Up @@ -3241,7 +3241,7 @@ mod global_state_tests {
let alice_initial_balance = alice_state_mut
.get_wallet_status_for_tip()
.await
.synced_unspent_available_amount(seven_months_post_launch);
.synced_unspent_liquid_amount(seven_months_post_launch);

// lucky alice's wallet begins with 20 balance from premine.
assert_eq!(alice_initial_balance, NeptuneCoins::new(20));
Expand Down Expand Up @@ -3272,7 +3272,7 @@ mod global_state_tests {
alice_state_mut
.get_wallet_status_for_tip()
.await
.synced_unspent_available_amount(seven_months_post_launch)
.synced_unspent_liquid_amount(seven_months_post_launch)
);
}
}
Expand Down
4 changes: 2 additions & 2 deletions src/models/state/wallet/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -900,7 +900,7 @@ mod wallet_tests {
let bobs_original_balance = bob
.get_wallet_status_for_tip()
.await
.synced_unspent_available_amount(in_seven_months);
.synced_unspent_liquid_amount(in_seven_months);
assert!(
!bobs_original_balance.is_zero(),
"Premine must have non-zero synced balance"
Expand Down Expand Up @@ -958,7 +958,7 @@ mod wallet_tests {
.unwrap(),
bob.get_wallet_status_for_tip()
.await
.synced_unspent_available_amount(in_seven_months),
.synced_unspent_liquid_amount(in_seven_months),
"Preminer must have spent 15: 12 + 1 for sent, 2 for fees"
);

Expand Down
13 changes: 13 additions & 0 deletions src/models/state/wallet/transaction_output.rs
Original file line number Diff line number Diff line change
Expand Up @@ -368,12 +368,25 @@ mod tests {
use super::*;
use crate::config_models::cli_args;
use crate::config_models::network::Network;
use crate::models::blockchain::transaction::utxo::Coin;
use crate::models::blockchain::type_scripts::neptune_coins::NeptuneCoins;
use crate::models::state::wallet::address::generation_address::GenerationReceivingAddress;
use crate::models::state::wallet::address::KeyType;
use crate::models::state::wallet::WalletSecret;
use crate::tests::shared::mock_genesis_global_state;

impl TxOutput {
pub(crate) fn with_coin(self, coin: Coin) -> Self {
Self {
utxo: self.utxo.with_coin(coin),
sender_randomness: self.sender_randomness,
receiver_digest: self.receiver_digest,
notification_method: self.notification_method,
owned: self.owned,
}
}
}

#[tokio::test]
async fn test_utxoreceiver_auto_not_owned_output() {
let global_state_lock = mock_genesis_global_state(
Expand Down
Loading

0 comments on commit 226a371

Please sign in to comment.