Skip to content
Open
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
4 changes: 2 additions & 2 deletions test/functional/wallet_multisig_address.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,9 +103,9 @@ async def async_test(self):
assert_in("The transaction was submitted successfully", await wallet.submit_transaction(encoded_tx, not store_tx_in_wallet))

if store_tx_in_wallet:
assert_in(f"Coins amount: {coins_to_send}", await wallet.get_balance(utxo_states=['inactive']))
assert_in(f"Coins amount: {coins_to_send}", await wallet.get_balance(utxo_states=['inactive', 'in-mempool']))
else:
assert_in(f"Coins amount: 0", await wallet.get_balance(utxo_states=['inactive']))
assert_in(f"Coins amount: 0", await wallet.get_balance(utxo_states=['inactive', 'in-mempool']))

assert node.mempool_contains_tx(receive_coins_tx_id)

Expand Down
2 changes: 1 addition & 1 deletion test/functional/wallet_submit_tx.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ async def async_test(self):
assert_in("The transaction was submitted successfully", await wallet.submit_transaction(encoded_tx, not store_tx_in_wallet))

if store_tx_in_wallet:
assert_in(f"Coins amount: {coins_to_send}", await wallet.get_balance(utxo_states=['inactive']))
assert_in(f"Coins amount: {coins_to_send}", await wallet.get_balance(utxo_states=['inactive', 'in-mempool']))
else:
assert_in(f"Coins amount: 0", await wallet.get_balance(utxo_states=['inactive']))

Expand Down
2 changes: 1 addition & 1 deletion test/functional/wallet_tx_compose.py
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,7 @@ def make_output(pub_key_bytes):

assert_in("The transaction was submitted successfully", await wallet.submit_transaction(signed_tx))

utxos = await wallet.list_utxos('all', 'unlocked', ['inactive'])
utxos = await wallet.list_utxos('all', 'unlocked', ['inactive', 'in-mempool'])
assert_equal(1, len(utxos))

# try to compose and sign a transaction with an inactive utxo that is not in chainstate only in the wallet
Expand Down
2 changes: 1 addition & 1 deletion test/functional/wallet_watch_address.py
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ async def async_test(self):
assert_in("The transaction was submitted successfully", await wallet.submit_transaction(encoded_tx, not store_tx_in_wallet))

if store_tx_in_wallet:
assert_in(f"Coins amount: {coins_to_send}", await wallet.get_balance(utxo_states=['inactive']))
assert_in(f"Coins amount: {coins_to_send}", await wallet.get_balance(utxo_states=['inactive', 'in-mempool']))
else:
assert_in("Coins amount: 0", await wallet.get_balance(utxo_states=['inactive']))

Expand Down
14 changes: 8 additions & 6 deletions wallet/src/account/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ use wallet_types::{

pub use self::output_cache::{
DelegationData, OrderData, OutputCacheInconsistencyError, OwnFungibleTokenInfo, PoolData,
TxInfo, UnconfirmedTokenInfo, UtxoWithTxOutput,
TxChanged, TxInfo, UnconfirmedTokenInfo, UtxoWithTxOutput,
};
use self::output_cache::{OutputCache, TokenIssuanceData};
use self::transaction_list::{get_transaction_list, TransactionList};
Expand Down Expand Up @@ -2089,14 +2089,16 @@ impl<K: AccountKeyChains> Account<K> {
let relevant_outputs = self.mark_outputs_as_seen(db_tx, tx.outputs())?;
if relevant_inputs || relevant_outputs {
let id = AccountWalletTxId::new(self.get_account_id(), tx.id());
db_tx.set_transaction(&id, &tx)?;
wallet_events.set_transaction(self.account_index(), &tx);
self.output_cache.add_tx(
let changed = self.output_cache.add_tx(
&self.chain_config,
self.account_info.best_block_height(),
id.into_item_id(),
tx,
id.clone().into_item_id(),
tx.clone(),
)?;
if changed == TxChanged::Yes {
db_tx.set_transaction(&id, &tx)?;
wallet_events.set_transaction(self.account_index(), &tx);
}
Ok(true)
} else {
Ok(false)
Expand Down
14 changes: 11 additions & 3 deletions wallet/src/account/output_cache/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,14 @@ impl TxInfo {
}
}

/// Result when adding a transaction representing if anything was changed or the tx already existed
/// in the same state and nothing changed
#[derive(Debug, Eq, PartialEq, Clone, Copy)]
pub enum TxChanged {
No,
Yes,
}

pub struct DelegationData {
pub pool_id: PoolId,
pub destination: Destination,
Expand Down Expand Up @@ -970,7 +978,7 @@ impl OutputCache {
best_block_height: BlockHeight,
tx_id: OutPointSourceId,
tx: WalletTx,
) -> WalletResult<()> {
) -> WalletResult<TxChanged> {
let existing_tx = self.txs.get(&tx_id);
let existing_tx_already_confirmed_or_same = existing_tx.is_some_and(|existing_tx| {
matches!(
Expand All @@ -984,7 +992,7 @@ impl OutputCache {
});

if existing_tx_already_confirmed_or_same {
return Ok(());
return Ok(TxChanged::No);
}

let already_present = existing_tx.is_some_and(|tx| match tx.state() {
Expand Down Expand Up @@ -1016,7 +1024,7 @@ impl OutputCache {
)?;

self.txs.insert(tx_id, tx);
Ok(())
Ok(TxChanged::Yes)
}

/// Update the pool states for a newly confirmed transaction
Expand Down
10 changes: 7 additions & 3 deletions wallet/src/account/output_cache/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -850,14 +850,15 @@ fn test_add_tx_state_transitions_logic(#[case] seed: Seed) {
let tx_a_source_id: OutPointSourceId = tx_a_id.into();

// Add A as Inactive
output_cache
let result = output_cache
.add_tx(
&chain_config,
best_block_height,
tx_a_source_id.clone(),
WalletTx::Tx(TxData::new(tx_a.clone(), TxState::Inactive(0))),
)
.unwrap();
assert_eq!(result, TxChanged::Yes);

assert!(
output_cache.unconfirmed_descendants.contains_key(&tx_a_source_id),
Expand All @@ -867,14 +868,15 @@ fn test_add_tx_state_transitions_logic(#[case] seed: Seed) {
// Add A as Confirmed
let confirmed_state =
TxState::Confirmed(BlockHeight::new(1), BlockTimestamp::from_int_seconds(0), 0);
output_cache
let result = output_cache
.add_tx(
&chain_config,
best_block_height,
tx_a_source_id.clone(),
WalletTx::Tx(TxData::new(tx_a.clone(), confirmed_state)),
)
.unwrap();
assert_eq!(result, TxChanged::Yes);

assert!(
!output_cache.unconfirmed_descendants.contains_key(&tx_a_source_id),
Expand All @@ -884,7 +886,7 @@ fn test_add_tx_state_transitions_logic(#[case] seed: Seed) {
// Add A as InMempool
// Because existing state is Confirmed, existing_tx_already_confirmed_or_same
// returns true. The state remains Confirmed (and not unconfirmed).
output_cache
let result = output_cache
.add_tx(
&chain_config,
best_block_height,
Expand All @@ -893,6 +895,8 @@ fn test_add_tx_state_transitions_logic(#[case] seed: Seed) {
)
.unwrap();

assert_eq!(result, TxChanged::No);

assert!(
!output_cache.unconfirmed_descendants.contains_key(&tx_a_source_id),
"Tx A should still NOT be in unconfirmed_descendants because the update to InMempool was ignored"
Expand Down
33 changes: 28 additions & 5 deletions wallet/wallet-rpc-lib/tests/basic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -214,20 +214,43 @@ async fn stake_and_send_coins_to_acct1(#[case] seed: Seed) {

// Start staking on account 0 to hopefully create a block that contains our transaction
let _: () = wallet_rpc.request("staking_start", [ACCOUNT0_ARG]).await.unwrap();

tokio::time::sleep(std::time::Duration::from_millis(300)).await;
let evt3 = EventInfo::from_json(wallet_events.next().await.unwrap().unwrap());
assert_eq!(evt3, EventInfo::RewardAdded {});

// Expect 2 TxUpdated events, one from each account received from the mempool events, and one
// RewardAdded when the new block comes.
let evt3 = EventInfo::from_json(wallet_events.next().await.unwrap().unwrap());
let evt4 = EventInfo::from_json(wallet_events.next().await.unwrap().unwrap());
let evt5 = EventInfo::from_json(wallet_events.next().await.unwrap().unwrap());
let events = [evt3, evt4, evt5];

let mempool_events: Vec<_> = events
.iter()
.filter(|evt| {
matches!(
evt,
EventInfo::TxUpdated {
state: TxState::InMempool { .. },
..
}
) && evt.tx_id() == evt1.tx_id()
})
.collect();

let reward_events: Vec<_> =
events.iter().filter(|evt| matches!(evt, EventInfo::RewardAdded {})).collect();

assert_eq!(mempool_events.len(), 2);
assert_eq!(reward_events.len(), 1);

let evt6 = EventInfo::from_json(wallet_events.next().await.unwrap().unwrap());
assert!(matches!(
evt4,
evt6,
EventInfo::TxUpdated {
state: TxState::Confirmed { .. },
id: _,
}
));
assert_eq!(evt4.tx_id(), evt1.tx_id());
assert_eq!(evt6.tx_id(), evt1.tx_id());

std::mem::drop(wallet_rpc);
tf.stop().await;
Expand Down
Loading