From 5df011a81bbd11cc86c16a8037d84b771ee6e578 Mon Sep 17 00:00:00 2001 From: Bert Date: Sun, 21 Sep 2025 13:28:16 +0200 Subject: [PATCH 01/11] GH-662: PayableAccount got the field removed --- masq_lib/src/messages.rs | 12 +- .../db_access_objects/payable_dao.rs | 533 ++++++++++-------- .../src/accountant/db_access_objects/utils.rs | 97 ++-- node/src/accountant/mod.rs | 61 +- node/src/accountant/scanners/mod.rs | 4 - .../src/accountant/scanners/scanners_utils.rs | 6 - node/src/accountant/test_utils.rs | 16 +- node/src/blockchain/blockchain_bridge.rs | 4 - .../blockchain_interface_web3/utils.rs | 2 - 9 files changed, 400 insertions(+), 335 deletions(-) diff --git a/masq_lib/src/messages.rs b/masq_lib/src/messages.rs index d93332c3d..dbb15dadb 100644 --- a/masq_lib/src/messages.rs +++ b/masq_lib/src/messages.rs @@ -7,6 +7,7 @@ use crate::ui_gateway::MessagePath::{Conversation, FireAndForget}; use crate::utils::to_string; use core::fmt::Display; use core::fmt::Formatter; +use ethereum_types::H256; use itertools::Itertools; use serde::de::DeserializeOwned; use serde_derive::{Deserialize, Serialize}; @@ -679,8 +680,15 @@ pub struct UiPayableAccount { pub age_s: u64, #[serde(rename = "balanceGwei")] pub balance_gwei: u64, - #[serde(rename = "pendingPayableHashOpt")] - pub pending_payable_hash_opt: Option, + #[serde(rename = "currentTxInfoOpt")] + pub current_tx_info_opt: Option, +} + +#[derive(Serialize, Deserialize, Debug, PartialEq, Eq)] +pub struct CurrentTxInfo { + #[serde(rename = "pendingOpt")] + pub pending_opt: Option, + pub failures: usize, } #[derive(Serialize, Deserialize, Debug, PartialEq, Eq)] diff --git a/node/src/accountant/db_access_objects/payable_dao.rs b/node/src/accountant/db_access_objects/payable_dao.rs index cff264a58..a139193b9 100644 --- a/node/src/accountant/db_access_objects/payable_dao.rs +++ b/node/src/accountant/db_access_objects/payable_dao.rs @@ -4,7 +4,7 @@ use crate::accountant::db_access_objects::sent_payable_dao::SentTx; use crate::accountant::db_access_objects::utils; use crate::accountant::db_access_objects::utils::{ sum_i128_values_from_table, to_unix_timestamp, AssemblerFeeder, CustomQuery, DaoFactoryReal, - RangeStmConfig, RowId, TopStmConfig, TxHash, VigilantRusqliteFlatten, + PayableAccountWithTxInfo, RangeStmConfig, RowId, TopStmConfig, TxHash, VigilantRusqliteFlatten, }; use crate::accountant::db_big_integer::big_int_db_processor::KeyVariants::WalletAddress; use crate::accountant::db_big_integer::big_int_db_processor::{ @@ -38,7 +38,6 @@ pub struct PayableAccount { pub wallet: Wallet, pub balance_wei: u128, pub last_paid_timestamp: SystemTime, - pub pending_payable_opt: Option, } pub trait PayableDao: Debug + Send { @@ -58,7 +57,8 @@ pub trait PayableDao: Debug + Send { fn non_pending_payables(&self) -> Vec; - fn custom_query(&self, custom_query: CustomQuery) -> Option>; + fn custom_query(&self, custom_query: CustomQuery) + -> Option>; fn total(&self) -> u128; @@ -193,7 +193,6 @@ impl PayableDao for PayableDaoReal { high_b, low_b, )), last_paid_timestamp: utils::from_unix_timestamp(last_paid_timestamp), - pending_payable_opt: None, }) } _ => panic!("Database is corrupt: PAYABLE table columns and/or types"), @@ -204,7 +203,10 @@ impl PayableDao for PayableDaoReal { .collect() } - fn custom_query(&self, custom_query: CustomQuery) -> Option> { + fn custom_query( + &self, + custom_query: CustomQuery, + ) -> Option> { let variant_top = TopStmConfig{ limit_clause: "limit :limit_count", gwei_min_resolution_clause: "where (balance_high_b > 0) or ((balance_high_b = 0) and (balance_low_b >= 1000000000))", @@ -217,14 +219,14 @@ impl PayableDao for PayableDaoReal { gwei_min_resolution_clause: "and ((balance_high_b > 0) or ((balance_high_b = 0) and (balance_low_b >= 1000000000)))", secondary_order_param: "last_paid_timestamp asc" }; - - custom_query.query::<_, i64, _, _>( - self.conn.as_ref(), - Self::stm_assembler_of_payable_cq, - variant_top, - variant_range, - Self::create_payable_account, - ) + todo!() + // custom_query.query::<_, i64, _, _>( + // self.conn.as_ref(), + // Self::stm_assembler_of_payable_cq, + // variant_top, + // variant_range, + // Self::create_payable_account, + // ) } fn total(&self) -> u128 { @@ -279,13 +281,6 @@ impl PayableDao for PayableDaoReal { high_bytes, low_bytes, )), last_paid_timestamp: utils::from_unix_timestamp(last_paid_timestamp), - pending_payable_opt: match rowid { - Some(rowid) => Some(PendingPayableId::new( - u64::try_from(rowid).unwrap(), - H256::from_uint(&U256::from(0)), //garbage - )), - None => None, - }, }) } e => panic!( @@ -325,7 +320,6 @@ impl PayableDaoReal { high_bytes, low_bytes, )), last_paid_timestamp: utils::from_unix_timestamp(last_paid_timestamp), - pending_payable_opt: None, }) } e => panic!( @@ -517,15 +511,22 @@ impl TableNameDAO for PayableDaoReal { #[cfg(test)] mod tests { use super::*; - use crate::accountant::db_access_objects::sent_payable_dao::SentTx; + use crate::accountant::db_access_objects::failed_payable_dao; + use crate::accountant::db_access_objects::failed_payable_dao::{ + FailedPayableDao, FailedPayableDaoReal, + }; + use crate::accountant::db_access_objects::sent_payable_dao::{ + SentPayableDao, SentPayableDaoReal, SentTx, TxStatus, + }; use crate::accountant::db_access_objects::utils::{ current_unix_timestamp, from_unix_timestamp, to_unix_timestamp, }; use crate::accountant::gwei_to_wei; use crate::accountant::test_utils::{ - assert_account_creation_fn_fails_on_finding_wrong_columns_and_value_types, make_sent_tx, - trick_rusqlite_with_read_only_conn, + assert_account_creation_fn_fails_on_finding_wrong_columns_and_value_types, make_failed_tx, + make_sent_tx, trick_rusqlite_with_read_only_conn, FailedPayableDaoFactoryMock, }; + use crate::blockchain::errors::validation_status::ValidationStatus; use crate::blockchain::test_utils::make_tx_hash; use crate::database::db_initializer::{ DbInitializationConfig, DbInitializer, DbInitializerReal, DATABASE_FILE, @@ -533,6 +534,7 @@ mod tests { use crate::database::rusqlite_wrappers::ConnectionWrapperReal; use crate::test_utils::make_wallet; use itertools::Itertools; + use masq_lib::messages::CurrentTxInfo; use masq_lib::messages::TopRecordsOrdering::{Age, Balance}; use masq_lib::test_utils::utils::ensure_node_home_directory_exists; use rusqlite::ToSql; @@ -929,8 +931,6 @@ mod tests { &format!("{:?}", test_inputs.receiver_wallet), i128::try_from(test_inputs.initial_amount_wei).unwrap(), to_unix_timestamp(test_inputs.previous_timestamp), - // TODO argument will be eliminated in GH-662 - None, ); let mut sent_tx = make_sent_tx((idx as u64 + 1) * 1234); sent_tx.hash = test_inputs.hash; @@ -1013,18 +1013,15 @@ mod tests { from_unix_timestamp(to_unix_timestamp(setup_holder.account_1.previous_timestamp)); let expected_last_paid_timestamp_2 = from_unix_timestamp(to_unix_timestamp(setup_holder.account_2.previous_timestamp)); - // TODO yes these pending_payable_opt values are unsensible now but it will eventually be all cleaned up with GH-662 let expected_status_before_1 = PayableAccount { wallet: wallet_1.clone(), balance_wei: initial_amount_1, last_paid_timestamp: expected_last_paid_timestamp_1, - pending_payable_opt: None, }; let expected_status_before_2 = PayableAccount { wallet: wallet_2.clone(), balance_wei: initial_amount_2, last_paid_timestamp: expected_last_paid_timestamp_2, - pending_payable_opt: None, }; let expected_resulting_status_1 = PayableAccount { wallet: wallet_1.clone(), @@ -1032,7 +1029,6 @@ mod tests { last_paid_timestamp: from_unix_timestamp( setup_holder.account_1.pending_payable.timestamp, ), - pending_payable_opt: None, }; let expected_resulting_status_2 = PayableAccount { wallet: wallet_2.clone(), @@ -1040,7 +1036,6 @@ mod tests { last_paid_timestamp: from_unix_timestamp( setup_holder.account_2.pending_payable.timestamp, ), - pending_payable_opt: None, }; assert_eq!(status_1_before_opt, Some(expected_status_before_1)); assert_eq!(status_2_before_opt, Some(expected_status_before_2)); @@ -1158,57 +1153,59 @@ mod tests { #[test] fn non_pending_payables_should_return_payables_with_no_pending_transaction() { - let home_dir = ensure_node_home_directory_exists( - "payable_dao", - "non_pending_payables_should_return_payables_with_no_pending_transaction", - ); - let subject = PayableDaoReal::new( - DbInitializerReal::default() - .initialize(&home_dir, DbInitializationConfig::test_default()) - .unwrap(), - ); - let mut flags = OpenFlags::empty(); - flags.insert(OpenFlags::SQLITE_OPEN_READ_WRITE); - let conn = Connection::open_with_flags(&home_dir.join(DATABASE_FILE), flags).unwrap(); - let conn = ConnectionWrapperReal::new(conn); - let insert = |wallet: &str, pending_payable_rowid: Option| { - insert_payable_record_fn( - &conn, - wallet, - 1234567890123456, - 111_111_111, - pending_payable_rowid, - ); - }; - insert("0x0000000000000000000000000000000000666f6f", Some(15)); - insert(&make_wallet("foobar").to_string(), None); - insert("0x0000000000000000000000000000000000626172", Some(16)); - insert(&make_wallet("barfoo").to_string(), None); - - let result = subject.non_pending_payables(); - - assert_eq!( - result, - vec![ - PayableAccount { - wallet: make_wallet("foobar"), - balance_wei: 1234567890123456 as u128, - last_paid_timestamp: from_unix_timestamp(111_111_111), - pending_payable_opt: None - }, - PayableAccount { - wallet: make_wallet("barfoo"), - balance_wei: 1234567890123456 as u128, - last_paid_timestamp: from_unix_timestamp(111_111_111), - pending_payable_opt: None - }, - ] - ); + // TODO waits for the merge from GH-605 + // let home_dir = ensure_node_home_directory_exists( + // "payable_dao", + // "non_pending_payables_should_return_payables_with_no_pending_transaction", + // ); + // let subject = PayableDaoReal::new( + // DbInitializerReal::default() + // .initialize(&home_dir, DbInitializationConfig::test_default()) + // .unwrap(), + // ); + // let mut flags = OpenFlags::empty(); + // flags.insert(OpenFlags::SQLITE_OPEN_READ_WRITE); + // let conn = Connection::open_with_flags(&home_dir.join(DATABASE_FILE), flags).unwrap(); + // let conn = ConnectionWrapperReal::new(conn); + // let insert = |wallet: &str, pending_payable_rowid: Option| { + // insert_payable_record_fn( + // &conn, + // wallet, + // 1234567890123456, + // 111_111_111, + // pending_payable_rowid, + // ); + // }; + // insert("0x0000000000000000000000000000000000666f6f", Some(15)); + // insert(&make_wallet("foobar").to_string(), None); + // insert("0x0000000000000000000000000000000000626172", Some(16)); + // insert(&make_wallet("barfoo").to_string(), None); + // + // let result = subject.non_pending_payables(); + // + // assert_eq!( + // result, + // vec![ + // PayableAccount { + // wallet: make_wallet("foobar"), + // balance_wei: 1234567890123456 as u128, + // last_paid_timestamp: from_unix_timestamp(111_111_111), + // }, + // PayableAccount { + // wallet: make_wallet("barfoo"), + // balance_wei: 1234567890123456 as u128, + // last_paid_timestamp: from_unix_timestamp(111_111_111), + // }, + // ] + // ); } #[test] fn custom_query_handles_empty_table_in_top_records_mode() { - let main_test_setup = |_conn: &dyn ConnectionWrapper, _insert: InsertPayableHelperFn| {}; + let main_test_setup = + |payable_dao_real: PayableDaoReal, _: SentPayableDaoReal, _: FailedPayableDaoReal| { + payable_dao_real + }; let subject = custom_query_test_body_for_payable( "custom_query_handles_empty_table_in_top_records_mode", main_test_setup, @@ -1230,18 +1227,11 @@ mod tests { wallet: &str, balance: i128, timestamp: i64, - pending_payable_rowid: Option, ) { let (high_bytes, low_bytes) = BigIntDivider::deconstruct(balance); - let params: &[&dyn ToSql] = &[ - &wallet, - &high_bytes, - &low_bytes, - ×tamp, - &pending_payable_rowid, - ]; + let params: &[&dyn ToSql] = &[&wallet, &high_bytes, &low_bytes, ×tamp]; conn - .prepare("insert into payable (wallet_address, balance_high_b, balance_low_b, last_paid_timestamp, pending_payable_rowid) values (?, ?, ?, ?, ?)") + .prepare("insert into payable (wallet_address, balance_high_b, balance_low_b, last_paid_timestamp) values (?, ?, ?, ?)") .unwrap() .execute(params) .unwrap(); @@ -1249,44 +1239,55 @@ mod tests { fn accounts_for_tests_of_top_records( now: i64, - ) -> Box { - Box::new(move |conn, insert: InsertPayableHelperFn| { - insert( - conn, - "0x1111111111111111111111111111111111111111", - 1_000_000_002, - now - 86_401, - None, - ); - insert( - conn, - "0x2222222222222222222222222222222222222222", - 7_562_000_300_000, - now - 86_001, - None, - ); - insert( - conn, - "0x3333333333333333333333333333333333333333", - 999_999_999, //balance smaller than 1 gwei - now - 86_000, - None, - ); - insert( - conn, - "0x4444444444444444444444444444444444444444", - 10_000_000_100, - now - 86_300, - None, - ); - insert( - conn, - "0x5555555555555555555555555555555555555555", - 10_000_000_100, - now - 86_401, - Some(1), - ); - }) + ) -> Box PayableDaoReal> + { + Box::new( + move |payable_dao_real: PayableDaoReal, + sent_payable_dao_real: SentPayableDaoReal, + failed_payable_dao_real: FailedPayableDaoReal| { + let insert_payable = |unix_time: i64, wallet_addr: &str, amount_minor: u128| { + payable_dao_real + .more_money_payable( + from_unix_timestamp(unix_time), + &Wallet::new(wallet_addr), + amount_minor, + ) + .unwrap() + }; + insert_payable( + now - 86_401, + "0x1111111111111111111111111111111111111111", + 1_000_000_002, + ); + insert_payable( + now - 86_001, + "0x2222222222222222222222222222222222222222", + 7_562_000_300_000, + ); + insert_payable( + now - 86_000, + "0x3333333333333333333333333333333333333333", + 999_999_999, //balance smaller than 1 gwei + ); + insert_payable( + now - 86_300, + "0x4444444444444444444444444444444444444444", + 10_000_000_100, + ); + insert_payable( + now - 86_401, + "0x5555555555555555555555555555555555555555", + 10_000_000_100, + ); + let mut failed_tx = make_failed_tx(123); + failed_tx.receiver_address = + Wallet::new("0x5555555555555555555555555555555555555555").address(); + failed_payable_dao_real + .insert_new_records(&vec![failed_tx]) + .unwrap(); + payable_dao_real + }, + ) } #[test] @@ -1311,23 +1312,35 @@ mod tests { assert_eq!( result, vec![ - PayableAccount { - wallet: Wallet::new("0x2222222222222222222222222222222222222222"), - balance_wei: 7_562_000_300_000, - last_paid_timestamp: from_unix_timestamp(now - 86_001), - pending_payable_opt: None + PayableAccountWithTxInfo { + account: PayableAccount { + wallet: Wallet::new("0x2222222222222222222222222222222222222222"), + balance_wei: 7_562_000_300_000, + last_paid_timestamp: from_unix_timestamp(now - 86_001), + }, + tx_opt: None }, - PayableAccount { - wallet: Wallet::new("0x5555555555555555555555555555555555555555"), - balance_wei: 10_000_000_100, - last_paid_timestamp: from_unix_timestamp(now - 86_401), - pending_payable_opt: None + PayableAccountWithTxInfo { + account: PayableAccount { + wallet: Wallet::new("0x5555555555555555555555555555555555555555"), + balance_wei: 10_000_000_100, + last_paid_timestamp: from_unix_timestamp(now - 86_401), + }, + tx_opt: Some(CurrentTxInfo { + pending_opt: Some(make_tx_hash(123)), + failures: 0 + }) }, - PayableAccount { - wallet: Wallet::new("0x4444444444444444444444444444444444444444"), - balance_wei: 10_000_000_100, - last_paid_timestamp: from_unix_timestamp(now - 86_300), - pending_payable_opt: None + PayableAccountWithTxInfo { + account: PayableAccount { + wallet: Wallet::new("0x4444444444444444444444444444444444444444"), + balance_wei: 10_000_000_100, + last_paid_timestamp: from_unix_timestamp(now - 86_300), + }, + tx_opt: Some(CurrentTxInfo { + pending_opt: None, + failures: 2 + }) }, ] ); @@ -1355,31 +1368,40 @@ mod tests { assert_eq!( result, vec![ - PayableAccount { - wallet: Wallet::new("0x5555555555555555555555555555555555555555"), - balance_wei: 10_000_000_100, - last_paid_timestamp: from_unix_timestamp(now - 86_401), - pending_payable_opt: None - }, - PayableAccount { - wallet: Wallet::new("0x1111111111111111111111111111111111111111"), - balance_wei: 1_000_000_002, - last_paid_timestamp: from_unix_timestamp(now - 86_401), - pending_payable_opt: None + PayableAccountWithTxInfo { + account: PayableAccount { + wallet: Wallet::new("0x5555555555555555555555555555555555555555"), + balance_wei: 10_000_000_100, + last_paid_timestamp: from_unix_timestamp(now - 86_401), + }, + tx_opt: None }, - PayableAccount { - wallet: Wallet::new("0x4444444444444444444444444444444444444444"), - balance_wei: 10_000_000_100, - last_paid_timestamp: from_unix_timestamp(now - 86_300), - pending_payable_opt: None + PayableAccountWithTxInfo { + account: PayableAccount { + wallet: Wallet::new("0x1111111111111111111111111111111111111111"), + balance_wei: 1_000_000_002, + last_paid_timestamp: from_unix_timestamp(now - 86_401), + }, + tx_opt: None }, + PayableAccountWithTxInfo { + account: PayableAccount { + wallet: Wallet::new("0x4444444444444444444444444444444444444444"), + balance_wei: 10_000_000_100, + last_paid_timestamp: from_unix_timestamp(now - 86_300), + }, + tx_opt: None + } ] ); } #[test] fn custom_query_handles_empty_table_in_range_mode() { - let main_test_setup = |_conn: &dyn ConnectionWrapper, _insert: InsertPayableHelperFn| {}; + let main_test_setup = + |payable_dao: PayableDaoReal, _: SentPayableDaoReal, _: FailedPayableDaoReal| { + payable_dao + }; let subject = custom_query_test_body_for_payable( "custom_query_handles_empty_table_in_range_mode", main_test_setup, @@ -1401,56 +1423,66 @@ mod tests { // Two accounts differ only in the debt age but not the balance which allows to check double // ordering, primarily by balance and then age. let now = current_unix_timestamp(); - let main_setup = |conn: &dyn ConnectionWrapper, insert: InsertPayableHelperFn| { - insert( - conn, - "0x1111111111111111111111111111111111111111", - gwei_to_wei::<_, u64>(499_999_999), //too small + let main_setup = |payable_dao: PayableDaoReal, + sent_payable_dao: SentPayableDaoReal, + _: FailedPayableDaoReal| { + let insert_payable_record = |time: i64, wallet_addr: &str, amount: u64| { + payable_dao + .more_money_payable( + from_unix_timestamp(time), + &Wallet::new(wallet_addr), + gwei_to_wei::<_, u64>(amount), //too small + ) + .unwrap(); + }; + insert_payable_record( now - 70_000, - None, + "0x1111111111111111111111111111111111111111", + 499_999_999, //too small ); - insert( - conn, - "0x2222222222222222222222222222222222222222", - gwei_to_wei::<_, u64>(1_800_456_000), + insert_payable_record( now - 55_120, - Some(1), + "0x2222222222222222222222222222222222222222", + 1_800_456_000, ); - insert( - conn, - "0x3333333333333333333333333333333333333333", - gwei_to_wei::<_, u64>(600_123_456), + insert_payable_record( now - 200_001, //too old - None, + "0x3333333333333333333333333333333333333333", + 600_123_456, ); - insert( - conn, - "0x4444444444444444444444444444444444444444", - gwei_to_wei::<_, u64>(1_033_456_000_u64), + insert_payable_record( now - 19_999, //too young - None, + "0x4444444444444444444444444444444444444444", + 1_033_456_000, ); - insert( - conn, - "0x5555555555555555555555555555555555555555", - gwei_to_wei::<_, u64>(35_000_000_001), //too big + insert_payable_record( now - 30_786, - None, + "0x5555555555555555555555555555555555555555", + 35_000_000_001, //too big ); - insert( - conn, - "0x6666666666666666666666666666666666666666", - gwei_to_wei::<_, u64>(1_800_456_000u64), + insert_payable_record( now - 100_401, - None, + "0x6666666666666666666666666666666666666666", + 1_800_456_000, ); - insert( - conn, - "0x7777777777777777777777777777777777777777", - gwei_to_wei::<_, u64>(2_500_647_000u64), + insert_payable_record( now - 80_333, - None, + "0x7777777777777777777777777777777777777777", + 2_500_647_000, ); + sent_payable_dao + .insert_new_records(&vec![SentTx { + hash: make_tx_hash(0xABC), + receiver_address: Wallet::new("0x6666666666666666666666666666666666666666") + .address(), + amount_minor: 0, + timestamp: 0, + gas_price_minor: 0, + nonce: 0, + status: TxStatus::Pending(ValidationStatus::Waiting), + }]) + .unwrap(); + payable_dao }; let subject = custom_query_test_body_for_payable("custom_query_in_range_mode", main_setup); @@ -1467,24 +1499,33 @@ mod tests { assert_eq!( result, vec![ - PayableAccount { - wallet: Wallet::new("0x7777777777777777777777777777777777777777"), - balance_wei: gwei_to_wei(2_500_647_000_u32), - last_paid_timestamp: from_unix_timestamp(now - 80_333), - pending_payable_opt: None + PayableAccountWithTxInfo { + account: PayableAccount { + wallet: Wallet::new("0x7777777777777777777777777777777777777777"), + balance_wei: gwei_to_wei(2_500_647_000_u32), + last_paid_timestamp: from_unix_timestamp(now - 80_333), + }, + tx_opt: None }, - PayableAccount { - wallet: Wallet::new("0x6666666666666666666666666666666666666666"), - balance_wei: gwei_to_wei(1_800_456_000_u32), - last_paid_timestamp: from_unix_timestamp(now - 100_401), - pending_payable_opt: None + PayableAccountWithTxInfo { + account: PayableAccount { + wallet: Wallet::new("0x6666666666666666666666666666666666666666"), + balance_wei: gwei_to_wei(1_800_456_000_u32), + last_paid_timestamp: from_unix_timestamp(now - 100_401), + }, + tx_opt: Some(CurrentTxInfo { + pending_opt: Some(make_tx_hash(0xABC)), + failures: 0 + }) + }, + PayableAccountWithTxInfo { + account: PayableAccount { + wallet: Wallet::new("0x2222222222222222222222222222222222222222"), + balance_wei: gwei_to_wei(1_800_456_000_u32), + last_paid_timestamp: from_unix_timestamp(now - 55_120), + }, + tx_opt: None }, - PayableAccount { - wallet: Wallet::new("0x2222222222222222222222222222222222222222"), - balance_wei: gwei_to_wei(1_800_456_000_u32), - last_paid_timestamp: from_unix_timestamp(now - 55_120), - pending_payable_opt: None - } ] ); } @@ -1492,24 +1533,26 @@ mod tests { #[test] fn range_query_does_not_display_values_from_below_1_gwei() { let now = current_unix_timestamp(); - let timestamp_1 = now - 11_001; - let timestamp_2 = now - 5000; - let main_setup = |conn: &dyn ConnectionWrapper, insert: InsertPayableHelperFn| { - insert( - conn, - "0x1111111111111111111111111111111111111111", - 400_005_601, - timestamp_1, - None, - ); - insert( - conn, - "0x2222222222222222222222222222222222222222", - 30_000_300_000, - timestamp_2, - None, - ); - }; + let timestamp_1 = from_unix_timestamp(now - 11_001); + let timestamp_2 = from_unix_timestamp(now - 5000); + let main_setup = + |payable_dao: PayableDaoReal, _: SentPayableDaoReal, _: FailedPayableDaoReal| { + payable_dao + .more_money_payable( + timestamp_1, + &Wallet::new("0x1111111111111111111111111111111111111111"), + 400_005_601, + ) + .unwrap(); + payable_dao + .more_money_payable( + timestamp_2, + &Wallet::new("0x2222222222222222222222222222222222222222"), + 30_000_300_000, + ) + .unwrap(); + payable_dao + }; let subject = custom_query_test_body_for_payable( "range_query_does_not_display_values_from_below_1_gwei", main_setup, @@ -1527,12 +1570,14 @@ mod tests { assert_eq!( result, - vec![PayableAccount { - wallet: Wallet::new("0x2222222222222222222222222222222222222222"), - balance_wei: 30_000_300_000, - last_paid_timestamp: from_unix_timestamp(timestamp_2), - pending_payable_opt: None - },] + vec![PayableAccountWithTxInfo { + account: PayableAccount { + wallet: Wallet::new("0x2222222222222222222222222222222222222222"), + balance_wei: 30_000_300_000, + last_paid_timestamp: timestamp_2, + }, + tx_opt: None + }] ) } @@ -1548,28 +1593,24 @@ mod tests { "0x1111111111111111111111111111111111111111", 999_999_999, timestamp - 1000, - None, ); insert_payable_record_fn( &*conn, "0x2222222222222222222222222222222222222222", 1_000_123_123, timestamp - 2000, - None, ); insert_payable_record_fn( &*conn, "0x3333333333333333333333333333333333333333", 1_000_000_000, timestamp - 3000, - None, ); insert_payable_record_fn( &*conn, "0x4444444444444444444444444444444444444444", 1_000_000_001, timestamp - 4000, - Some(3), ); let subject = PayableDaoReal::new(conn); @@ -1593,14 +1634,12 @@ mod tests { "0x1111111111111111111111111111111111111111", 123_456, 111_111_111, - None, ); insert_payable_record_fn( &*conn, "0x2222222222222222222222222222222222222222", -999_999, 222_222_222, - None, ); let subject = PayableDaoReal::new(conn); @@ -1642,13 +1681,17 @@ mod tests { fn custom_query_test_body_for_payable(test_name: &str, main_setup_fn: F) -> PayableDaoReal where - F: Fn(&dyn ConnectionWrapper, InsertPayableHelperFn), + F: Fn(PayableDaoReal, SentPayableDaoReal, FailedPayableDaoReal) -> PayableDaoReal, { let home_dir = ensure_node_home_directory_exists("payable_dao", test_name); - let conn = DbInitializerReal::default() - .initialize(&home_dir, DbInitializationConfig::test_default()) - .unwrap(); - main_setup_fn(conn.as_ref(), &insert_payable_record_fn); - PayableDaoReal::new(conn) + let conn = || { + DbInitializerReal::default() + .initialize(&home_dir, DbInitializationConfig::test_default()) + .unwrap() + }; + let failed_payable_dao = FailedPayableDaoReal::new(conn()); + let sent_payable_dao = SentPayableDaoReal::new(conn()); + let payable_dao = PayableDaoReal::new(conn()); + main_setup_fn(payable_dao, sent_payable_dao, failed_payable_dao) } } diff --git a/node/src/accountant/db_access_objects/utils.rs b/node/src/accountant/db_access_objects/utils.rs index 21c9cdc83..377d7a349 100644 --- a/node/src/accountant/db_access_objects/utils.rs +++ b/node/src/accountant/db_access_objects/utils.rs @@ -12,7 +12,8 @@ use crate::sub_lib::accountant::PaymentThresholds; use ethereum_types::H256; use masq_lib::constants::WEIS_IN_GWEI; use masq_lib::messages::{ - RangeQuery, TopRecordsConfig, TopRecordsOrdering, UiPayableAccount, UiReceivableAccount, + CurrentTxInfo, RangeQuery, TopRecordsConfig, TopRecordsOrdering, UiPayableAccount, + UiReceivableAccount, }; use rusqlite::{Row, Statement, ToSql}; use std::collections::HashMap; @@ -264,27 +265,34 @@ impl From<&RangeQuery> for CustomQuery { } } -pub fn remap_payable_accounts(accounts: Vec) -> Vec { +#[derive(Debug, PartialEq)] +pub struct PayableAccountWithTxInfo { + pub account: PayableAccount, + pub tx_opt: Option, +} + +pub fn remap_payable_accounts(accounts: Vec) -> Vec { accounts .into_iter() - .map(|account| UiPayableAccount { - wallet: account.wallet.to_string(), - age_s: to_age(account.last_paid_timestamp), - balance_gwei: { - let gwei = (account.balance_wei / (WEIS_IN_GWEI as u128)) as u64; - if gwei > 0 { - gwei - } else { - panic!( - "Broken code: PayableAccount with less than 1 gwei passed through db query \ - constraints; wallet: {}, balance: {}", - account.wallet, account.balance_wei - ) - } - }, - pending_payable_hash_opt: account - .pending_payable_opt - .map(|full_id| full_id.hash.to_string()), + .map(|account_with_tx_info| { + let account = account_with_tx_info.account; + UiPayableAccount { + wallet: account.wallet.to_string(), + age_s: to_age(account.last_paid_timestamp), + balance_gwei: { + let gwei = (account.balance_wei / (WEIS_IN_GWEI as u128)) as u64; + if gwei > 0 { + gwei + } else { + panic!( + "Broken code: PayableAccount with less than 1 gwei passed through \ + db query constraints; wallet: {}, balance: {}", + account.wallet, account.balance_wei + ) + } + }, + current_tx_info_opt: account_with_tx_info.tx_opt, + } }) .collect() } @@ -295,14 +303,23 @@ pub fn remap_receivable_accounts(accounts: Vec) -> Vec(payment_thresholds.maturity_threshold_sec) + 1, ), - pending_payable_opt: None, }, // above minimum balance, to the right of minimum time (not in buffer zone, below the curve) PayableAccount { @@ -3891,7 +3888,6 @@ mod tests { now - checked_conversion::(payment_thresholds.threshold_interval_sec) + 1, ), - pending_payable_opt: None, }, ]; let payable_dao = PayableDaoMock::new() @@ -3961,7 +3957,6 @@ mod tests { + 10, ), ), - pending_payable_opt: None, }, // Slightly above the curve (balance intersection), to the right of minimum time PayableAccount { @@ -3972,7 +3967,6 @@ mod tests { DEFAULT_PAYMENT_THRESHOLDS.maturity_threshold_sec + 10, ), ), - pending_payable_opt: None, }, ]; let payable_dao = @@ -6041,11 +6035,13 @@ mod tests { //that part is in the responsibility of the database manager, answering the specific SQL query let payable_custom_query_params_arc = Arc::new(Mutex::new(vec![])); let receivable_custom_query_params_arc = Arc::new(Mutex::new(vec![])); - let payable_accounts_retrieved = vec![PayableAccount { - wallet: make_wallet("abcd123"), - balance_wei: 58_568_686_005, - last_paid_timestamp: SystemTime::now().sub(Duration::from_secs(5000)), - pending_payable_opt: None, + let payable_accounts_retrieved = vec![PayableAccountWithTxInfo { + account: PayableAccount { + wallet: make_wallet("abcd123"), + balance_wei: 58_568_686_005, + last_paid_timestamp: SystemTime::now().sub(Duration::from_secs(5000)), + }, + tx_opt: None, }]; let payable_dao = PayableDaoMock::new() .custom_query_params(&payable_custom_query_params_arc) @@ -6091,7 +6087,7 @@ mod tests { wallet: make_wallet("abcd123").to_string(), age_s: extracted_payable_ages[0], balance_gwei: 58, - pending_payable_hash_opt: None + current_tx_info_opt: None, },]), receivable_opt: Some(vec![UiReceivableAccount { wallet: make_wallet("efe4848").to_string(), @@ -6190,11 +6186,16 @@ mod tests { fn compute_financials_processes_request_with_range_queries_only() { let payable_custom_query_params_arc = Arc::new(Mutex::new(vec![])); let receivable_custom_query_params_arc = Arc::new(Mutex::new(vec![])); - let payable_accounts_retrieved = vec![PayableAccount { - wallet: make_wallet("abcd123"), - balance_wei: 5_686_860_056, - last_paid_timestamp: SystemTime::now().sub(Duration::from_secs(7580)), - pending_payable_opt: None, + let payable_accounts_retrieved = vec![PayableAccountWithTxInfo { + account: PayableAccount { + wallet: make_wallet("abcd123"), + balance_wei: 5_686_860_056, + last_paid_timestamp: SystemTime::now().sub(Duration::from_secs(7580)), + }, + tx_opt: Some(CurrentTxInfo { + pending_opt: None, + failures: 2, + }), }]; let payable_dao = PayableDaoMock::new() .custom_query_params(&payable_custom_query_params_arc) @@ -6257,7 +6258,10 @@ mod tests { wallet: make_wallet("abcd123").to_string(), age_s: extracted_payable_ages[0], balance_gwei: 5, - pending_payable_hash_opt: None + current_tx_info_opt: Some(CurrentTxInfo { + pending_opt: None, + failures: 2 + }), },]), receivable_opt: Some(vec![ UiReceivableAccount { @@ -6449,11 +6453,16 @@ mod tests { )] fn compute_financials_blows_up_on_screwed_sql_query_for_payables_returning_balance_smaller_than_one_gwei( ) { - let payable_accounts_retrieved = vec![PayableAccount { - wallet: make_wallet("abcd123"), - balance_wei: 8_686_005, - last_paid_timestamp: SystemTime::now().sub(Duration::from_secs(5000)), - pending_payable_opt: None, + let payable_accounts_retrieved = vec![PayableAccountWithTxInfo { + account: PayableAccount { + wallet: make_wallet("abcd123"), + balance_wei: 8_686_005, + last_paid_timestamp: SystemTime::now().sub(Duration::from_secs(5000)), + }, + tx_opt: Some(CurrentTxInfo { + pending_opt: None, + failures: 3, + }), }]; let payable_dao = PayableDaoMock::new().custom_query_result(Some(payable_accounts_retrieved)); diff --git a/node/src/accountant/scanners/mod.rs b/node/src/accountant/scanners/mod.rs index 1d69ab3c9..81c882d52 100644 --- a/node/src/accountant/scanners/mod.rs +++ b/node/src/accountant/scanners/mod.rs @@ -2105,7 +2105,6 @@ mod tests { wallet: make_wallet("hi"), balance_wei: balance, last_paid_timestamp, - pending_payable_opt: None, }; let custom_payment_thresholds = PaymentThresholds { maturity_threshold_sec: 1111, @@ -2170,7 +2169,6 @@ mod tests { wallet: make_wallet("wallet0"), balance_wei: debt, last_paid_timestamp: from_unix_timestamp(time), - pending_payable_opt: None, }]; let subject = PayableScannerBuilder::new() .payment_thresholds(payment_thresholds) @@ -2199,7 +2197,6 @@ mod tests { wallet: make_wallet("wallet0"), balance_wei: debt, last_paid_timestamp: from_unix_timestamp(time), - pending_payable_opt: None, }; let subject = PayableScannerBuilder::new() .payment_thresholds(payment_thresholds) @@ -2233,7 +2230,6 @@ mod tests { last_paid_timestamp: from_unix_timestamp( to_unix_timestamp(now) - payment_thresholds.maturity_threshold_sec as i64 + 1, ), - pending_payable_opt: None, }]; let subject = PayableScannerBuilder::new() .payment_thresholds(payment_thresholds) diff --git a/node/src/accountant/scanners/scanners_utils.rs b/node/src/accountant/scanners/scanners_utils.rs index c459f7226..ca8f4f5c0 100644 --- a/node/src/accountant/scanners/scanners_utils.rs +++ b/node/src/accountant/scanners/scanners_utils.rs @@ -334,27 +334,23 @@ mod tests { wallet: make_wallet("wallet0"), balance_wei: same_amount_significance, last_paid_timestamp: from_unix_timestamp(now_t - 5000), - pending_payable_opt: None, }, //this debt is more significant because beside being high in amount it's also older, so should be prioritized and picked PayableAccount { wallet: make_wallet("wallet1"), balance_wei: same_amount_significance, last_paid_timestamp: from_unix_timestamp(now_t - 10000), - pending_payable_opt: None, }, //similarly these two wallets have debts equally old but the second has a bigger balance and should be chosen PayableAccount { wallet: make_wallet("wallet3"), balance_wei: 100, last_paid_timestamp: same_age_significance, - pending_payable_opt: None, }, PayableAccount { wallet: make_wallet("wallet2"), balance_wei: 330, last_paid_timestamp: same_age_significance, - pending_payable_opt: None, }, ]; @@ -480,7 +476,6 @@ mod tests { + payment_thresholds.threshold_interval_sec, ), ), - pending_payable_opt: None, }, 10_000_000_001_152_000_u128, ), @@ -493,7 +488,6 @@ mod tests { payment_thresholds.maturity_threshold_sec + 55, ), ), - pending_payable_opt: None, }, 999_978_993_055_555_580, ), diff --git a/node/src/accountant/test_utils.rs b/node/src/accountant/test_utils.rs index 2f777e57b..8db54d693 100644 --- a/node/src/accountant/test_utils.rs +++ b/node/src/accountant/test_utils.rs @@ -20,7 +20,8 @@ use crate::accountant::db_access_objects::sent_payable_dao::{ SentPayableDao, SentPayableDaoFactory, TxStatus, }; use crate::accountant::db_access_objects::utils::{ - from_unix_timestamp, to_unix_timestamp, CustomQuery, TxHash, TxIdentifiers, + from_unix_timestamp, to_unix_timestamp, CustomQuery, PayableAccountWithTxInfo, TxHash, + TxIdentifiers, }; use crate::accountant::payment_adjuster::{Adjustment, AnalysisError, PaymentAdjuster}; use crate::accountant::scanners::payable_scanner_extension::msgs::{ @@ -96,7 +97,6 @@ pub fn make_payable_account_with_wallet_and_balance_and_timestamp_opt( wallet, balance_wei: balance, last_paid_timestamp: timestamp_opt.unwrap_or(SystemTime::now()), - pending_payable_opt: None, } } @@ -633,7 +633,7 @@ pub struct PayableDaoMock { transactions_confirmed_params: Arc>>>, transactions_confirmed_results: RefCell>>, custom_query_params: Arc>>>, - custom_query_result: RefCell>>>, + custom_query_result: RefCell>>>, total_results: RefCell>, } @@ -684,7 +684,10 @@ impl PayableDao for PayableDaoMock { self.non_pending_payables_results.borrow_mut().remove(0) } - fn custom_query(&self, custom_query: CustomQuery) -> Option> { + fn custom_query( + &self, + custom_query: CustomQuery, + ) -> Option> { self.custom_query_params.lock().unwrap().push(custom_query); self.custom_query_result.borrow_mut().remove(0) } @@ -759,7 +762,7 @@ impl PayableDaoMock { self } - pub fn custom_query_result(self, result: Option>) -> Self { + pub fn custom_query_result(self, result: Option>) -> Self { self.custom_query_result.borrow_mut().push(result); self } @@ -1525,7 +1528,6 @@ pub fn make_qualified_and_unqualified_payables( last_paid_timestamp: from_unix_timestamp( to_unix_timestamp(now) - payment_thresholds.maturity_threshold_sec as i64 + 1, ), - pending_payable_opt: None, }]; let qualified_payable_accounts = vec![ PayableAccount { @@ -1536,7 +1538,6 @@ pub fn make_qualified_and_unqualified_payables( last_paid_timestamp: from_unix_timestamp( to_unix_timestamp(now) - payment_thresholds.maturity_threshold_sec as i64 - 1, ), - pending_payable_opt: None, }, PayableAccount { wallet: make_wallet("wallet3"), @@ -1546,7 +1547,6 @@ pub fn make_qualified_and_unqualified_payables( last_paid_timestamp: from_unix_timestamp( to_unix_timestamp(now) - payment_thresholds.maturity_threshold_sec as i64 - 100, ), - pending_payable_opt: None, }, ]; diff --git a/node/src/blockchain/blockchain_bridge.rs b/node/src/blockchain/blockchain_bridge.rs index 3458a4140..a2ca60879 100644 --- a/node/src/blockchain/blockchain_bridge.rs +++ b/node/src/blockchain/blockchain_bridge.rs @@ -716,7 +716,6 @@ mod tests { last_paid_timestamp: SystemTime::now() .checked_sub(Duration::from_secs(1000)) .unwrap(), - pending_payable_opt: None, }, PayableAccount { wallet: wallet_2.clone(), @@ -724,7 +723,6 @@ mod tests { last_paid_timestamp: SystemTime::now() .checked_sub(Duration::from_secs(500)) .unwrap(), - pending_payable_opt: None, }, ]; let mut subject = BlockchainBridge::new( @@ -878,7 +876,6 @@ mod tests { wallet: wallet_account, balance_wei: 111_420_204, last_paid_timestamp: from_unix_timestamp(150_000_000), - pending_payable_opt: None, }; let agent_id_stamp = ArbitraryIdStamp::new(); let agent = BlockchainAgentMock::default() @@ -979,7 +976,6 @@ mod tests { wallet: account_wallet.clone(), balance_wei: 111_420_204, last_paid_timestamp: from_unix_timestamp(150_000_000), - pending_payable_opt: None, }; let consuming_wallet = make_paying_wallet(b"consuming_wallet"); let agent = BlockchainAgentMock::default() diff --git a/node/src/blockchain/blockchain_interface/blockchain_interface_web3/utils.rs b/node/src/blockchain/blockchain_interface/blockchain_interface_web3/utils.rs index d8e1729f9..f2a45b022 100644 --- a/node/src/blockchain/blockchain_interface/blockchain_interface_web3/utils.rs +++ b/node/src/blockchain/blockchain_interface/blockchain_interface_web3/utils.rs @@ -638,13 +638,11 @@ mod tests { wallet: make_wallet("4567"), balance_wei: 2_345_678, last_paid_timestamp: from_unix_timestamp(4500000), - pending_payable_opt: None, }, PayableAccount { wallet: make_wallet("5656"), balance_wei: 6_543_210, last_paid_timestamp: from_unix_timestamp(333000), - pending_payable_opt: None, }, ]; let tx_hashes = vec![make_tx_hash(444), make_tx_hash(333)]; From 234d41c8ab68a52d7617fe1a7223106d2180631b Mon Sep 17 00:00:00 2001 From: Bert Date: Sun, 21 Sep 2025 13:32:42 +0200 Subject: [PATCH 02/11] GH-662: mark pending payable functionality removed --- .../db_access_objects/payable_dao.rs | 376 ------------------ node/src/accountant/scanners/mod.rs | 54 --- node/src/accountant/test_utils.rs | 19 - 3 files changed, 449 deletions(-) diff --git a/node/src/accountant/db_access_objects/payable_dao.rs b/node/src/accountant/db_access_objects/payable_dao.rs index a139193b9..18b5c5ee3 100644 --- a/node/src/accountant/db_access_objects/payable_dao.rs +++ b/node/src/accountant/db_access_objects/payable_dao.rs @@ -48,11 +48,6 @@ pub trait PayableDao: Debug + Send { amount_minor: u128, ) -> Result<(), PayableDaoError>; - fn mark_pending_payables_rowids( - &self, - mark_instructions: &[MarkPendingPayableID], - ) -> Result<(), PayableDaoError>; - fn transactions_confirmed(&self, confirmed_payables: &[SentTx]) -> Result<(), PayableDaoError>; fn non_pending_payables(&self) -> Vec; @@ -121,30 +116,6 @@ impl PayableDao for PayableDaoReal { Ok(()) } - fn mark_pending_payables_rowids( - &self, - _mark_instructions: &[MarkPendingPayableID], - ) -> Result<(), PayableDaoError> { - todo!("Will be an object of removal in GH-662") - // if wallets_and_rowids.is_empty() { - // panic!("broken code: empty input is not permit to enter this method") - // } - // - // let case_expr = compose_case_expression(wallets_and_rowids); - // let wallets = serialize_wallets(wallets_and_rowids, Some('\'')); - // //the Wallet type is secure against SQL injections - // let sql = format!( - // "update payable set \ - // pending_payable_rowid = {} \ - // where - // pending_payable_rowid is null and wallet_address in ({}) - // returning - // pending_payable_rowid", - // case_expr, wallets, - // ); - // execute_command(&*self.conn, wallets_and_rowids, &sql) - } - fn transactions_confirmed(&self, confirmed_payables: &[SentTx]) -> Result<(), PayableDaoError> { confirmed_payables.iter().try_for_each(|confirmed_payable| { let main_sql = "update payable set \ @@ -358,156 +329,6 @@ impl TableNameDAO for PayableDaoReal { } } -// TODO Will be an object of removal in GH-662 -// mod mark_pending_payable_associated_functions { -// use crate::accountant::comma_joined_stringifiable; -// use crate::accountant::db_access_objects::payable_dao::{MarkPendingPayableID, PayableDaoError}; -// use crate::accountant::db_access_objects::utils::{ -// update_rows_and_return_valid_count, VigilantRusqliteFlatten, -// }; -// use crate::database::rusqlite_wrappers::ConnectionWrapper; -// use crate::sub_lib::wallet::Wallet; -// use itertools::Itertools; -// use rusqlite::Row; -// use std::fmt::Display; -// -// pub fn execute_command( -// conn: &dyn ConnectionWrapper, -// wallets_and_rowids: &[(&Wallet, u64)], -// sql: &str, -// ) -> Result<(), PayableDaoError> { -// let mut stm = conn.prepare(sql).expect("Internal Error"); -// let validator = validate_row_updated; -// let rows_affected_res = update_rows_and_return_valid_count(&mut stm, validator); -// -// match rows_affected_res { -// Ok(rows_affected) => match rows_affected { -// num if num == wallets_and_rowids.len() => Ok(()), -// num => mismatched_row_count_panic(conn, wallets_and_rowids, num), -// }, -// Err(errs) => { -// let err_msg = format!( -// "Multi-row update to mark pending payable hit these errors: {:?}", -// errs -// ); -// Err(PayableDaoError::RusqliteError(err_msg)) -// } -// } -// } -// -// pub fn compose_case_expression(wallets_and_rowids: &[(&Wallet, u64)]) -> String { -// //the Wallet type is secure against SQL injections -// fn when_clause((wallet, rowid): &(&Wallet, u64)) -> String { -// format!("when wallet_address = '{wallet}' then {rowid}") -// } -// -// format!( -// "case {} end", -// wallets_and_rowids.iter().map(when_clause).join("\n") -// ) -// } -// -// pub fn serialize_wallets( -// wallets_and_rowids: &[MarkPendingPayableID], -// quotes_opt: Option, -// ) -> String { -// wallets_and_rowids -// .iter() -// .map(|(wallet, _)| match quotes_opt { -// Some(char) => format!("{}{}{}", char, wallet, char), -// None => wallet.to_string(), -// }) -// .join(", ") -// } -// -// fn validate_row_updated(row: &Row) -> Result { -// row.get::>(0).map(|opt| opt.is_some()) -// } -// -// fn mismatched_row_count_panic( -// conn: &dyn ConnectionWrapper, -// wallets_and_rowids: &[(&Wallet, u64)], -// actual_count: usize, -// ) -> ! { -// let serialized_wallets = serialize_wallets(wallets_and_rowids, None); -// let expected_count = wallets_and_rowids.len(); -// let extension = explanatory_extension(conn, wallets_and_rowids); -// panic!( -// "Marking pending payable rowid for wallets {serialized_wallets} affected \ -// {actual_count} rows but expected {expected_count}. {extension}" -// ) -// } -// -// pub(super) fn explanatory_extension( -// conn: &dyn ConnectionWrapper, -// wallets_and_rowids: &[(&Wallet, u64)], -// ) -> String { -// let resulting_pairs_collection = -// query_resulting_pairs_of_wallets_and_rowids(conn, wallets_and_rowids); -// let resulting_pairs_summary = if resulting_pairs_collection.is_empty() { -// "".to_string() -// } else { -// pairs_in_pretty_string(&resulting_pairs_collection, |rowid_opt: &Option| { -// match rowid_opt { -// Some(rowid) => Box::new(*rowid), -// None => Box::new("N/A"), -// } -// }) -// }; -// let wallets_and_non_optional_rowids = -// pairs_in_pretty_string(wallets_and_rowids, |rowid: &u64| Box::new(*rowid)); -// format!( -// "\ -// The demanded data according to {} looks different from the resulting state {}!. Operation failed.\n\ -// Notes:\n\ -// a) if row ids have stayed non-populated it points out that writing failed but without the double payment threat,\n\ -// b) if some accounts on the resulting side are missing, other kind of serious issues should be suspected but see other\n\ -// points to figure out if you were put in danger of double payment,\n\ -// c) seeing ids different from those demanded might be a sign of some payments having been doubled.\n\ -// The operation which is supposed to clear out the ids of the payments previously requested for this account\n\ -// probably had not managed to complete successfully before another payment was requested: preventive measures failed.\n", -// wallets_and_non_optional_rowids, resulting_pairs_summary) -// } -// -// fn query_resulting_pairs_of_wallets_and_rowids( -// conn: &dyn ConnectionWrapper, -// wallets_and_rowids: &[(&Wallet, u64)], -// ) -> Vec<(Wallet, Option)> { -// let select_dealt_accounts = -// format!( -// "select wallet_address, pending_payable_rowid from payable where wallet_address in ({})", -// serialize_wallets(wallets_and_rowids, Some('\'')) -// ); -// let row_processor = |row: &Row| { -// Ok(( -// row.get::(0) -// .expect("database corrupt: wallet addresses found in bad format"), -// row.get::>(1) -// .expect("database_corrupt: rowid found in bad format"), -// )) -// }; -// conn.prepare(&select_dealt_accounts) -// .expect("select failed") -// .query_map([], row_processor) -// .expect("no args yet binding failed") -// .vigilant_flatten() -// .collect() -// } -// -// fn pairs_in_pretty_string( -// pairs: &[(W, R)], -// rowid_pretty_writer: fn(&R) -> Box, -// ) -> String { -// comma_joined_stringifiable(pairs, |(wallet, rowid)| { -// format!( -// "( Wallet: {}, Rowid: {} )", -// wallet, -// rowid_pretty_writer(rowid) -// ) -// }) -// } -// } - #[cfg(test)] mod tests { use super::*; @@ -683,203 +504,6 @@ mod tests { ) } - #[test] - fn mark_pending_payables_marks_pending_transactions_for_new_addresses() { - //the extra unchanged record checks the safety of right count of changed rows; - //experienced serious troubles in the past - // TODO Will be an object of removal in GH-662 - // let home_dir = ensure_node_home_directory_exists( - // "payable_dao", - // "mark_pending_payables_marks_pending_transactions_for_new_addresses", - // ); - // let wallet_0 = make_wallet("wallet"); - // let wallet_1 = make_wallet("booga"); - // let pending_payable_rowid_1 = 656; - // let wallet_2 = make_wallet("bagaboo"); - // let pending_payable_rowid_2 = 657; - // let boxed_conn = DbInitializerReal::default() - // .initialize(&home_dir, DbInitializationConfig::test_default()) - // .unwrap(); - // { - // let insert = "insert into payable (wallet_address, balance_high_b, balance_low_b, \ - // last_paid_timestamp) values (?, ?, ?, ?), (?, ?, ?, ?), (?, ?, ?, ?)"; - // let mut stm = boxed_conn.prepare(insert).unwrap(); - // let params = [ - // [&wallet_0 as &dyn ToSql, &12345, &1, &45678], - // [&wallet_1, &0, &i64::MAX, &150_000_000], - // [&wallet_2, &3, &0, &151_000_000], - // ] - // .into_iter() - // .flatten() - // .collect::>(); - // stm.execute(params.as_slice()).unwrap(); - // } - // let subject = PayableDaoReal::new(boxed_conn); - // - // subject - // .mark_pending_payables_rowids(&[ - // (&wallet_1, pending_payable_rowid_1), - // (&wallet_2, pending_payable_rowid_2), - // ]) - // .unwrap(); - // - // let account_statuses = [&wallet_0, &wallet_1, &wallet_2] - // .iter() - // .map(|wallet| subject.account_status(wallet).unwrap()) - // .collect::>(); - // assert_eq!( - // account_statuses, - // vec![ - // PayableAccount { - // wallet: wallet_0, - // balance_wei: u128::try_from(BigIntDivider::reconstitute(12345, 1)).unwrap(), - // last_paid_timestamp: from_unix_timestamp(45678), - // pending_payable_opt: None, - // }, - // PayableAccount { - // wallet: wallet_1, - // balance_wei: u128::try_from(BigIntDivider::reconstitute(0, i64::MAX)).unwrap(), - // last_paid_timestamp: from_unix_timestamp(150_000_000), - // pending_payable_opt: Some(PendingPayableId::new( - // pending_payable_rowid_1, - // make_tx_hash(0) - // )), - // }, - // //notice the hashes are garbage generated by a test method not knowing doing better - // PayableAccount { - // wallet: wallet_2, - // balance_wei: u128::try_from(BigIntDivider::reconstitute(3, 0)).unwrap(), - // last_paid_timestamp: from_unix_timestamp(151_000_000), - // pending_payable_opt: Some(PendingPayableId::new( - // pending_payable_rowid_2, - // make_tx_hash(0) - // )) - // } - // ] - // ) - } - - #[test] - // #[should_panic(expected = "\ - // Marking pending payable rowid for wallets 0x000000000000000000000000000000626f6f6761, \ - // 0x0000000000000000000000000000007961686f6f affected 0 rows but expected 2. \ - // The demanded data according to ( Wallet: 0x000000000000000000000000000000626f6f6761, Rowid: 456 ), \ - // ( Wallet: 0x0000000000000000000000000000007961686f6f, Rowid: 789 ) looks different from \ - // the resulting state ( Wallet: 0x000000000000000000000000000000626f6f6761, Rowid: 456 )!. Operation failed.\n\ - // Notes:\n\ - // a) if row ids have stayed non-populated it points out that writing failed but without the double payment threat,\n\ - // b) if some accounts on the resulting side are missing, other kind of serious issues should be suspected but see other\n\ - // points to figure out if you were put in danger of double payment,\n\ - // c) seeing ids different from those demanded might be a sign of some payments having been doubled.\n\ - // The operation which is supposed to clear out the ids of the payments previously requested for this account\n\ - // probably had not managed to complete successfully before another payment was requested: preventive measures failed.")] - fn mark_pending_payables_rowids_returned_different_row_count_than_expected_with_one_account_missing_and_one_unmodified( - ) { - // TODO Will be an object of removal in GH-662 - // let home_dir = ensure_node_home_directory_exists( - // "payable_dao", - // "mark_pending_payables_rowids_returned_different_row_count_than_expected_with_one_account_missing_and_one_unmodified", - // ); - // let conn = DbInitializerReal::default() - // .initialize(&home_dir, DbInitializationConfig::test_default()) - // .unwrap(); - // let first_wallet = make_wallet("booga"); - // let first_rowid = 456; - // insert_payable_record_fn( - // &*conn, - // &first_wallet.to_string(), - // 123456, - // 789789, - // Some(first_rowid), - // ); - // let subject = PayableDaoReal::new(conn); - // - // let _ = subject.mark_pending_payables_rowids(&[ - // (&first_wallet, first_rowid as u64), - // (&make_wallet("yahoo"), 789), - // ]); - } - - #[test] - fn explanatory_extension_shows_resulting_account_with_unpopulated_rowid() { - // TODO Will be an object of removal in GH-662 - // let home_dir = ensure_node_home_directory_exists( - // "payable_dao", - // "explanatory_extension_shows_resulting_account_with_unpopulated_rowid", - // ); - // let wallet_1 = make_wallet("hooga"); - // let rowid_1 = 550; - // let wallet_2 = make_wallet("booga"); - // let rowid_2 = 555; - // let conn = DbInitializerReal::default() - // .initialize(&home_dir, DbInitializationConfig::test_default()) - // .unwrap(); - // let record_seeds = [ - // (&wallet_1.to_string(), 12345, 1_000_000_000, None), - // (&wallet_2.to_string(), 23456, 1_000_000_111, Some(540)), - // ]; - // record_seeds - // .into_iter() - // .for_each(|(wallet, balance, timestamp, rowid_opt)| { - // insert_payable_record_fn(&*conn, wallet, balance, timestamp, rowid_opt) - // }); - // - // let result = explanatory_extension(&*conn, &[(&wallet_1, rowid_1), (&wallet_2, rowid_2)]); - // - // assert_eq!(result, "\ - // The demanded data according to ( Wallet: 0x000000000000000000000000000000686f6f6761, Rowid: 550 ), \ - // ( Wallet: 0x000000000000000000000000000000626f6f6761, Rowid: 555 ) looks different from \ - // the resulting state ( Wallet: 0x000000000000000000000000000000626f6f6761, Rowid: 540 ), \ - // ( Wallet: 0x000000000000000000000000000000686f6f6761, Rowid: N/A )!. \ - // Operation failed.\n\ - // Notes:\n\ - // a) if row ids have stayed non-populated it points out that writing failed but without the double \ - // payment threat,\n\ - // b) if some accounts on the resulting side are missing, other kind of serious issues should be \ - // suspected but see other\npoints to figure out if you were put in danger of double payment,\n\ - // c) seeing ids different from those demanded might be a sign of some payments having been doubled.\n\ - // The operation which is supposed to clear out the ids of the payments previously requested for \ - // this account\nprobably had not managed to complete successfully before another payment was \ - // requested: preventive measures failed.\n".to_string()) - } - - #[test] - fn mark_pending_payables_rowids_handles_general_sql_error() { - // TODO Will be an object of removal in GH-662 - // let home_dir = ensure_node_home_directory_exists( - // "payable_dao", - // "mark_pending_payables_rowids_handles_general_sql_error", - // ); - // let wallet = make_wallet("booga"); - // let rowid = 656; - // let single_mark_instruction = MarkPendingPayableID::new(wallet.address(), rowid); - // let conn = payable_read_only_conn(&home_dir); - // let conn_wrapped = ConnectionWrapperReal::new(conn); - // let subject = PayableDaoReal::new(Box::new(conn_wrapped)); - // - // let result = subject.mark_pending_payables_rowids(&[single_mark_instruction]); - // - // assert_eq!( - // result, - // Err(PayableDaoError::RusqliteError( - // "Multi-row update to mark pending payable hit these errors: [SqliteFailure(\ - // Error { code: ReadOnly, extended_code: 8 }, Some(\"attempt to write a readonly \ - // database\"))]" - // .to_string() - // )) - // ) - } - - #[test] - //#[should_panic(expected = "broken code: empty input is not permit to enter this method")] - fn mark_pending_payables_rowids_is_strict_about_empty_input() { - // TODO Will be an object of removal in GH-662 - // let wrapped_conn = ConnectionWrapperMock::default(); - // let subject = PayableDaoReal::new(Box::new(wrapped_conn)); - // - // let _ = subject.mark_pending_payables_rowids(&[]); - } - struct TestSetupValuesHolder { account_1: TxWalletAndTimestamp, account_2: TxWalletAndTimestamp, diff --git a/node/src/accountant/scanners/mod.rs b/node/src/accountant/scanners/mod.rs index 81c882d52..aa54b9b43 100644 --- a/node/src/accountant/scanners/mod.rs +++ b/node/src/accountant/scanners/mod.rs @@ -755,60 +755,6 @@ impl PayableScanner { } } - // TODO this has become dead (GH-662) - #[allow(dead_code)] - fn mark_pending_payable(&self, _sent_payments: &[&PendingPayable], _logger: &Logger) { - todo!("remove me when the time comes") - // fn missing_fingerprints_msg(nonexistent: &[PendingPayableMissingInDb]) -> String { - // format!( - // "Expected pending payable fingerprints for {} were not found; system unreliable", - // comma_joined_stringifiable(nonexistent, |pp_triple| format!( - // "(tx: {:?}, to wallet: {})", - // pp_triple.hash, pp_triple.recipient - // )) - // ) - // } - // fn ready_data_for_supply<'a>( - // existent: &'a [PendingPayableMissingInDb], - // ) -> Vec<(&'a Wallet, u64)> { - // existent - // .iter() - // .map(|pp_triple| (pp_triple.recipient, pp_triple.rowid_opt.expectv("rowid"))) - // .collect() - // } - // - // // TODO eventually should be taken over by GH-655 - // let missing_sent_tx_records = - // self.check_for_missing_records(sent_payments); - // - // if !existent.is_empty() { - // if let Err(e) = self - // .payable_dao - // .as_ref() - // .mark_pending_payables_rowids(&existent) - // { - // mark_pending_payable_fatal_error( - // sent_payments, - // &nonexistent, - // e, - // missing_fingerprints_msg, - // logger, - // ) - // } - // debug!( - // logger, - // "Payables {} marked as pending in the payable table", - // comma_joined_stringifiable(sent_payments, |pending_p| format!( - // "{:?}", - // pending_p.hash - // )) - // ) - // } - // if !missing_sent_tx_records.is_empty() { - // panic!("{}", missing_fingerprints_msg(&missing_sent_tx_records)) - // } - } - fn handle_sent_payable_errors( &self, err_opt: Option, diff --git a/node/src/accountant/test_utils.rs b/node/src/accountant/test_utils.rs index 8db54d693..483c69d25 100644 --- a/node/src/accountant/test_utils.rs +++ b/node/src/accountant/test_utils.rs @@ -652,25 +652,6 @@ impl PayableDao for PayableDaoMock { self.more_money_payable_results.borrow_mut().remove(0) } - fn mark_pending_payables_rowids( - &self, - _mark_instructions: &[MarkPendingPayableID], - ) -> Result<(), PayableDaoError> { - todo!("will be removed in the associated card - GH-662") - // self.mark_pending_payables_rowids_params - // .lock() - // .unwrap() - // .push( - // mark_instructions - // .iter() - // .map(|(wallet, id)| ((*wallet).clone(), *id)) - // .collect(), - // ); - // self.mark_pending_payables_rowids_results - // .borrow_mut() - // .remove(0) - } - fn transactions_confirmed(&self, confirmed_payables: &[SentTx]) -> Result<(), PayableDaoError> { self.transactions_confirmed_params .lock() From a02ea20a011e89db01936d57c4820b769ce150aa Mon Sep 17 00:00:00 2001 From: Bert Date: Mon, 22 Sep 2025 11:40:15 +0200 Subject: [PATCH 03/11] GH-662: repaired custom query --- .../db_access_objects/payable_dao.rs | 206 +++++++++++++----- 1 file changed, 152 insertions(+), 54 deletions(-) diff --git a/node/src/accountant/db_access_objects/payable_dao.rs b/node/src/accountant/db_access_objects/payable_dao.rs index 18b5c5ee3..1a30d7e4d 100644 --- a/node/src/accountant/db_access_objects/payable_dao.rs +++ b/node/src/accountant/db_access_objects/payable_dao.rs @@ -4,7 +4,7 @@ use crate::accountant::db_access_objects::sent_payable_dao::SentTx; use crate::accountant::db_access_objects::utils; use crate::accountant::db_access_objects::utils::{ sum_i128_values_from_table, to_unix_timestamp, AssemblerFeeder, CustomQuery, DaoFactoryReal, - PayableAccountWithTxInfo, RangeStmConfig, RowId, TopStmConfig, TxHash, VigilantRusqliteFlatten, + PayableAccountWithTxInfo, RangeStmConfig, RowId, TopStmConfig, VigilantRusqliteFlatten, }; use crate::accountant::db_big_integer::big_int_db_processor::KeyVariants::WalletAddress; use crate::accountant::db_big_integer::big_int_db_processor::{ @@ -24,8 +24,10 @@ use masq_lib::utils::ExpectValue; use rusqlite::OptionalExtension; use rusqlite::{Error, Row}; use std::fmt::Debug; +use std::str::FromStr; use std::time::SystemTime; use web3::types::H256; +use masq_lib::messages::CurrentTxInfo; #[derive(Debug, PartialEq, Eq)] pub enum PayableDaoError { @@ -190,14 +192,14 @@ impl PayableDao for PayableDaoReal { gwei_min_resolution_clause: "and ((balance_high_b > 0) or ((balance_high_b = 0) and (balance_low_b >= 1000000000)))", secondary_order_param: "last_paid_timestamp asc" }; - todo!() - // custom_query.query::<_, i64, _, _>( - // self.conn.as_ref(), - // Self::stm_assembler_of_payable_cq, - // variant_top, - // variant_range, - // Self::create_payable_account, - // ) + + custom_query.query::<_, i64, _, _>( + self.conn.as_ref(), + Self::stm_assembler_of_payable_cq, + variant_top, + variant_range, + Self::create_payable_account_with_tx_info, + ) } fn total(&self) -> u128 { @@ -273,25 +275,29 @@ impl PayableDaoReal { } } - fn create_payable_account(row: &Row) -> rusqlite::Result { + fn create_payable_account_with_tx_info(row: &Row) -> rusqlite::Result { let wallet_result: Result = row.get(0); let balance_high_bytes_result = row.get(1); let balance_low_bytes_result = row.get(2); let last_paid_timestamp_result = row.get(3); + let tx_hash_opt_result = row.get(4); + let previous_failures = row.get(5); match ( wallet_result, balance_high_bytes_result, balance_low_bytes_result, last_paid_timestamp_result, + tx_hash_opt_result, + previous_failures ) { - (Ok(wallet), Ok(high_bytes), Ok(low_bytes), Ok(last_paid_timestamp)) => { - Ok(PayableAccount { + (Ok(wallet), Ok(high_bytes), Ok(low_bytes), Ok(last_paid_timestamp), Ok(tx_hash_opt), Ok(previous_failures)) => { + Ok(PayableAccountWithTxInfo {account: PayableAccount{ wallet, balance_wei: checked_conversion::(BigIntDivider::reconstitute( high_bytes, low_bytes, )), last_paid_timestamp: utils::from_unix_timestamp(last_paid_timestamp), - }) + }, tx_opt: Self::maybe_construct_tx_info(tx_hash_opt, previous_failures)}) } e => panic!( "Database is corrupt: PAYABLE table columns and/or types: {:?}", @@ -300,26 +306,52 @@ impl PayableDaoReal { } } + fn maybe_construct_tx_info(tx_hash_opt: Option, previous_failures: usize)->Option{ + if tx_hash_opt.is_some() || previous_failures > 0{ + Some(CurrentTxInfo{ pending_opt: tx_hash_opt.map(|tx_hash_str|H256::from_str(&tx_hash_str[2..]).unwrap_or_else(|_|panic!("Wrong tx hash format: {}", tx_hash_str))), failures: previous_failures }) + } else { + None + } + } + fn stm_assembler_of_payable_cq(feeder: AssemblerFeeder) -> String { - format!( - "select - wallet_address, - balance_high_b, - balance_low_b, - last_paid_timestamp - from - payable - {} {} - order by - {}, - {} - {}", + let stm = format!( + "SELECT + p.wallet_address, + p.balance_high_b, + p.balance_low_b, + p.last_paid_timestamp, + CASE WHEN s.status LIKE '%Pending%' THEN s.tx_hash ELSE NULL END AS pending_tx_hash, + CASE WHEN EXISTS ( + SELECT 1 + FROM failed_payable + WHERE status IS NOT 'Concluded' + AND receiver_address = p.wallet_address + ) + THEN ( + SELECT COUNT(*) + FROM failed_payable + WHERE nonce = (SELECT MAX(nonce) FROM failed_payable) + ) + ELSE 0 + END AS recent_failures_count + FROM + payable p + LEFT JOIN + sent_payable s on p.wallet_address = s.receiver_address + {} {} + ORDER BY + {}, + {} + {}", feeder.main_where_clause, feeder.where_clause_extension, feeder.order_by_first_param, feeder.order_by_second_param, feeder.limit_clause - ) + ); + eprintln!("{}", stm); + stm } } @@ -332,25 +364,20 @@ impl TableNameDAO for PayableDaoReal { #[cfg(test)] mod tests { use super::*; - use crate::accountant::db_access_objects::failed_payable_dao; - use crate::accountant::db_access_objects::failed_payable_dao::{ - FailedPayableDao, FailedPayableDaoReal, - }; + use crate::accountant::db_access_objects::failed_payable_dao::{FailedPayableDao, FailedPayableDaoReal, FailedTx}; use crate::accountant::db_access_objects::sent_payable_dao::{ SentPayableDao, SentPayableDaoReal, SentTx, TxStatus, }; - use crate::accountant::db_access_objects::utils::{ - current_unix_timestamp, from_unix_timestamp, to_unix_timestamp, - }; + use crate::accountant::db_access_objects::utils::{current_unix_timestamp, from_unix_timestamp, to_unix_timestamp, TxHash}; use crate::accountant::gwei_to_wei; use crate::accountant::test_utils::{ assert_account_creation_fn_fails_on_finding_wrong_columns_and_value_types, make_failed_tx, - make_sent_tx, trick_rusqlite_with_read_only_conn, FailedPayableDaoFactoryMock, + make_sent_tx, trick_rusqlite_with_read_only_conn, }; use crate::blockchain::errors::validation_status::ValidationStatus; use crate::blockchain::test_utils::make_tx_hash; use crate::database::db_initializer::{ - DbInitializationConfig, DbInitializer, DbInitializerReal, DATABASE_FILE, + DbInitializationConfig, DbInitializer, DbInitializerReal, }; use crate::database::rusqlite_wrappers::ConnectionWrapperReal; use crate::test_utils::make_wallet; @@ -861,9 +888,19 @@ mod tests { .unwrap(); } + struct TxFailuresRecordsAdjustment { + failed_txs: Vec + } + + struct HealthyPendingTxAdjustment { + pending_tx: SentTx + } + fn accounts_for_tests_of_top_records( + sent_tx_adjustment_opt: Option, + failed_txs_adjustment_opt: Option, now: i64, - ) -> Box PayableDaoReal> + ) -> Box PayableDaoReal> { Box::new( move |payable_dao_real: PayableDaoReal, @@ -903,12 +940,14 @@ mod tests { "0x5555555555555555555555555555555555555555", 10_000_000_100, ); - let mut failed_tx = make_failed_tx(123); - failed_tx.receiver_address = - Wallet::new("0x5555555555555555555555555555555555555555").address(); - failed_payable_dao_real - .insert_new_records(&vec![failed_tx]) - .unwrap(); + + if let Some(failed_txs_adjustment) = failed_txs_adjustment_opt { + failed_payable_dao_real.insert_new_records(&failed_txs_adjustment.failed_txs).unwrap() + } + if let Some(sent_tx_adjustment) = sent_tx_adjustment_opt { + sent_payable_dao_real.insert_new_records(&vec![sent_tx_adjustment.pending_tx]).unwrap() + } + payable_dao_real }, ) @@ -920,7 +959,24 @@ mod tests { // Two accounts differ only in the debt age but not the balance which allows to check double // ordering, primarily by balance and then age. let now = current_unix_timestamp(); - let main_test_setup = accounts_for_tests_of_top_records(now); + let wallet_addr = "0x5555555555555555555555555555555555555555"; + let mut sent_tx = make_sent_tx(789); + sent_tx.receiver_address = Wallet::new(wallet_addr).address(); + let sent_tx_hash = sent_tx.hash; + let sent_tx_adjustment = HealthyPendingTxAdjustment { + pending_tx: sent_tx + }; + let mut failed_tx_1 = make_failed_tx(123); + let wallet_addr = "0x2222222222222222222222222222222222222222"; + failed_tx_1.receiver_address = Wallet::new(wallet_addr).address(); + let failed_tx_nonce_1 = failed_tx_1.nonce; + let mut failed_tx_2 = make_failed_tx(456); + failed_tx_2.receiver_address = Wallet::new(wallet_addr).address(); + failed_tx_2.nonce = failed_tx_nonce_1; + let failure_tx_adjustment = TxFailuresRecordsAdjustment { + failed_txs: vec![failed_tx_1, failed_tx_2] + }; + let main_test_setup = accounts_for_tests_of_top_records(Some(sent_tx_adjustment), Some(failure_tx_adjustment), now); let subject = custom_query_test_body_for_payable( "custom_query_in_top_records_mode_with_default_ordering", main_test_setup, @@ -942,7 +998,10 @@ mod tests { balance_wei: 7_562_000_300_000, last_paid_timestamp: from_unix_timestamp(now - 86_001), }, - tx_opt: None + tx_opt: Some(CurrentTxInfo { + pending_opt: None, + failures: 2 + }) }, PayableAccountWithTxInfo { account: PayableAccount { @@ -951,7 +1010,7 @@ mod tests { last_paid_timestamp: from_unix_timestamp(now - 86_401), }, tx_opt: Some(CurrentTxInfo { - pending_opt: Some(make_tx_hash(123)), + pending_opt: Some(sent_tx_hash), failures: 0 }) }, @@ -961,10 +1020,7 @@ mod tests { balance_wei: 10_000_000_100, last_paid_timestamp: from_unix_timestamp(now - 86_300), }, - tx_opt: Some(CurrentTxInfo { - pending_opt: None, - failures: 2 - }) + tx_opt: None }, ] ); @@ -976,7 +1032,14 @@ mod tests { // Two accounts differ only in the debt age but not the balance which allows to check double // ordering, primarily by balance and then age. let now = current_unix_timestamp(); - let main_test_setup = accounts_for_tests_of_top_records(now); + let wallet_addr = "0x1111111111111111111111111111111111111111"; + let mut sent_tx = make_sent_tx(789); + sent_tx.receiver_address = Wallet::new(wallet_addr).address(); + let sent_tx_hash = sent_tx.hash; + let sent_tx_adjustment = HealthyPendingTxAdjustment { + pending_tx: sent_tx + }; + let main_test_setup = accounts_for_tests_of_top_records(Some(sent_tx_adjustment), None, now); let subject = custom_query_test_body_for_payable( "custom_query_in_top_records_mode_ordered_by_age", main_test_setup, @@ -1006,7 +1069,7 @@ mod tests { balance_wei: 1_000_000_002, last_paid_timestamp: from_unix_timestamp(now - 86_401), }, - tx_opt: None + tx_opt: Some(CurrentTxInfo{ pending_opt: Some(sent_tx_hash), failures: 0 }) }, PayableAccountWithTxInfo { account: PayableAccount { @@ -1290,7 +1353,7 @@ mod tests { )] fn create_payable_account_panics_on_database_error() { assert_account_creation_fn_fails_on_finding_wrong_columns_and_value_types( - PayableDaoReal::create_payable_account, + PayableDaoReal::create_payable_account_with_tx_info, ); } @@ -1305,7 +1368,7 @@ mod tests { fn custom_query_test_body_for_payable(test_name: &str, main_setup_fn: F) -> PayableDaoReal where - F: Fn(PayableDaoReal, SentPayableDaoReal, FailedPayableDaoReal) -> PayableDaoReal, + F: FnOnce(PayableDaoReal, SentPayableDaoReal, FailedPayableDaoReal) -> PayableDaoReal, { let home_dir = ensure_node_home_directory_exists("payable_dao", test_name); let conn = || { @@ -1318,4 +1381,39 @@ mod tests { let payable_dao = PayableDaoReal::new(conn()); main_setup_fn(payable_dao, sent_payable_dao, failed_payable_dao) } + + #[test] + fn maybe_construct_tx_info_with_tx_hash_present_and_no_errors(){ + let tx_hash = make_tx_hash(123); + + let result = PayableDaoReal::maybe_construct_tx_info(Some(format!("{:?}", tx_hash)), 0); + + assert_eq!(result, Some(CurrentTxInfo{ pending_opt: Some(tx_hash), failures: 0 })); + } + + #[test] + fn maybe_construct_tx_info_with_tx_hash_present_and_also_errors(){ + let tx_hash = make_tx_hash(123); + let errors = 3; + + let result = PayableDaoReal::maybe_construct_tx_info(Some(format!("{:?}", tx_hash)), errors); + + assert_eq!(result, Some(CurrentTxInfo{ pending_opt: Some(make_tx_hash(123)), failures: errors })); + } + + #[test] + fn maybe_construct_tx_info_with_only_errors_present(){ + let errors = 1; + + let result = PayableDaoReal::maybe_construct_tx_info(None, errors); + + assert_eq!(result, Some(CurrentTxInfo{ pending_opt: None, failures: errors })); + } + + #[test] + fn maybe_construct_tx_info_returns_none(){ + let result = PayableDaoReal::maybe_construct_tx_info(None, 0); + + assert_eq!(result, None); + } } From afa5e26969bb495d7f106c3d0c1b7253564e7b19 Mon Sep 17 00:00:00 2001 From: Bert Date: Mon, 22 Sep 2025 15:36:05 +0200 Subject: [PATCH 04/11] GH-662: SQL improved --- masq_lib/src/messages.rs | 4 +- .../db_access_objects/payable_dao.rs | 353 ++++++++++++++---- node/src/accountant/mod.rs | 6 +- node/src/accountant/test_utils.rs | 2 +- 4 files changed, 281 insertions(+), 84 deletions(-) diff --git a/masq_lib/src/messages.rs b/masq_lib/src/messages.rs index dbb15dadb..e778446ba 100644 --- a/masq_lib/src/messages.rs +++ b/masq_lib/src/messages.rs @@ -686,8 +686,8 @@ pub struct UiPayableAccount { #[derive(Serialize, Deserialize, Debug, PartialEq, Eq)] pub struct CurrentTxInfo { - #[serde(rename = "pendingOpt")] - pub pending_opt: Option, + #[serde(rename = "pendingTxHashOpt")] + pub pending_tx_hash_opt: Option, pub failures: usize, } diff --git a/node/src/accountant/db_access_objects/payable_dao.rs b/node/src/accountant/db_access_objects/payable_dao.rs index 1a30d7e4d..e6518c419 100644 --- a/node/src/accountant/db_access_objects/payable_dao.rs +++ b/node/src/accountant/db_access_objects/payable_dao.rs @@ -12,12 +12,11 @@ use crate::accountant::db_big_integer::big_int_db_processor::{ ParamByUse, SQLParamsBuilder, TableNameDAO, WeiChange, WeiChangeDirection, }; use crate::accountant::db_big_integer::big_int_divider::BigIntDivider; -use crate::accountant::{checked_conversion, sign_conversion, PendingPayableId}; +use crate::accountant::{checked_conversion, sign_conversion}; use crate::database::rusqlite_wrappers::ConnectionWrapper; use crate::sub_lib::wallet::Wallet; use ethabi::Address; #[cfg(test)] -use ethereum_types::{BigEndianHash, U256}; use itertools::Either; use masq_lib::utils::ExpectValue; #[cfg(test)] @@ -240,14 +239,12 @@ impl PayableDao for PayableDaoReal { let high_bytes_result = row.get(0); let low_bytes_result = row.get(1); let last_paid_timestamp_result = row.get(2); - let pending_payable_rowid_result: Result, Error> = row.get(3); match ( high_bytes_result, low_bytes_result, last_paid_timestamp_result, - pending_payable_rowid_result, ) { - (Ok(high_bytes), Ok(low_bytes), Ok(last_paid_timestamp), Ok(rowid)) => { + (Ok(high_bytes), Ok(low_bytes), Ok(last_paid_timestamp)) => { Ok(PayableAccount { wallet: wallet.clone(), balance_wei: checked_conversion::(BigIntDivider::reconstitute( @@ -308,7 +305,7 @@ impl PayableDaoReal { fn maybe_construct_tx_info(tx_hash_opt: Option, previous_failures: usize)->Option{ if tx_hash_opt.is_some() || previous_failures > 0{ - Some(CurrentTxInfo{ pending_opt: tx_hash_opt.map(|tx_hash_str|H256::from_str(&tx_hash_str[2..]).unwrap_or_else(|_|panic!("Wrong tx hash format: {}", tx_hash_str))), failures: previous_failures }) + Some(CurrentTxInfo{ pending_tx_hash_opt: tx_hash_opt.map(|tx_hash_str|H256::from_str(&tx_hash_str[2..]).unwrap_or_else(|_|panic!("Wrong tx hash format: {}", tx_hash_str))), failures: previous_failures }) } else { None } @@ -321,17 +318,30 @@ impl PayableDaoReal { p.balance_high_b, p.balance_low_b, p.last_paid_timestamp, - CASE WHEN s.status LIKE '%Pending%' THEN s.tx_hash ELSE NULL END AS pending_tx_hash, + CASE WHEN s.status LIKE '%Pending%' + THEN s.tx_hash + ELSE NULL + END AS pending_tx_hash, + /* + The following case stm counts the failing attempts for a tx processing that hasn't + ended yet and is ongoing. + */ CASE WHEN EXISTS ( SELECT 1 FROM failed_payable - WHERE status IS NOT 'Concluded' + WHERE status NOT LIKE '%Concluded%' AND receiver_address = p.wallet_address ) THEN ( + WITH nonces_of_failures_by_wallet AS + ( + SELECT nonce + FROM failed_payable + WHERE receiver_address = p.wallet_address + ) SELECT COUNT(*) - FROM failed_payable - WHERE nonce = (SELECT MAX(nonce) FROM failed_payable) + FROM nonces_of_failures_by_wallet + WHERE nonce = (SELECT MAX(nonce) FROM nonces_of_failures_by_wallet) ) ELSE 0 END AS recent_failures_count @@ -364,7 +374,7 @@ impl TableNameDAO for PayableDaoReal { #[cfg(test)] mod tests { use super::*; - use crate::accountant::db_access_objects::failed_payable_dao::{FailedPayableDao, FailedPayableDaoReal, FailedTx}; + use crate::accountant::db_access_objects::failed_payable_dao::{FailedPayableDao, FailedPayableDaoReal, FailedTx, FailureStatus}; use crate::accountant::db_access_objects::sent_payable_dao::{ SentPayableDao, SentPayableDaoReal, SentTx, TxStatus, }; @@ -386,7 +396,7 @@ mod tests { use masq_lib::messages::TopRecordsOrdering::{Age, Balance}; use masq_lib::test_utils::utils::ensure_node_home_directory_exists; use rusqlite::ToSql; - use rusqlite::{Connection, OpenFlags}; + use rusqlite::{Connection}; use std::path::Path; use std::time::Duration; @@ -869,10 +879,7 @@ mod tests { assert_eq!(result, None) } - - type InsertPayableHelperFn<'b> = - &'b dyn for<'a> Fn(&'a dyn ConnectionWrapper, &'a str, i128, i64, Option); - + fn insert_payable_record_fn( conn: &dyn ConnectionWrapper, wallet: &str, @@ -888,24 +895,14 @@ mod tests { .unwrap(); } - struct TxFailuresRecordsAdjustment { - failed_txs: Vec - } - - struct HealthyPendingTxAdjustment { - pending_tx: SentTx - } - fn accounts_for_tests_of_top_records( - sent_tx_adjustment_opt: Option, - failed_txs_adjustment_opt: Option, now: i64, ) -> Box PayableDaoReal> { Box::new( move |payable_dao_real: PayableDaoReal, - sent_payable_dao_real: SentPayableDaoReal, - failed_payable_dao_real: FailedPayableDaoReal| { + _: SentPayableDaoReal, + _: FailedPayableDaoReal| { let insert_payable = |unix_time: i64, wallet_addr: &str, amount_minor: u128| { payable_dao_real .more_money_payable( @@ -941,13 +938,6 @@ mod tests { 10_000_000_100, ); - if let Some(failed_txs_adjustment) = failed_txs_adjustment_opt { - failed_payable_dao_real.insert_new_records(&failed_txs_adjustment.failed_txs).unwrap() - } - if let Some(sent_tx_adjustment) = sent_tx_adjustment_opt { - sent_payable_dao_real.insert_new_records(&vec![sent_tx_adjustment.pending_tx]).unwrap() - } - payable_dao_real }, ) @@ -956,27 +946,10 @@ mod tests { #[test] fn custom_query_in_top_records_mode_with_default_ordering() { // Accounts of balances smaller than one gwei don't qualify. - // Two accounts differ only in the debt age but not the balance which allows to check double - // ordering, primarily by balance and then age. + // Two accounts differ only in the debt age but not the balance, which allows checking + // double ordering, primarily by balance and then age. let now = current_unix_timestamp(); - let wallet_addr = "0x5555555555555555555555555555555555555555"; - let mut sent_tx = make_sent_tx(789); - sent_tx.receiver_address = Wallet::new(wallet_addr).address(); - let sent_tx_hash = sent_tx.hash; - let sent_tx_adjustment = HealthyPendingTxAdjustment { - pending_tx: sent_tx - }; - let mut failed_tx_1 = make_failed_tx(123); - let wallet_addr = "0x2222222222222222222222222222222222222222"; - failed_tx_1.receiver_address = Wallet::new(wallet_addr).address(); - let failed_tx_nonce_1 = failed_tx_1.nonce; - let mut failed_tx_2 = make_failed_tx(456); - failed_tx_2.receiver_address = Wallet::new(wallet_addr).address(); - failed_tx_2.nonce = failed_tx_nonce_1; - let failure_tx_adjustment = TxFailuresRecordsAdjustment { - failed_txs: vec![failed_tx_1, failed_tx_2] - }; - let main_test_setup = accounts_for_tests_of_top_records(Some(sent_tx_adjustment), Some(failure_tx_adjustment), now); + let main_test_setup = accounts_for_tests_of_top_records(now); let subject = custom_query_test_body_for_payable( "custom_query_in_top_records_mode_with_default_ordering", main_test_setup, @@ -998,10 +971,7 @@ mod tests { balance_wei: 7_562_000_300_000, last_paid_timestamp: from_unix_timestamp(now - 86_001), }, - tx_opt: Some(CurrentTxInfo { - pending_opt: None, - failures: 2 - }) + tx_opt: None }, PayableAccountWithTxInfo { account: PayableAccount { @@ -1009,10 +979,7 @@ mod tests { balance_wei: 10_000_000_100, last_paid_timestamp: from_unix_timestamp(now - 86_401), }, - tx_opt: Some(CurrentTxInfo { - pending_opt: Some(sent_tx_hash), - failures: 0 - }) + tx_opt: None }, PayableAccountWithTxInfo { account: PayableAccount { @@ -1029,17 +996,10 @@ mod tests { #[test] fn custom_query_in_top_records_mode_ordered_by_age() { // Accounts of balances smaller than one gwei don't qualify. - // Two accounts differ only in the debt age but not the balance which allows to check double - // ordering, primarily by balance and then age. + // Two accounts differ only in the debt age but not the balance, which allows checking + // double ordering, primarily by balance and then age. let now = current_unix_timestamp(); - let wallet_addr = "0x1111111111111111111111111111111111111111"; - let mut sent_tx = make_sent_tx(789); - sent_tx.receiver_address = Wallet::new(wallet_addr).address(); - let sent_tx_hash = sent_tx.hash; - let sent_tx_adjustment = HealthyPendingTxAdjustment { - pending_tx: sent_tx - }; - let main_test_setup = accounts_for_tests_of_top_records(Some(sent_tx_adjustment), None, now); + let main_test_setup = accounts_for_tests_of_top_records(now); let subject = custom_query_test_body_for_payable( "custom_query_in_top_records_mode_ordered_by_age", main_test_setup, @@ -1069,7 +1029,7 @@ mod tests { balance_wei: 1_000_000_002, last_paid_timestamp: from_unix_timestamp(now - 86_401), }, - tx_opt: Some(CurrentTxInfo{ pending_opt: Some(sent_tx_hash), failures: 0 }) + tx_opt: None }, PayableAccountWithTxInfo { account: PayableAccount { @@ -1083,6 +1043,243 @@ mod tests { ); } + #[test] + fn custom_query_top_records_mode_can_report_tx_info(){ + let now = current_unix_timestamp(); + let wallet_addr = "0x1111111111111111111111111111111111111111"; + let mut sent_tx_1 = make_sent_tx(789); + sent_tx_1.receiver_address = Wallet::new(wallet_addr).address(); + let sent_tx_hash_1 = sent_tx_1.hash; + let wallet_addr = "0x3333333333333333333333333333333333333333"; + let mut sent_tx_2 = make_sent_tx(345); + sent_tx_2.receiver_address = Wallet::new(wallet_addr).address(); + let sent_tx_hash_2 = sent_tx_2.hash; + let mut failed_tx_1 = make_failed_tx(123); + let wallet_addr = "0x2222222222222222222222222222222222222222"; + failed_tx_1.receiver_address = Wallet::new(wallet_addr).address(); + failed_tx_1.status = FailureStatus::Concluded; + failed_tx_1.nonce = 99; + let mut failed_tx_2 = make_failed_tx(456); + failed_tx_2.receiver_address = Wallet::new(wallet_addr).address(); + failed_tx_2.nonce = 99; + failed_tx_2.status = FailureStatus::RecheckRequired(ValidationStatus::Waiting); + // Will be ignored as it is not of the highest nonce for this wallet + let mut failed_tx_3 = make_failed_tx(678); + failed_tx_3.receiver_address = Wallet::new(wallet_addr).address(); + failed_tx_3.nonce = 98; + failed_tx_3.status = FailureStatus::Concluded; + let mut failed_tx_4 = make_failed_tx(567); + let wallet_addr = "0x3333333333333333333333333333333333333333"; + failed_tx_4.receiver_address = Wallet::new(wallet_addr).address(); + failed_tx_4.status = FailureStatus::RetryRequired; + failed_tx_4.nonce = 100; + let main_test_setup = Box::new( + move |payable_dao_real: PayableDaoReal, + sent_payable_dao_real: SentPayableDaoReal, + failed_payable_dao_real: FailedPayableDaoReal| { + let insert_payable = |unix_time: i64, wallet_addr: &str, amount_minor: u128| { + payable_dao_real + .more_money_payable( + from_unix_timestamp(unix_time), + &Wallet::new(wallet_addr), + amount_minor, + ) + .unwrap() + }; + insert_payable( + now - 80_000, + "0x1111111111111111111111111111111111111111", + 222_000_000_000, + ); + insert_payable( + now - 80_000, + "0x2222222222222222222222222222222222222222", + 333_000_000_000, + ); + insert_payable( + now - 80_000, + "0x3333333333333333333333333333333333333333", + 111_000_000_000, + ); + insert_payable( + now - 80_000, + "0x4444444444444444444444444444444444444444", + 1_000_000_000, + ); + failed_payable_dao_real.insert_new_records(&vec![failed_tx_1, failed_tx_2, failed_tx_3, failed_tx_4]).unwrap(); + sent_payable_dao_real.insert_new_records(&vec![sent_tx_1, sent_tx_2]).unwrap(); + payable_dao_real + }, + ); + let subject = custom_query_test_body_for_payable( + "custom_query_top_records_mode_can_report_tx_info", + main_test_setup, + ); + + let result = subject + .custom_query(CustomQuery::TopRecords { + count: 3, + ordered_by: Balance, + }) + .unwrap(); + + assert_eq!( + result, + vec![ + PayableAccountWithTxInfo { + account: PayableAccount { + wallet: Wallet::new("0x2222222222222222222222222222222222222222"), + balance_wei: 333_000_000_000, + last_paid_timestamp: from_unix_timestamp(now - 80_000), + }, + tx_opt: Some(CurrentTxInfo{ pending_tx_hash_opt: None, failures: 2 }) + }, + PayableAccountWithTxInfo { + account: PayableAccount { + wallet: Wallet::new("0x1111111111111111111111111111111111111111"), + balance_wei: 222_000_000_000, + last_paid_timestamp: from_unix_timestamp(now - 80_000), + }, + tx_opt: Some(CurrentTxInfo{ pending_tx_hash_opt: Some(sent_tx_hash_1), failures: 0 }) + }, + PayableAccountWithTxInfo { + account: PayableAccount { + wallet: Wallet::new("0x3333333333333333333333333333333333333333"), + balance_wei: 111_000_000_000, + last_paid_timestamp: from_unix_timestamp(now - 80_000), + }, + tx_opt: Some(CurrentTxInfo{ pending_tx_hash_opt: Some(sent_tx_hash_2), failures: 1 }) + } + ] + ); + } + + #[test] + fn custom_query_range_mode_can_report_tx_info(){ + let now = current_unix_timestamp(); + let wallet_addr = "0x2222222222222222222222222222222222222222"; + let mut sent_tx_1 = make_sent_tx(789); + sent_tx_1.receiver_address = Wallet::new(wallet_addr).address(); + let sent_tx_hash_1 = sent_tx_1.hash; + let wallet_addr = "0x4444444444444444444444444444444444444444"; + let mut sent_tx_2 = make_sent_tx(345); + sent_tx_2.receiver_address = Wallet::new(wallet_addr).address(); + let sent_tx_hash_2 = sent_tx_2.hash; + let mut failed_tx_1 = make_failed_tx(123); + let wallet_addr = "0x3333333333333333333333333333333333333333"; + failed_tx_1.receiver_address = Wallet::new(wallet_addr).address(); + failed_tx_1.nonce = 99; + let wallet_addr = "0x2222222222222222222222222222222222222222"; + let mut failed_tx_2 = make_failed_tx(456); + failed_tx_2.receiver_address = Wallet::new(wallet_addr).address(); + failed_tx_2.nonce = 100; + let mut failed_tx_3 = make_failed_tx(567); + failed_tx_3.receiver_address = Wallet::new(wallet_addr).address(); + failed_tx_3.nonce = 100; + let mut failed_tx_4 = make_failed_tx(222); + let wallet_addr = "0x5555555555555555555555555555555555555555"; + failed_tx_4.receiver_address = Wallet::new(wallet_addr).address(); + failed_tx_4.nonce = 98; + failed_tx_4.status = FailureStatus::Concluded; + let main_test_setup = Box::new( + move |payable_dao_real: PayableDaoReal, + sent_payable_dao_real: SentPayableDaoReal, + failed_payable_dao_real: FailedPayableDaoReal| { + let insert_payable = |unix_time: i64, wallet_addr: &str, amount_minor: u128| { + payable_dao_real + .more_money_payable( + from_unix_timestamp(unix_time), + &Wallet::new(wallet_addr), + amount_minor, + ) + .unwrap() + }; + insert_payable( + now - 80_000, + "0x1111111111111111111111111111111111111111", + 5_000_000_000, + ); + insert_payable( + now - 80_000, + "0x2222222222222222222222222222222222222222", + 4_000_000_000, + ); + insert_payable( + now - 80_000, + "0x3333333333333333333333333333333333333333", + 3_000_000_000, + ); + insert_payable( + now - 80_000, + "0x4444444444444444444444444444444444444444", + 2_000_000_000, + ); + insert_payable( + now - 80_000, + "0x5555555555555555555555555555555555555555", + 1_000_000_000, + ); + failed_payable_dao_real.insert_new_records(&vec![failed_tx_1, failed_tx_2, failed_tx_3, failed_tx_4]).unwrap(); + sent_payable_dao_real.insert_new_records(&vec![sent_tx_1, sent_tx_2]).unwrap(); + payable_dao_real + }, + ); + let subject = custom_query_test_body_for_payable( + "custom_query_range_mode_can_report_tx_info", + main_test_setup, + ); + + let result = subject + .custom_query(CustomQuery::RangeQuery { + max_age_s: 80_100, + min_age_s: 79_900, + max_amount_gwei: 4, + min_amount_gwei: 1, + timestamp: from_unix_timestamp(now), + }) + .unwrap(); + + assert_eq!( + result, + vec![ + PayableAccountWithTxInfo { + account: PayableAccount { + wallet: Wallet::new("0x2222222222222222222222222222222222222222"), + balance_wei: 4_000_000_000, + last_paid_timestamp: from_unix_timestamp(now - 80_000), + }, + tx_opt: Some(CurrentTxInfo{ pending_tx_hash_opt: Some(sent_tx_hash_1), failures: 2 }) + }, + PayableAccountWithTxInfo { + account: PayableAccount { + wallet: Wallet::new("0x3333333333333333333333333333333333333333"), + balance_wei: 3_000_000_000, + last_paid_timestamp: from_unix_timestamp(now - 80_000), + }, + tx_opt: Some(CurrentTxInfo{ pending_tx_hash_opt: None, failures: 1 }) + }, + PayableAccountWithTxInfo { + account: PayableAccount { + wallet: Wallet::new("0x4444444444444444444444444444444444444444"), + balance_wei: 2_000_000_000, + last_paid_timestamp: from_unix_timestamp(now - 80_000), + }, + tx_opt: Some(CurrentTxInfo{ pending_tx_hash_opt: Some(sent_tx_hash_2), failures: 0 }) + }, + PayableAccountWithTxInfo { + account: PayableAccount { + wallet: Wallet::new("0x5555555555555555555555555555555555555555"), + balance_wei: 1_000_000_000, + last_paid_timestamp: from_unix_timestamp(now - 80_000), + }, + // No CurrentTxInfo despite existing FailedTx records (The record has the failure + // status = 'Concluded') + tx_opt: None + } + ] + ); + } + #[test] fn custom_query_handles_empty_table_in_range_mode() { let main_test_setup = @@ -1201,7 +1398,7 @@ mod tests { last_paid_timestamp: from_unix_timestamp(now - 100_401), }, tx_opt: Some(CurrentTxInfo { - pending_opt: Some(make_tx_hash(0xABC)), + pending_tx_hash_opt: Some(make_tx_hash(0xABC)), failures: 0 }) }, @@ -1388,7 +1585,7 @@ mod tests { let result = PayableDaoReal::maybe_construct_tx_info(Some(format!("{:?}", tx_hash)), 0); - assert_eq!(result, Some(CurrentTxInfo{ pending_opt: Some(tx_hash), failures: 0 })); + assert_eq!(result, Some(CurrentTxInfo{ pending_tx_hash_opt: Some(tx_hash), failures: 0 })); } #[test] @@ -1398,7 +1595,7 @@ mod tests { let result = PayableDaoReal::maybe_construct_tx_info(Some(format!("{:?}", tx_hash)), errors); - assert_eq!(result, Some(CurrentTxInfo{ pending_opt: Some(make_tx_hash(123)), failures: errors })); + assert_eq!(result, Some(CurrentTxInfo{ pending_tx_hash_opt: Some(make_tx_hash(123)), failures: errors })); } #[test] @@ -1407,7 +1604,7 @@ mod tests { let result = PayableDaoReal::maybe_construct_tx_info(None, errors); - assert_eq!(result, Some(CurrentTxInfo{ pending_opt: None, failures: errors })); + assert_eq!(result, Some(CurrentTxInfo{ pending_tx_hash_opt: None, failures: errors })); } #[test] diff --git a/node/src/accountant/mod.rs b/node/src/accountant/mod.rs index f81bdad26..444b49a33 100644 --- a/node/src/accountant/mod.rs +++ b/node/src/accountant/mod.rs @@ -6193,7 +6193,7 @@ mod tests { last_paid_timestamp: SystemTime::now().sub(Duration::from_secs(7580)), }, tx_opt: Some(CurrentTxInfo { - pending_opt: None, + pending_tx_hash_opt: None, failures: 2, }), }]; @@ -6259,7 +6259,7 @@ mod tests { age_s: extracted_payable_ages[0], balance_gwei: 5, current_tx_info_opt: Some(CurrentTxInfo { - pending_opt: None, + pending_tx_hash_opt: None, failures: 2 }), },]), @@ -6460,7 +6460,7 @@ mod tests { last_paid_timestamp: SystemTime::now().sub(Duration::from_secs(5000)), }, tx_opt: Some(CurrentTxInfo { - pending_opt: None, + pending_tx_hash_opt: None, failures: 3, }), }]; diff --git a/node/src/accountant/test_utils.rs b/node/src/accountant/test_utils.rs index 483c69d25..d456cbaaf 100644 --- a/node/src/accountant/test_utils.rs +++ b/node/src/accountant/test_utils.rs @@ -8,7 +8,7 @@ use crate::accountant::db_access_objects::failed_payable_dao::{ FailureRetrieveCondition, FailureStatus, }; use crate::accountant::db_access_objects::payable_dao::{ - MarkPendingPayableID, PayableAccount, PayableDao, PayableDaoError, PayableDaoFactory, + PayableAccount, PayableDao, PayableDaoError, PayableDaoFactory, }; use crate::accountant::db_access_objects::receivable_dao::{ ReceivableAccount, ReceivableDao, ReceivableDaoError, ReceivableDaoFactory, From 18fedcf45eaa9c278aa438f6c159e597eafdf3c4 Mon Sep 17 00:00:00 2001 From: Bert Date: Tue, 23 Sep 2025 12:37:19 +0200 Subject: [PATCH 05/11] GH-662: formatting --- .../db_access_objects/payable_dao.rs | 156 +++++++++++++----- 1 file changed, 111 insertions(+), 45 deletions(-) diff --git a/node/src/accountant/db_access_objects/payable_dao.rs b/node/src/accountant/db_access_objects/payable_dao.rs index e6518c419..2e4a1f92d 100644 --- a/node/src/accountant/db_access_objects/payable_dao.rs +++ b/node/src/accountant/db_access_objects/payable_dao.rs @@ -18,6 +18,7 @@ use crate::sub_lib::wallet::Wallet; use ethabi::Address; #[cfg(test)] use itertools::Either; +use masq_lib::messages::CurrentTxInfo; use masq_lib::utils::ExpectValue; #[cfg(test)] use rusqlite::OptionalExtension; @@ -26,7 +27,6 @@ use std::fmt::Debug; use std::str::FromStr; use std::time::SystemTime; use web3::types::H256; -use masq_lib::messages::CurrentTxInfo; #[derive(Debug, PartialEq, Eq)] pub enum PayableDaoError { @@ -244,15 +244,13 @@ impl PayableDao for PayableDaoReal { low_bytes_result, last_paid_timestamp_result, ) { - (Ok(high_bytes), Ok(low_bytes), Ok(last_paid_timestamp)) => { - Ok(PayableAccount { - wallet: wallet.clone(), - balance_wei: checked_conversion::(BigIntDivider::reconstitute( - high_bytes, low_bytes, - )), - last_paid_timestamp: utils::from_unix_timestamp(last_paid_timestamp), - }) - } + (Ok(high_bytes), Ok(low_bytes), Ok(last_paid_timestamp)) => Ok(PayableAccount { + wallet: wallet.clone(), + balance_wei: checked_conversion::(BigIntDivider::reconstitute( + high_bytes, low_bytes, + )), + last_paid_timestamp: utils::from_unix_timestamp(last_paid_timestamp), + }), e => panic!( "Database is corrupt: PAYABLE table columns and/or types: {:?}", e @@ -272,7 +270,9 @@ impl PayableDaoReal { } } - fn create_payable_account_with_tx_info(row: &Row) -> rusqlite::Result { + fn create_payable_account_with_tx_info( + row: &Row, + ) -> rusqlite::Result { let wallet_result: Result = row.get(0); let balance_high_bytes_result = row.get(1); let balance_low_bytes_result = row.get(2); @@ -285,17 +285,25 @@ impl PayableDaoReal { balance_low_bytes_result, last_paid_timestamp_result, tx_hash_opt_result, - previous_failures + previous_failures, ) { - (Ok(wallet), Ok(high_bytes), Ok(low_bytes), Ok(last_paid_timestamp), Ok(tx_hash_opt), Ok(previous_failures)) => { - Ok(PayableAccountWithTxInfo {account: PayableAccount{ + ( + Ok(wallet), + Ok(high_bytes), + Ok(low_bytes), + Ok(last_paid_timestamp), + Ok(tx_hash_opt), + Ok(previous_failures), + ) => Ok(PayableAccountWithTxInfo { + account: PayableAccount { wallet, balance_wei: checked_conversion::(BigIntDivider::reconstitute( high_bytes, low_bytes, )), last_paid_timestamp: utils::from_unix_timestamp(last_paid_timestamp), - }, tx_opt: Self::maybe_construct_tx_info(tx_hash_opt, previous_failures)}) - } + }, + tx_opt: Self::maybe_construct_tx_info(tx_hash_opt, previous_failures), + }), e => panic!( "Database is corrupt: PAYABLE table columns and/or types: {:?}", e @@ -303,9 +311,18 @@ impl PayableDaoReal { } } - fn maybe_construct_tx_info(tx_hash_opt: Option, previous_failures: usize)->Option{ - if tx_hash_opt.is_some() || previous_failures > 0{ - Some(CurrentTxInfo{ pending_tx_hash_opt: tx_hash_opt.map(|tx_hash_str|H256::from_str(&tx_hash_str[2..]).unwrap_or_else(|_|panic!("Wrong tx hash format: {}", tx_hash_str))), failures: previous_failures }) + fn maybe_construct_tx_info( + tx_hash_opt: Option, + previous_failures: usize, + ) -> Option { + if tx_hash_opt.is_some() || previous_failures > 0 { + Some(CurrentTxInfo { + pending_tx_hash_opt: tx_hash_opt.map(|tx_hash_str| { + H256::from_str(&tx_hash_str[2..]) + .unwrap_or_else(|_| panic!("Wrong tx hash format: {}", tx_hash_str)) + }), + failures: previous_failures, + }) } else { None } @@ -313,7 +330,7 @@ impl PayableDaoReal { fn stm_assembler_of_payable_cq(feeder: AssemblerFeeder) -> String { let stm = format!( - "SELECT + "SELECT p.wallet_address, p.balance_high_b, p.balance_low_b, @@ -374,11 +391,15 @@ impl TableNameDAO for PayableDaoReal { #[cfg(test)] mod tests { use super::*; - use crate::accountant::db_access_objects::failed_payable_dao::{FailedPayableDao, FailedPayableDaoReal, FailedTx, FailureStatus}; + use crate::accountant::db_access_objects::failed_payable_dao::{ + FailedPayableDao, FailedPayableDaoReal, FailedTx, FailureStatus, + }; use crate::accountant::db_access_objects::sent_payable_dao::{ SentPayableDao, SentPayableDaoReal, SentTx, TxStatus, }; - use crate::accountant::db_access_objects::utils::{current_unix_timestamp, from_unix_timestamp, to_unix_timestamp, TxHash}; + use crate::accountant::db_access_objects::utils::{ + current_unix_timestamp, from_unix_timestamp, to_unix_timestamp, TxHash, + }; use crate::accountant::gwei_to_wei; use crate::accountant::test_utils::{ assert_account_creation_fn_fails_on_finding_wrong_columns_and_value_types, make_failed_tx, @@ -395,8 +416,8 @@ mod tests { use masq_lib::messages::CurrentTxInfo; use masq_lib::messages::TopRecordsOrdering::{Age, Balance}; use masq_lib::test_utils::utils::ensure_node_home_directory_exists; + use rusqlite::Connection; use rusqlite::ToSql; - use rusqlite::{Connection}; use std::path::Path; use std::time::Duration; @@ -879,7 +900,7 @@ mod tests { assert_eq!(result, None) } - + fn insert_payable_record_fn( conn: &dyn ConnectionWrapper, wallet: &str, @@ -1044,7 +1065,7 @@ mod tests { } #[test] - fn custom_query_top_records_mode_can_report_tx_info(){ + fn custom_query_top_records_mode_can_report_tx_info() { let now = current_unix_timestamp(); let wallet_addr = "0x1111111111111111111111111111111111111111"; let mut sent_tx_1 = make_sent_tx(789); @@ -1106,8 +1127,12 @@ mod tests { "0x4444444444444444444444444444444444444444", 1_000_000_000, ); - failed_payable_dao_real.insert_new_records(&vec![failed_tx_1, failed_tx_2, failed_tx_3, failed_tx_4]).unwrap(); - sent_payable_dao_real.insert_new_records(&vec![sent_tx_1, sent_tx_2]).unwrap(); + failed_payable_dao_real + .insert_new_records(&vec![failed_tx_1, failed_tx_2, failed_tx_3, failed_tx_4]) + .unwrap(); + sent_payable_dao_real + .insert_new_records(&vec![sent_tx_1, sent_tx_2]) + .unwrap(); payable_dao_real }, ); @@ -1132,7 +1157,10 @@ mod tests { balance_wei: 333_000_000_000, last_paid_timestamp: from_unix_timestamp(now - 80_000), }, - tx_opt: Some(CurrentTxInfo{ pending_tx_hash_opt: None, failures: 2 }) + tx_opt: Some(CurrentTxInfo { + pending_tx_hash_opt: None, + failures: 2 + }) }, PayableAccountWithTxInfo { account: PayableAccount { @@ -1140,7 +1168,10 @@ mod tests { balance_wei: 222_000_000_000, last_paid_timestamp: from_unix_timestamp(now - 80_000), }, - tx_opt: Some(CurrentTxInfo{ pending_tx_hash_opt: Some(sent_tx_hash_1), failures: 0 }) + tx_opt: Some(CurrentTxInfo { + pending_tx_hash_opt: Some(sent_tx_hash_1), + failures: 0 + }) }, PayableAccountWithTxInfo { account: PayableAccount { @@ -1148,14 +1179,17 @@ mod tests { balance_wei: 111_000_000_000, last_paid_timestamp: from_unix_timestamp(now - 80_000), }, - tx_opt: Some(CurrentTxInfo{ pending_tx_hash_opt: Some(sent_tx_hash_2), failures: 1 }) + tx_opt: Some(CurrentTxInfo { + pending_tx_hash_opt: Some(sent_tx_hash_2), + failures: 1 + }) } ] ); } #[test] - fn custom_query_range_mode_can_report_tx_info(){ + fn custom_query_range_mode_can_report_tx_info() { let now = current_unix_timestamp(); let wallet_addr = "0x2222222222222222222222222222222222222222"; let mut sent_tx_1 = make_sent_tx(789); @@ -1181,7 +1215,7 @@ mod tests { failed_tx_4.receiver_address = Wallet::new(wallet_addr).address(); failed_tx_4.nonce = 98; failed_tx_4.status = FailureStatus::Concluded; - let main_test_setup = Box::new( + let main_test_setup = Box::new( move |payable_dao_real: PayableDaoReal, sent_payable_dao_real: SentPayableDaoReal, failed_payable_dao_real: FailedPayableDaoReal| { @@ -1219,8 +1253,12 @@ mod tests { "0x5555555555555555555555555555555555555555", 1_000_000_000, ); - failed_payable_dao_real.insert_new_records(&vec![failed_tx_1, failed_tx_2, failed_tx_3, failed_tx_4]).unwrap(); - sent_payable_dao_real.insert_new_records(&vec![sent_tx_1, sent_tx_2]).unwrap(); + failed_payable_dao_real + .insert_new_records(&vec![failed_tx_1, failed_tx_2, failed_tx_3, failed_tx_4]) + .unwrap(); + sent_payable_dao_real + .insert_new_records(&vec![sent_tx_1, sent_tx_2]) + .unwrap(); payable_dao_real }, ); @@ -1248,7 +1286,10 @@ mod tests { balance_wei: 4_000_000_000, last_paid_timestamp: from_unix_timestamp(now - 80_000), }, - tx_opt: Some(CurrentTxInfo{ pending_tx_hash_opt: Some(sent_tx_hash_1), failures: 2 }) + tx_opt: Some(CurrentTxInfo { + pending_tx_hash_opt: Some(sent_tx_hash_1), + failures: 2 + }) }, PayableAccountWithTxInfo { account: PayableAccount { @@ -1256,7 +1297,10 @@ mod tests { balance_wei: 3_000_000_000, last_paid_timestamp: from_unix_timestamp(now - 80_000), }, - tx_opt: Some(CurrentTxInfo{ pending_tx_hash_opt: None, failures: 1 }) + tx_opt: Some(CurrentTxInfo { + pending_tx_hash_opt: None, + failures: 1 + }) }, PayableAccountWithTxInfo { account: PayableAccount { @@ -1264,7 +1308,10 @@ mod tests { balance_wei: 2_000_000_000, last_paid_timestamp: from_unix_timestamp(now - 80_000), }, - tx_opt: Some(CurrentTxInfo{ pending_tx_hash_opt: Some(sent_tx_hash_2), failures: 0 }) + tx_opt: Some(CurrentTxInfo { + pending_tx_hash_opt: Some(sent_tx_hash_2), + failures: 0 + }) }, PayableAccountWithTxInfo { account: PayableAccount { @@ -1580,35 +1627,54 @@ mod tests { } #[test] - fn maybe_construct_tx_info_with_tx_hash_present_and_no_errors(){ + fn maybe_construct_tx_info_with_tx_hash_present_and_no_errors() { let tx_hash = make_tx_hash(123); let result = PayableDaoReal::maybe_construct_tx_info(Some(format!("{:?}", tx_hash)), 0); - assert_eq!(result, Some(CurrentTxInfo{ pending_tx_hash_opt: Some(tx_hash), failures: 0 })); + assert_eq!( + result, + Some(CurrentTxInfo { + pending_tx_hash_opt: Some(tx_hash), + failures: 0 + }) + ); } #[test] - fn maybe_construct_tx_info_with_tx_hash_present_and_also_errors(){ + fn maybe_construct_tx_info_with_tx_hash_present_and_also_errors() { let tx_hash = make_tx_hash(123); let errors = 3; - let result = PayableDaoReal::maybe_construct_tx_info(Some(format!("{:?}", tx_hash)), errors); + let result = + PayableDaoReal::maybe_construct_tx_info(Some(format!("{:?}", tx_hash)), errors); - assert_eq!(result, Some(CurrentTxInfo{ pending_tx_hash_opt: Some(make_tx_hash(123)), failures: errors })); + assert_eq!( + result, + Some(CurrentTxInfo { + pending_tx_hash_opt: Some(make_tx_hash(123)), + failures: errors + }) + ); } #[test] - fn maybe_construct_tx_info_with_only_errors_present(){ + fn maybe_construct_tx_info_with_only_errors_present() { let errors = 1; let result = PayableDaoReal::maybe_construct_tx_info(None, errors); - assert_eq!(result, Some(CurrentTxInfo{ pending_tx_hash_opt: None, failures: errors })); + assert_eq!( + result, + Some(CurrentTxInfo { + pending_tx_hash_opt: None, + failures: errors + }) + ); } #[test] - fn maybe_construct_tx_info_returns_none(){ + fn maybe_construct_tx_info_returns_none() { let result = PayableDaoReal::maybe_construct_tx_info(None, 0); assert_eq!(result, None); From 2bc1bc3bb5674bd359236ba7360539de03fec01c Mon Sep 17 00:00:00 2001 From: Bert Date: Tue, 23 Sep 2025 13:58:16 +0200 Subject: [PATCH 06/11] GH-662: interim commit --- masq/src/commands/financials_command/mod.rs | 49 +++++++++++-------- .../financials_command/pretty_print_utils.rs | 12 ++++- masq_lib/src/messages.rs | 2 +- node/tests/financials_test.rs | 2 +- 4 files changed, 41 insertions(+), 24 deletions(-) diff --git a/masq/src/commands/financials_command/mod.rs b/masq/src/commands/financials_command/mod.rs index c01b6ca43..ca961bd11 100644 --- a/masq/src/commands/financials_command/mod.rs +++ b/masq/src/commands/financials_command/mod.rs @@ -323,10 +323,7 @@ mod tests { use crate::commands::financials_command::test_utils::transpose_inputs_to_nested_tuples; use crate::test_utils::mocks::CommandContextMock; use atty::Stream; - use masq_lib::messages::{ - ToMessageBody, TopRecordsOrdering, UiFinancialStatistics, UiFinancialsResponse, - UiPayableAccount, UiReceivableAccount, - }; + use masq_lib::messages::{CurrentTxInfo, ToMessageBody, TopRecordsOrdering, UiFinancialStatistics, UiFinancialsResponse, UiPayableAccount, UiReceivableAccount}; use masq_lib::ui_gateway::MessageBody; use masq_lib::utils::slice_of_strs_to_vec_of_strings; use regex::Regex; @@ -1028,15 +1025,27 @@ mod tests { wallet: "0xA884A2F1A5Ec6C2e499644666a5E6af97B966888".to_string(), age_s: 5645405400, balance_gwei: 68843325667, - pending_payable_hash_opt: None, + current_tx_info_opt: None, }, UiPayableAccount { wallet: "0x6DbcCaC5596b7ac986ff8F7ca06F212aEB444440".to_string(), age_s: 150000, - balance_gwei: 8, - pending_payable_hash_opt: Some( - "0x0290db1d56121112f4d45c1c3f36348644f6afd20b759b762f1dba9c4949066e" - .to_string(), + balance_gwei: 999888777, + current_tx_info_opt: Some(CurrentTxInfo{pending_tx_hash_opt: Some("0x0290db1d56121112f4d45c1c3f36348644f6afd20b759b762f1dba9c4949066e".to_string()),failures: 0} + ), + }, + UiPayableAccount { + wallet: "0x563cCaC5596b7ac986ff8F7ca056789a122c3230".to_string(), + age_s: 12055, + balance_gwei: 33444555, + current_tx_info_opt: Some(CurrentTxInfo{pending_tx_hash_opt: None,failures: 3} + ), + }, + UiPayableAccount { + wallet: "0xeF456a11A5Ec6C2e499655787a5E6af97c961123".to_string(), + age_s: 161514, + balance_gwei: 1111, + current_tx_info_opt: Some(CurrentTxInfo{pending_tx_hash_opt: Some("0xb45a663d56121112f4d45c1c3a245be32eAA6afd20b759b762f1dba945ec2f41".to_string()),failures: 1} ), }, ]), @@ -1059,9 +1068,9 @@ mod tests { wallet: "0x6DbcCaC5596b7ac986ff8F7ca06F212aEB444440".to_string(), age_s: 150000, balance_gwei: 8, - pending_payable_hash_opt: Some( + current_tx_info_opt: Some(CurrentTxInfo{pending_tx_hash_opt: Some( "0x0290db1d56121112f4d45c1c3f36348644f6afd20b759b762f1dba9c4949066e" - .to_string(), + .to_string()),failures: 0} ), }]), receivable_opt: None, @@ -1352,9 +1361,9 @@ mod tests { wallet: "0x6DbcCaC5596b7ac986ff8F7ca06F212aEB444440".to_string(), age_s: 150000, balance_gwei: 1200000000000, - pending_payable_hash_opt: Some( + current_tx_info_opt: Some(CurrentTxInfo{pending_tx_hash_opt: Some( "0x0290db1d56121112f4d45c1c3f36348644f6afd20b759b762f1dba9c4949066e" - .to_string(), + .to_string()),failures: 4} ), }]), receivable_opt: Some(vec![UiReceivableAccount { @@ -1603,16 +1612,16 @@ mod tests { wallet: "0xA884A2F1A5Ec6C2e499644666a5E6af97B966888".to_string(), age_s: 5405400, balance_gwei: 644000000, - pending_payable_hash_opt: Some( + current_tx_info_opt: Some(CurrentTxInfo{pending_tx_hash_opt: Some( "0x3648c8b8c7e067ac30b80b6936159326d564dd13b7ae465b26647154ada2c638" - .to_string(), + .to_string()), failures: 0} ), }, UiPayableAccount { wallet: "0xEA674fdac714fd979de3EdF0F56AA9716B198ec8".to_string(), age_s: 28120444, balance_gwei: 97524120, - pending_payable_hash_opt: None, + current_tx_info_opt: None, }, ]), receivable_opt: Some(vec![ @@ -1689,22 +1698,22 @@ mod tests { wallet: "0x6e250504DdfFDb986C4F0bb8Df162503B4118b05".to_string(), age_s: 4445, balance_gwei: 3862654858938090, - pending_payable_hash_opt: Some( + current_tx_info_opt: Some( CurrentTxInfo{pending_tx_hash_opt: Some( "0x5fe272ed1e941cc05fbd624ec4b1546cd03c25d53e24ba2c18b11feb83cd4581" - .to_string(), + .to_string()),failures: 0} ), }, UiPayableAccount { wallet: "0xA884A2F1A5Ec6C2e499644666a5E6af97B966888".to_string(), age_s: 70000, balance_gwei: 708090, - pending_payable_hash_opt: None, + current_tx_info_opt: Some(CurrentTxInfo{pending_tx_hash_opt: None, failures: 3}), }, UiPayableAccount { wallet: "0x6DbcCaC5596b7ac986ff8F7ca06F212aEB444440".to_string(), age_s: 6089909, balance_gwei: 66658, - pending_payable_hash_opt: None, + current_tx_info_opt: None, }, ]), receivable_opt: None, diff --git a/masq/src/commands/financials_command/pretty_print_utils.rs b/masq/src/commands/financials_command/pretty_print_utils.rs index 2adbfbad4..805380d37 100644 --- a/masq/src/commands/financials_command/pretty_print_utils.rs +++ b/masq/src/commands/financials_command/pretty_print_utils.rs @@ -27,8 +27,16 @@ pub(in crate::commands::financials_command) mod restricted { self.wallet.to_string(), self.age_s.separate_with_commas(), process_gwei_into_requested_format(self.balance_gwei, is_gwei), - if let Some(hash) = &self.pending_payable_hash_opt { - hash.to_string() + if let Some(current_tx_info) = &self.current_tx_info_opt { + if let Some(hash) = current_tx_info.pending_tx_hash_opt.as_ref() { + if current_tx_info.failures == 0 { + hash.clone() + } else { + format!("{} – {} failed attempts so far", hash, current_tx_info.failures) + } + } else { + format!("In processing – {} failed attempts so far", current_tx_info.failures) + } } else { "None".to_string() }, diff --git a/masq_lib/src/messages.rs b/masq_lib/src/messages.rs index e778446ba..b4c324765 100644 --- a/masq_lib/src/messages.rs +++ b/masq_lib/src/messages.rs @@ -687,7 +687,7 @@ pub struct UiPayableAccount { #[derive(Serialize, Deserialize, Debug, PartialEq, Eq)] pub struct CurrentTxInfo { #[serde(rename = "pendingTxHashOpt")] - pub pending_tx_hash_opt: Option, + pub pending_tx_hash_opt: Option, pub failures: usize, } diff --git a/node/tests/financials_test.rs b/node/tests/financials_test.rs index 7aff319d2..45d982c82 100644 --- a/node/tests/financials_test.rs +++ b/node/tests/financials_test.rs @@ -100,7 +100,7 @@ fn financials_command_retrieves_payable_and_receivable_records_integration() { let receivable = query_results.receivable_opt.unwrap(); assert_eq!(payable[0].wallet, wallet_payable.to_string()); assert_eq!(payable[0].balance_gwei, 45678357_u64); - assert_eq!(payable[0].pending_payable_hash_opt, None); + assert_eq!(payable[0].current_tx_info_opt, None); let act_period = after.duration_since(before).unwrap().as_secs(); let age_payable = payable[0].age_s; assert!(age_payable >= 678 && age_payable <= (age_payable + act_period)); From 40f8383e754309cb9015d2d2e4f4166849bf7d47 Mon Sep 17 00:00:00 2001 From: Bert Date: Tue, 23 Sep 2025 14:35:02 +0200 Subject: [PATCH 07/11] GH-662: CLI fixed --- masq/src/commands/financials_command/mod.rs | 50 +++++++------ .../financials_command/pretty_print_utils.rs | 71 +++++++++++++++---- masq_lib/src/messages.rs | 1 - 3 files changed, 87 insertions(+), 35 deletions(-) diff --git a/masq/src/commands/financials_command/mod.rs b/masq/src/commands/financials_command/mod.rs index ca961bd11..67483bd9d 100644 --- a/masq/src/commands/financials_command/mod.rs +++ b/masq/src/commands/financials_command/mod.rs @@ -323,7 +323,10 @@ mod tests { use crate::commands::financials_command::test_utils::transpose_inputs_to_nested_tuples; use crate::test_utils::mocks::CommandContextMock; use atty::Stream; - use masq_lib::messages::{CurrentTxInfo, ToMessageBody, TopRecordsOrdering, UiFinancialStatistics, UiFinancialsResponse, UiPayableAccount, UiReceivableAccount}; + use masq_lib::messages::{ + CurrentTxInfo, ToMessageBody, TopRecordsOrdering, UiFinancialStatistics, + UiFinancialsResponse, UiPayableAccount, UiReceivableAccount, + }; use masq_lib::ui_gateway::MessageBody; use masq_lib::utils::slice_of_strs_to_vec_of_strings; use regex::Regex; @@ -1126,9 +1129,11 @@ mod tests { \n\ Payable\n\ \n\ - # Wallet Age [s] Balance [MASQ] Pending tx \n\ - 1 0xA884A2F1A5Ec6C2e499644666a5E6af97B966888 5,645,405,400 68.84 None \n\ - 2 0x6DbcCaC5596b7ac986ff8F7ca06F212aEB444440 150,000 < 0.01 0x0290db1d56121112f4d45c1c3f36348644f6afd20b759b762f1dba9c4949066e\n\ + # Wallet Age [s] Balance [MASQ] Pending tx \n\ + 1 0xA884A2F1A5Ec6C2e499644666a5E6af97B966888 5,645,405,400 68.84 None \n\ + 2 0x6DbcCaC5596b7ac986ff8F7ca06F212aEB444440 150,000 0.99 0x0290db1d56121112f4d45c1c3f36348644f6afd20b759b762f1dba9c4949066e \n\ + 3 0x563cCaC5596b7ac986ff8F7ca056789a122c3230 12,055 0.03 Processing... 3 failed attempts \n\ + 4 0xeF456a11A5Ec6C2e499655787a5E6af97c961123 161,514 < 0.01 0xb45a663d56121112f4d45c1c3a245be32eAA6afd20b759b762f1dba945ec2f41 (1 failed attempt)\n\ \n\ \n\ \n\ @@ -1262,9 +1267,11 @@ mod tests { \n\ Payable\n\ \n\ - # Wallet Age [s] Balance [gwei] Pending tx \n\ - 1 0xA884A2F1A5Ec6C2e499644666a5E6af97B966888 5,645,405,400 68,843,325,667 None \n\ - 2 0x6DbcCaC5596b7ac986ff8F7ca06F212aEB444440 150,000 8 0x0290db1d56121112f4d45c1c3f36348644f6afd20b759b762f1dba9c4949066e\n\ + # Wallet Age [s] Balance [gwei] Pending tx \n\ + 1 0xA884A2F1A5Ec6C2e499644666a5E6af97B966888 5,645,405,400 68,843,325,667 None \n\ + 2 0x6DbcCaC5596b7ac986ff8F7ca06F212aEB444440 150,000 999,888,777 0x0290db1d56121112f4d45c1c3f36348644f6afd20b759b762f1dba9c4949066e \n\ + 3 0x563cCaC5596b7ac986ff8F7ca056789a122c3230 12,055 33,444,555 Processing... 3 failed attempts \n\ + 4 0xeF456a11A5Ec6C2e499655787a5E6af97c961123 161,514 1,111 0xb45a663d56121112f4d45c1c3a245be32eAA6afd20b759b762f1dba945ec2f41 (1 failed attempt)\n\ \n\ \n\ \n\ @@ -1354,10 +1361,11 @@ mod tests { #[test] fn custom_query_balance_range_can_be_shorthanded() { let transact_params_arc = Arc::new(Mutex::new(vec![])); - let expected_response = UiFinancialsResponse { - stats_opt: None, - query_results_opt: Some(QueryResults { - payable_opt: Some(vec![UiPayableAccount { + let expected_response = + UiFinancialsResponse { + stats_opt: None, + query_results_opt: Some(QueryResults { + payable_opt: Some(vec![UiPayableAccount { wallet: "0x6DbcCaC5596b7ac986ff8F7ca06F212aEB444440".to_string(), age_s: 150000, balance_gwei: 1200000000000, @@ -1366,13 +1374,13 @@ mod tests { .to_string()),failures: 4} ), }]), - receivable_opt: Some(vec![UiReceivableAccount { - wallet: "0x8bA50675e590b545D2128905b89039256Eaa24F6".to_string(), - age_s: 45700, - balance_gwei: 5050330000, - }]), - }), - }; + receivable_opt: Some(vec![UiReceivableAccount { + wallet: "0x8bA50675e590b545D2128905b89039256Eaa24F6".to_string(), + age_s: 45700, + balance_gwei: 5050330000, + }]), + }), + }; let args = slice_of_strs_to_vec_of_strings(&[ "financials", "--payable", @@ -1420,8 +1428,8 @@ mod tests { "\n\ Specific payable query: 0-350000 sec 5-UNLIMITED MASQ\n\ \n\ - # Wallet Age [s] Balance [MASQ] Pending tx \n\ - 1 0x6DbcCaC5596b7ac986ff8F7ca06F212aEB444440 150,000 1,200.00 0x0290db1d56121112f4d45c1c3f36348644f6afd20b759b762f1dba9c4949066e\n\ + # Wallet Age [s] Balance [MASQ] Pending tx \n\ + 1 0x6DbcCaC5596b7ac986ff8F7ca06F212aEB444440 150,000 1,200.00 0x0290db1d56121112f4d45c1c3f36348644f6afd20b759b762f1dba9c4949066e (4 failed attempts)\n\ \n\ \n\ \n\ @@ -1763,7 +1771,7 @@ mod tests { \n\ # Wallet Age [s] Balance [MASQ] Pending tx \n\ 1 0x6e250504DdfFDb986C4F0bb8Df162503B4118b05 4,445 3,862,654.85 0x5fe272ed1e941cc05fbd624ec4b1546cd03c25d53e24ba2c18b11feb83cd4581\n\ - 2 0xA884A2F1A5Ec6C2e499644666a5E6af97B966888 70,000 < 0.01 None \n\ + 2 0xA884A2F1A5Ec6C2e499644666a5E6af97B966888 70,000 < 0.01 Processing... 3 failed attempts \n\ 3 0x6DbcCaC5596b7ac986ff8F7ca06F212aEB444440 6,089,909 < 0.01 None \n" ); assert_eq!(stderr_arc.lock().unwrap().get_string(), String::new()); diff --git a/masq/src/commands/financials_command/pretty_print_utils.rs b/masq/src/commands/financials_command/pretty_print_utils.rs index 805380d37..5022756f5 100644 --- a/masq/src/commands/financials_command/pretty_print_utils.rs +++ b/masq/src/commands/financials_command/pretty_print_utils.rs @@ -12,7 +12,7 @@ pub(in crate::commands::financials_command) mod restricted { use masq_lib::messages::{UiPayableAccount, UiReceivableAccount}; use masq_lib::short_writeln; use masq_lib::utils::to_string; - use std::fmt::{Debug, Display}; + use std::fmt::{Debug, Display, Formatter}; use std::io::Write; use thousands::Separable; @@ -27,18 +27,27 @@ pub(in crate::commands::financials_command) mod restricted { self.wallet.to_string(), self.age_s.separate_with_commas(), process_gwei_into_requested_format(self.balance_gwei, is_gwei), - if let Some(current_tx_info) = &self.current_tx_info_opt { - if let Some(hash) = current_tx_info.pending_tx_hash_opt.as_ref() { - if current_tx_info.failures == 0 { - hash.clone() - } else { - format!("{} – {} failed attempts so far", hash, current_tx_info.failures) + match &self.current_tx_info_opt { + Some(current_tx_info) => match ¤t_tx_info.pending_tx_hash_opt { + Some(hash) => { + if current_tx_info.failures == 0 { + hash.clone() + } else { + format!( + "{} ({})", + hash, + AttemptsConjugator::new(current_tx_info.failures) + ) + } } - } else { - format!("In processing – {} failed attempts so far", current_tx_info.failures) - } - } else { - "None".to_string() + None => { + format!( + "Processing... {}", + AttemptsConjugator::new(current_tx_info.failures) + ) + } + }, + None => "None".to_string(), }, ] } @@ -55,6 +64,26 @@ pub(in crate::commands::financials_command) mod restricted { } } + pub(super) struct AttemptsConjugator { + failures: usize, + } + + impl AttemptsConjugator { + pub fn new(failures: usize) -> Self { + Self { failures } + } + } + + impl Display for AttemptsConjugator { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + if self.failures == 1 { + write!(f, "1 failed attempt") + } else { + write!(f, "{} failed attempts", self.failures) + } + } + } + pub fn financial_status_totals_title(stdout: &mut dyn Write, is_gwei: bool) { short_writeln!( stdout, @@ -258,7 +287,7 @@ pub(in crate::commands::financials_command) mod restricted { #[cfg(test)] mod tests { use crate::commands::financials_command::pretty_print_utils::restricted::{ - figure_out_max_widths, StringValuesFormattableAccount, + figure_out_max_widths, AttemptsConjugator, StringValuesFormattableAccount, }; #[derive(Clone)] @@ -319,4 +348,20 @@ mod tests { //the second number is always 42 as the length of wallet address assert_eq!(result, vec![3, 42, 5, 10]) } + + #[test] + fn failures_conjugator_works_for_singular_failure() { + let subject = AttemptsConjugator::new(1); + assert_eq!(subject.to_string(), "1 failed attempt") + } + + #[test] + fn failures_conjugator_works_for_plural_failure() { + let failures = vec![2, 5, 10]; + + failures.iter().for_each(|failure| { + let subject = AttemptsConjugator::new(*failure); + assert_eq!(subject.to_string(), format!("{} failed attempts", failure)) + }) + } } diff --git a/masq_lib/src/messages.rs b/masq_lib/src/messages.rs index b4c324765..317edf590 100644 --- a/masq_lib/src/messages.rs +++ b/masq_lib/src/messages.rs @@ -7,7 +7,6 @@ use crate::ui_gateway::MessagePath::{Conversation, FireAndForget}; use crate::utils::to_string; use core::fmt::Display; use core::fmt::Formatter; -use ethereum_types::H256; use itertools::Itertools; use serde::de::DeserializeOwned; use serde_derive::{Deserialize, Serialize}; From 3b624ce5e11f523d2c8b34428738a18041156c87 Mon Sep 17 00:00:00 2001 From: Bert Date: Tue, 23 Sep 2025 14:54:24 +0200 Subject: [PATCH 08/11] GH-662: complete --- .../db_access_objects/payable_dao.rs | 37 ++++++++----------- 1 file changed, 15 insertions(+), 22 deletions(-) diff --git a/node/src/accountant/db_access_objects/payable_dao.rs b/node/src/accountant/db_access_objects/payable_dao.rs index 2e4a1f92d..90679ad2c 100644 --- a/node/src/accountant/db_access_objects/payable_dao.rs +++ b/node/src/accountant/db_access_objects/payable_dao.rs @@ -24,9 +24,7 @@ use masq_lib::utils::ExpectValue; use rusqlite::OptionalExtension; use rusqlite::{Error, Row}; use std::fmt::Debug; -use std::str::FromStr; use std::time::SystemTime; -use web3::types::H256; #[derive(Debug, PartialEq, Eq)] pub enum PayableDaoError { @@ -312,15 +310,12 @@ impl PayableDaoReal { } fn maybe_construct_tx_info( - tx_hash_opt: Option, + pending_tx_hash_opt: Option, previous_failures: usize, ) -> Option { - if tx_hash_opt.is_some() || previous_failures > 0 { + if pending_tx_hash_opt.is_some() || previous_failures > 0 { Some(CurrentTxInfo { - pending_tx_hash_opt: tx_hash_opt.map(|tx_hash_str| { - H256::from_str(&tx_hash_str[2..]) - .unwrap_or_else(|_| panic!("Wrong tx hash format: {}", tx_hash_str)) - }), + pending_tx_hash_opt, failures: previous_failures, }) } else { @@ -329,7 +324,7 @@ impl PayableDaoReal { } fn stm_assembler_of_payable_cq(feeder: AssemblerFeeder) -> String { - let stm = format!( + format!( "SELECT p.wallet_address, p.balance_high_b, @@ -340,8 +335,8 @@ impl PayableDaoReal { ELSE NULL END AS pending_tx_hash, /* - The following case stm counts the failing attempts for a tx processing that hasn't - ended yet and is ongoing. + The following case stm counts up the failing attempts in the tx processing while it + is still ongoing. */ CASE WHEN EXISTS ( SELECT 1 @@ -376,9 +371,7 @@ impl PayableDaoReal { feeder.order_by_first_param, feeder.order_by_second_param, feeder.limit_clause - ); - eprintln!("{}", stm); - stm + ) } } @@ -392,7 +385,7 @@ impl TableNameDAO for PayableDaoReal { mod tests { use super::*; use crate::accountant::db_access_objects::failed_payable_dao::{ - FailedPayableDao, FailedPayableDaoReal, FailedTx, FailureStatus, + FailedPayableDao, FailedPayableDaoReal, FailureStatus, }; use crate::accountant::db_access_objects::sent_payable_dao::{ SentPayableDao, SentPayableDaoReal, SentTx, TxStatus, @@ -1169,7 +1162,7 @@ mod tests { last_paid_timestamp: from_unix_timestamp(now - 80_000), }, tx_opt: Some(CurrentTxInfo { - pending_tx_hash_opt: Some(sent_tx_hash_1), + pending_tx_hash_opt: Some(format!("{:?}", sent_tx_hash_1)), failures: 0 }) }, @@ -1180,7 +1173,7 @@ mod tests { last_paid_timestamp: from_unix_timestamp(now - 80_000), }, tx_opt: Some(CurrentTxInfo { - pending_tx_hash_opt: Some(sent_tx_hash_2), + pending_tx_hash_opt: Some(format!("{:?}", sent_tx_hash_2)), failures: 1 }) } @@ -1287,7 +1280,7 @@ mod tests { last_paid_timestamp: from_unix_timestamp(now - 80_000), }, tx_opt: Some(CurrentTxInfo { - pending_tx_hash_opt: Some(sent_tx_hash_1), + pending_tx_hash_opt: Some(format!("{:?}", sent_tx_hash_1)), failures: 2 }) }, @@ -1309,7 +1302,7 @@ mod tests { last_paid_timestamp: from_unix_timestamp(now - 80_000), }, tx_opt: Some(CurrentTxInfo { - pending_tx_hash_opt: Some(sent_tx_hash_2), + pending_tx_hash_opt: Some(format!("{:?}", sent_tx_hash_2)), failures: 0 }) }, @@ -1445,7 +1438,7 @@ mod tests { last_paid_timestamp: from_unix_timestamp(now - 100_401), }, tx_opt: Some(CurrentTxInfo { - pending_tx_hash_opt: Some(make_tx_hash(0xABC)), + pending_tx_hash_opt: Some(format!("{:?}", make_tx_hash(0xABC))), failures: 0 }) }, @@ -1635,7 +1628,7 @@ mod tests { assert_eq!( result, Some(CurrentTxInfo { - pending_tx_hash_opt: Some(tx_hash), + pending_tx_hash_opt: Some(format!("{:?}", tx_hash)), failures: 0 }) ); @@ -1652,7 +1645,7 @@ mod tests { assert_eq!( result, Some(CurrentTxInfo { - pending_tx_hash_opt: Some(make_tx_hash(123)), + pending_tx_hash_opt: Some(format!("{:?}", make_tx_hash(123))), failures: errors }) ); From 3953e5073c92079d15b9b6621ff74d5b06670997 Mon Sep 17 00:00:00 2001 From: Bert Date: Wed, 24 Sep 2025 12:57:20 +0200 Subject: [PATCH 09/11] GH-662: renaming; plus, updated the UI-Node documentation --- USER-INTERFACE-INTERFACE.md | 27 ++++++++++++++--- masq/src/commands/financials_command/mod.rs | 24 +++++++-------- .../financials_command/pretty_print_utils.rs | 2 +- masq_lib/src/messages.rs | 6 ++-- .../db_access_objects/payable_dao.rs | 30 +++++++++---------- .../src/accountant/db_access_objects/utils.rs | 6 ++-- node/src/accountant/mod.rs | 10 +++---- node/tests/financials_test.rs | 2 +- 8 files changed, 63 insertions(+), 44 deletions(-) diff --git a/USER-INTERFACE-INTERFACE.md b/USER-INTERFACE-INTERFACE.md index 3a5cacffa..1112ad7a5 100644 --- a/USER-INTERFACE-INTERFACE.md +++ b/USER-INTERFACE-INTERFACE.md @@ -769,7 +769,10 @@ searching over; this is the upper limit for the debt's age, or how long it has b "wallet": , "ageS": , "balanceGwei": , - "pendingPayableHashOpt": + "txProcessingInfoOpt": { + "pendingTxHashOpt": , + "failures": + } }, [...] ], @@ -826,9 +829,25 @@ payment was later also confirmed on the blockchain. `balanceGwei` is a number of gwei we owe to this particular Node. -`pendingPayableHashOpt` is present only sporadically. When it is, it denotes that we've recently sent a payment to the -blockchain, but our confirmation detector has not yet determined that the payment has been confirmed. The value is -either null or stores a transaction hash of the pending transaction. +`txProcessingInfoOpt` is present only sporadically. When it is, it denotes that we've recently sent a tx to +the blockchain, but the payment hasn't been confirmed yet. The value is either null, which means that no tx has been +recently issued for the respective account, or stores an object representing an already begun payment operation; +this object has two fields. + +The first, `pendingTxHashOpt`, if present, contains a transaction hash of an alive, pending transaction that is still +waiting for resolution. + +The second, `failures`, is a positive number of times we've tried to send this same tx to the blockchain in this latest +payment, but it has failed repeatedly. The tx always bears the same nonce and is bound for the same wallet. +There are three possible observable states: + +1. `pendingTxHashOpt` is not null and `failures` is 0. This means that we've yet made only a single attempt to send +a tx to the blockchain, and the tx is pending. +2. `pendingTxHashOpt` is not null and `failures` is greater than 0. This means that we've made more than one attempt. +There is an alive pending tx, plus we've experienced X failures before that. +3. `pendingTxHashOpt` is null and `failures` is greater than 0. This means that the payment mechanism is in a mid-state +preparing a submission of another payment attempt, but the blockchain hasn't been updated about this new tx yet. +At the same time, there have already been preceding failed tx attempts. `receivable` is the field devoted to receivable records if any exist. diff --git a/masq/src/commands/financials_command/mod.rs b/masq/src/commands/financials_command/mod.rs index 67483bd9d..b252a4091 100644 --- a/masq/src/commands/financials_command/mod.rs +++ b/masq/src/commands/financials_command/mod.rs @@ -324,7 +324,7 @@ mod tests { use crate::test_utils::mocks::CommandContextMock; use atty::Stream; use masq_lib::messages::{ - CurrentTxInfo, ToMessageBody, TopRecordsOrdering, UiFinancialStatistics, + TxProcessingInfo, ToMessageBody, TopRecordsOrdering, UiFinancialStatistics, UiFinancialsResponse, UiPayableAccount, UiReceivableAccount, }; use masq_lib::ui_gateway::MessageBody; @@ -1028,27 +1028,27 @@ mod tests { wallet: "0xA884A2F1A5Ec6C2e499644666a5E6af97B966888".to_string(), age_s: 5645405400, balance_gwei: 68843325667, - current_tx_info_opt: None, + tx_processing_info_opt: None, }, UiPayableAccount { wallet: "0x6DbcCaC5596b7ac986ff8F7ca06F212aEB444440".to_string(), age_s: 150000, balance_gwei: 999888777, - current_tx_info_opt: Some(CurrentTxInfo{pending_tx_hash_opt: Some("0x0290db1d56121112f4d45c1c3f36348644f6afd20b759b762f1dba9c4949066e".to_string()),failures: 0} + tx_processing_info_opt: Some(TxProcessingInfo{pending_tx_hash_opt: Some("0x0290db1d56121112f4d45c1c3f36348644f6afd20b759b762f1dba9c4949066e".to_string()),failures: 0} ), }, UiPayableAccount { wallet: "0x563cCaC5596b7ac986ff8F7ca056789a122c3230".to_string(), age_s: 12055, balance_gwei: 33444555, - current_tx_info_opt: Some(CurrentTxInfo{pending_tx_hash_opt: None,failures: 3} + tx_processing_info_opt: Some(TxProcessingInfo{pending_tx_hash_opt: None,failures: 3} ), }, UiPayableAccount { wallet: "0xeF456a11A5Ec6C2e499655787a5E6af97c961123".to_string(), age_s: 161514, balance_gwei: 1111, - current_tx_info_opt: Some(CurrentTxInfo{pending_tx_hash_opt: Some("0xb45a663d56121112f4d45c1c3a245be32eAA6afd20b759b762f1dba945ec2f41".to_string()),failures: 1} + tx_processing_info_opt: Some(TxProcessingInfo{pending_tx_hash_opt: Some("0xb45a663d56121112f4d45c1c3a245be32eAA6afd20b759b762f1dba945ec2f41".to_string()),failures: 1} ), }, ]), @@ -1071,7 +1071,7 @@ mod tests { wallet: "0x6DbcCaC5596b7ac986ff8F7ca06F212aEB444440".to_string(), age_s: 150000, balance_gwei: 8, - current_tx_info_opt: Some(CurrentTxInfo{pending_tx_hash_opt: Some( + tx_processing_info_opt: Some(TxProcessingInfo{pending_tx_hash_opt: Some( "0x0290db1d56121112f4d45c1c3f36348644f6afd20b759b762f1dba9c4949066e" .to_string()),failures: 0} ), @@ -1369,7 +1369,7 @@ mod tests { wallet: "0x6DbcCaC5596b7ac986ff8F7ca06F212aEB444440".to_string(), age_s: 150000, balance_gwei: 1200000000000, - current_tx_info_opt: Some(CurrentTxInfo{pending_tx_hash_opt: Some( + tx_processing_info_opt: Some(TxProcessingInfo{pending_tx_hash_opt: Some( "0x0290db1d56121112f4d45c1c3f36348644f6afd20b759b762f1dba9c4949066e" .to_string()),failures: 4} ), @@ -1620,7 +1620,7 @@ mod tests { wallet: "0xA884A2F1A5Ec6C2e499644666a5E6af97B966888".to_string(), age_s: 5405400, balance_gwei: 644000000, - current_tx_info_opt: Some(CurrentTxInfo{pending_tx_hash_opt: Some( + tx_processing_info_opt: Some(TxProcessingInfo{pending_tx_hash_opt: Some( "0x3648c8b8c7e067ac30b80b6936159326d564dd13b7ae465b26647154ada2c638" .to_string()), failures: 0} ), @@ -1629,7 +1629,7 @@ mod tests { wallet: "0xEA674fdac714fd979de3EdF0F56AA9716B198ec8".to_string(), age_s: 28120444, balance_gwei: 97524120, - current_tx_info_opt: None, + tx_processing_info_opt: None, }, ]), receivable_opt: Some(vec![ @@ -1706,7 +1706,7 @@ mod tests { wallet: "0x6e250504DdfFDb986C4F0bb8Df162503B4118b05".to_string(), age_s: 4445, balance_gwei: 3862654858938090, - current_tx_info_opt: Some( CurrentTxInfo{pending_tx_hash_opt: Some( + tx_processing_info_opt: Some( TxProcessingInfo{pending_tx_hash_opt: Some( "0x5fe272ed1e941cc05fbd624ec4b1546cd03c25d53e24ba2c18b11feb83cd4581" .to_string()),failures: 0} ), @@ -1715,13 +1715,13 @@ mod tests { wallet: "0xA884A2F1A5Ec6C2e499644666a5E6af97B966888".to_string(), age_s: 70000, balance_gwei: 708090, - current_tx_info_opt: Some(CurrentTxInfo{pending_tx_hash_opt: None, failures: 3}), + tx_processing_info_opt: Some(TxProcessingInfo{pending_tx_hash_opt: None, failures: 3}), }, UiPayableAccount { wallet: "0x6DbcCaC5596b7ac986ff8F7ca06F212aEB444440".to_string(), age_s: 6089909, balance_gwei: 66658, - current_tx_info_opt: None, + tx_processing_info_opt: None, }, ]), receivable_opt: None, diff --git a/masq/src/commands/financials_command/pretty_print_utils.rs b/masq/src/commands/financials_command/pretty_print_utils.rs index 5022756f5..1dbea4991 100644 --- a/masq/src/commands/financials_command/pretty_print_utils.rs +++ b/masq/src/commands/financials_command/pretty_print_utils.rs @@ -27,7 +27,7 @@ pub(in crate::commands::financials_command) mod restricted { self.wallet.to_string(), self.age_s.separate_with_commas(), process_gwei_into_requested_format(self.balance_gwei, is_gwei), - match &self.current_tx_info_opt { + match &self.tx_processing_info_opt { Some(current_tx_info) => match ¤t_tx_info.pending_tx_hash_opt { Some(hash) => { if current_tx_info.failures == 0 { diff --git a/masq_lib/src/messages.rs b/masq_lib/src/messages.rs index 317edf590..495bd1254 100644 --- a/masq_lib/src/messages.rs +++ b/masq_lib/src/messages.rs @@ -679,12 +679,12 @@ pub struct UiPayableAccount { pub age_s: u64, #[serde(rename = "balanceGwei")] pub balance_gwei: u64, - #[serde(rename = "currentTxInfoOpt")] - pub current_tx_info_opt: Option, + #[serde(rename = "txProcessingInfoOpt")] + pub tx_processing_info_opt: Option, } #[derive(Serialize, Deserialize, Debug, PartialEq, Eq)] -pub struct CurrentTxInfo { +pub struct TxProcessingInfo { #[serde(rename = "pendingTxHashOpt")] pub pending_tx_hash_opt: Option, pub failures: usize, diff --git a/node/src/accountant/db_access_objects/payable_dao.rs b/node/src/accountant/db_access_objects/payable_dao.rs index 90679ad2c..2a18e6d44 100644 --- a/node/src/accountant/db_access_objects/payable_dao.rs +++ b/node/src/accountant/db_access_objects/payable_dao.rs @@ -18,7 +18,7 @@ use crate::sub_lib::wallet::Wallet; use ethabi::Address; #[cfg(test)] use itertools::Either; -use masq_lib::messages::CurrentTxInfo; +use masq_lib::messages::TxProcessingInfo; use masq_lib::utils::ExpectValue; #[cfg(test)] use rusqlite::OptionalExtension; @@ -312,9 +312,9 @@ impl PayableDaoReal { fn maybe_construct_tx_info( pending_tx_hash_opt: Option, previous_failures: usize, - ) -> Option { + ) -> Option { if pending_tx_hash_opt.is_some() || previous_failures > 0 { - Some(CurrentTxInfo { + Some(TxProcessingInfo { pending_tx_hash_opt, failures: previous_failures, }) @@ -406,7 +406,7 @@ mod tests { use crate::database::rusqlite_wrappers::ConnectionWrapperReal; use crate::test_utils::make_wallet; use itertools::Itertools; - use masq_lib::messages::CurrentTxInfo; + use masq_lib::messages::TxProcessingInfo; use masq_lib::messages::TopRecordsOrdering::{Age, Balance}; use masq_lib::test_utils::utils::ensure_node_home_directory_exists; use rusqlite::Connection; @@ -1150,7 +1150,7 @@ mod tests { balance_wei: 333_000_000_000, last_paid_timestamp: from_unix_timestamp(now - 80_000), }, - tx_opt: Some(CurrentTxInfo { + tx_opt: Some(TxProcessingInfo { pending_tx_hash_opt: None, failures: 2 }) @@ -1161,7 +1161,7 @@ mod tests { balance_wei: 222_000_000_000, last_paid_timestamp: from_unix_timestamp(now - 80_000), }, - tx_opt: Some(CurrentTxInfo { + tx_opt: Some(TxProcessingInfo { pending_tx_hash_opt: Some(format!("{:?}", sent_tx_hash_1)), failures: 0 }) @@ -1172,7 +1172,7 @@ mod tests { balance_wei: 111_000_000_000, last_paid_timestamp: from_unix_timestamp(now - 80_000), }, - tx_opt: Some(CurrentTxInfo { + tx_opt: Some(TxProcessingInfo { pending_tx_hash_opt: Some(format!("{:?}", sent_tx_hash_2)), failures: 1 }) @@ -1279,7 +1279,7 @@ mod tests { balance_wei: 4_000_000_000, last_paid_timestamp: from_unix_timestamp(now - 80_000), }, - tx_opt: Some(CurrentTxInfo { + tx_opt: Some(TxProcessingInfo { pending_tx_hash_opt: Some(format!("{:?}", sent_tx_hash_1)), failures: 2 }) @@ -1290,7 +1290,7 @@ mod tests { balance_wei: 3_000_000_000, last_paid_timestamp: from_unix_timestamp(now - 80_000), }, - tx_opt: Some(CurrentTxInfo { + tx_opt: Some(TxProcessingInfo { pending_tx_hash_opt: None, failures: 1 }) @@ -1301,7 +1301,7 @@ mod tests { balance_wei: 2_000_000_000, last_paid_timestamp: from_unix_timestamp(now - 80_000), }, - tx_opt: Some(CurrentTxInfo { + tx_opt: Some(TxProcessingInfo { pending_tx_hash_opt: Some(format!("{:?}", sent_tx_hash_2)), failures: 0 }) @@ -1312,7 +1312,7 @@ mod tests { balance_wei: 1_000_000_000, last_paid_timestamp: from_unix_timestamp(now - 80_000), }, - // No CurrentTxInfo despite existing FailedTx records (The record has the failure + // No TxProcessingInfo despite existing FailedTx records (The record has the failure // status = 'Concluded') tx_opt: None } @@ -1437,7 +1437,7 @@ mod tests { balance_wei: gwei_to_wei(1_800_456_000_u32), last_paid_timestamp: from_unix_timestamp(now - 100_401), }, - tx_opt: Some(CurrentTxInfo { + tx_opt: Some(TxProcessingInfo { pending_tx_hash_opt: Some(format!("{:?}", make_tx_hash(0xABC))), failures: 0 }) @@ -1627,7 +1627,7 @@ mod tests { assert_eq!( result, - Some(CurrentTxInfo { + Some(TxProcessingInfo { pending_tx_hash_opt: Some(format!("{:?}", tx_hash)), failures: 0 }) @@ -1644,7 +1644,7 @@ mod tests { assert_eq!( result, - Some(CurrentTxInfo { + Some(TxProcessingInfo { pending_tx_hash_opt: Some(format!("{:?}", make_tx_hash(123))), failures: errors }) @@ -1659,7 +1659,7 @@ mod tests { assert_eq!( result, - Some(CurrentTxInfo { + Some(TxProcessingInfo { pending_tx_hash_opt: None, failures: errors }) diff --git a/node/src/accountant/db_access_objects/utils.rs b/node/src/accountant/db_access_objects/utils.rs index 377d7a349..282e664f2 100644 --- a/node/src/accountant/db_access_objects/utils.rs +++ b/node/src/accountant/db_access_objects/utils.rs @@ -12,7 +12,7 @@ use crate::sub_lib::accountant::PaymentThresholds; use ethereum_types::H256; use masq_lib::constants::WEIS_IN_GWEI; use masq_lib::messages::{ - CurrentTxInfo, RangeQuery, TopRecordsConfig, TopRecordsOrdering, UiPayableAccount, + TxProcessingInfo, RangeQuery, TopRecordsConfig, TopRecordsOrdering, UiPayableAccount, UiReceivableAccount, }; use rusqlite::{Row, Statement, ToSql}; @@ -268,7 +268,7 @@ impl From<&RangeQuery> for CustomQuery { #[derive(Debug, PartialEq)] pub struct PayableAccountWithTxInfo { pub account: PayableAccount, - pub tx_opt: Option, + pub tx_opt: Option, } pub fn remap_payable_accounts(accounts: Vec) -> Vec { @@ -291,7 +291,7 @@ pub fn remap_payable_accounts(accounts: Vec) -> Vec= 678 && age_payable <= (age_payable + act_period)); From f355b76e9ed868eff23e461e20c5ea4c006d04db Mon Sep 17 00:00:00 2001 From: Bert Date: Wed, 24 Sep 2025 13:05:59 +0200 Subject: [PATCH 10/11] GH-662: formatting --- masq/src/commands/financials_command/mod.rs | 2 +- node/src/accountant/db_access_objects/payable_dao.rs | 2 +- node/src/accountant/db_access_objects/utils.rs | 2 +- node/src/accountant/mod.rs | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/masq/src/commands/financials_command/mod.rs b/masq/src/commands/financials_command/mod.rs index b252a4091..f4788d44b 100644 --- a/masq/src/commands/financials_command/mod.rs +++ b/masq/src/commands/financials_command/mod.rs @@ -324,7 +324,7 @@ mod tests { use crate::test_utils::mocks::CommandContextMock; use atty::Stream; use masq_lib::messages::{ - TxProcessingInfo, ToMessageBody, TopRecordsOrdering, UiFinancialStatistics, + ToMessageBody, TopRecordsOrdering, TxProcessingInfo, UiFinancialStatistics, UiFinancialsResponse, UiPayableAccount, UiReceivableAccount, }; use masq_lib::ui_gateway::MessageBody; diff --git a/node/src/accountant/db_access_objects/payable_dao.rs b/node/src/accountant/db_access_objects/payable_dao.rs index 2a18e6d44..826de43ce 100644 --- a/node/src/accountant/db_access_objects/payable_dao.rs +++ b/node/src/accountant/db_access_objects/payable_dao.rs @@ -406,8 +406,8 @@ mod tests { use crate::database::rusqlite_wrappers::ConnectionWrapperReal; use crate::test_utils::make_wallet; use itertools::Itertools; - use masq_lib::messages::TxProcessingInfo; use masq_lib::messages::TopRecordsOrdering::{Age, Balance}; + use masq_lib::messages::TxProcessingInfo; use masq_lib::test_utils::utils::ensure_node_home_directory_exists; use rusqlite::Connection; use rusqlite::ToSql; diff --git a/node/src/accountant/db_access_objects/utils.rs b/node/src/accountant/db_access_objects/utils.rs index 282e664f2..ff32cad99 100644 --- a/node/src/accountant/db_access_objects/utils.rs +++ b/node/src/accountant/db_access_objects/utils.rs @@ -12,7 +12,7 @@ use crate::sub_lib::accountant::PaymentThresholds; use ethereum_types::H256; use masq_lib::constants::WEIS_IN_GWEI; use masq_lib::messages::{ - TxProcessingInfo, RangeQuery, TopRecordsConfig, TopRecordsOrdering, UiPayableAccount, + RangeQuery, TopRecordsConfig, TopRecordsOrdering, TxProcessingInfo, UiPayableAccount, UiReceivableAccount, }; use rusqlite::{Row, Statement, ToSql}; diff --git a/node/src/accountant/mod.rs b/node/src/accountant/mod.rs index 900472f51..010d25efc 100644 --- a/node/src/accountant/mod.rs +++ b/node/src/accountant/mod.rs @@ -1335,7 +1335,7 @@ mod tests { }; use masq_lib::messages::TopRecordsOrdering::{Age, Balance}; use masq_lib::messages::{ - TxProcessingInfo, CustomQueries, RangeQuery, TopRecordsConfig, UiFinancialStatistics, + CustomQueries, RangeQuery, TopRecordsConfig, TxProcessingInfo, UiFinancialStatistics, UiMessageError, UiPayableAccount, UiReceivableAccount, UiScanRequest, UiScanResponse, }; use masq_lib::test_utils::logging::init_test_logging; From 0734e5ba4c08477ade6c819ac4d39e5c493f8ab2 Mon Sep 17 00:00:00 2001 From: Bert Date: Wed, 1 Oct 2025 11:26:26 +0200 Subject: [PATCH 11/11] GH-662: added Eq attr --- node/src/accountant/db_access_objects/utils.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/node/src/accountant/db_access_objects/utils.rs b/node/src/accountant/db_access_objects/utils.rs index 834b9fabe..faa9d8656 100644 --- a/node/src/accountant/db_access_objects/utils.rs +++ b/node/src/accountant/db_access_objects/utils.rs @@ -304,7 +304,7 @@ impl From<&RangeQuery> for CustomQuery { } } -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Eq)] pub struct PayableAccountWithTxInfo { pub account: PayableAccount, pub tx_opt: Option,