Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 23 additions & 4 deletions USER-INTERFACE-INTERFACE.md
Original file line number Diff line number Diff line change
Expand Up @@ -769,7 +769,10 @@ searching over; this is the upper limit for the debt's age, or how long it has b
"wallet": <string>,
"ageS": <integer>,
"balanceGwei": <integer>,
"pendingPayableHashOpt": <optional string>
"txProcessingInfoOpt": {
"pendingTxHashOpt": <optional string>,
"failures": <integer>
}
},
[...]
],
Expand Down Expand Up @@ -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.

Expand Down
93 changes: 55 additions & 38 deletions masq/src/commands/financials_command/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -324,8 +324,8 @@ mod tests {
use crate::test_utils::mocks::CommandContextMock;
use atty::Stream;
use masq_lib::messages::{
ToMessageBody, TopRecordsOrdering, UiFinancialStatistics, UiFinancialsResponse,
UiPayableAccount, UiReceivableAccount,
ToMessageBody, TopRecordsOrdering, TxProcessingInfo, UiFinancialStatistics,
UiFinancialsResponse, UiPayableAccount, UiReceivableAccount,
};
use masq_lib::ui_gateway::MessageBody;
use masq_lib::utils::slice_of_strs_to_vec_of_strings;
Expand Down Expand Up @@ -1028,15 +1028,27 @@ mod tests {
wallet: "0xA884A2F1A5Ec6C2e499644666a5E6af97B966888".to_string(),
age_s: 5645405400,
balance_gwei: 68843325667,
pending_payable_hash_opt: None,
tx_processing_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,
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,
tx_processing_info_opt: Some(TxProcessingInfo{pending_tx_hash_opt: None,failures: 3}
),
},
UiPayableAccount {
wallet: "0xeF456a11A5Ec6C2e499655787a5E6af97c961123".to_string(),
age_s: 161514,
balance_gwei: 1111,
tx_processing_info_opt: Some(TxProcessingInfo{pending_tx_hash_opt: Some("0xb45a663d56121112f4d45c1c3a245be32eAA6afd20b759b762f1dba945ec2f41".to_string()),failures: 1}
),
},
]),
Expand All @@ -1059,9 +1071,9 @@ mod tests {
wallet: "0x6DbcCaC5596b7ac986ff8F7ca06F212aEB444440".to_string(),
age_s: 150000,
balance_gwei: 8,
pending_payable_hash_opt: Some(
tx_processing_info_opt: Some(TxProcessingInfo{pending_tx_hash_opt: Some(
"0x0290db1d56121112f4d45c1c3f36348644f6afd20b759b762f1dba9c4949066e"
.to_string(),
.to_string()),failures: 0}
),
}]),
receivable_opt: None,
Expand Down Expand Up @@ -1117,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\
Expand Down Expand Up @@ -1253,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\
Expand Down Expand Up @@ -1345,25 +1361,26 @@ 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,
pending_payable_hash_opt: Some(
tx_processing_info_opt: Some(TxProcessingInfo{pending_tx_hash_opt: Some(
"0x0290db1d56121112f4d45c1c3f36348644f6afd20b759b762f1dba9c4949066e"
.to_string(),
.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",
Expand Down Expand Up @@ -1411,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\
Expand Down Expand Up @@ -1603,16 +1620,16 @@ mod tests {
wallet: "0xA884A2F1A5Ec6C2e499644666a5E6af97B966888".to_string(),
age_s: 5405400,
balance_gwei: 644000000,
pending_payable_hash_opt: Some(
tx_processing_info_opt: Some(TxProcessingInfo{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,
tx_processing_info_opt: None,
},
]),
receivable_opt: Some(vec![
Expand Down Expand Up @@ -1689,22 +1706,22 @@ mod tests {
wallet: "0x6e250504DdfFDb986C4F0bb8Df162503B4118b05".to_string(),
age_s: 4445,
balance_gwei: 3862654858938090,
pending_payable_hash_opt: Some(
tx_processing_info_opt: Some( TxProcessingInfo{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,
tx_processing_info_opt: Some(TxProcessingInfo{pending_tx_hash_opt: None, failures: 3}),
},
UiPayableAccount {
wallet: "0x6DbcCaC5596b7ac986ff8F7ca06F212aEB444440".to_string(),
age_s: 6089909,
balance_gwei: 66658,
pending_payable_hash_opt: None,
tx_processing_info_opt: None,
},
]),
receivable_opt: None,
Expand Down Expand Up @@ -1754,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());
Expand Down
65 changes: 59 additions & 6 deletions masq/src/commands/financials_command/pretty_print_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -27,10 +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(hash) = &self.pending_payable_hash_opt {
hash.to_string()
} else {
"None".to_string()
match &self.tx_processing_info_opt {
Some(current_tx_info) => match &current_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)
)
}
}
None => {
format!(
"Processing... {}",
AttemptsConjugator::new(current_tx_info.failures)
)
}
},
None => "None".to_string(),
},
]
}
Expand All @@ -47,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,
Expand Down Expand Up @@ -250,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)]
Expand Down Expand Up @@ -311,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))
})
}
}
11 changes: 9 additions & 2 deletions masq_lib/src/messages.rs
Original file line number Diff line number Diff line change
Expand Up @@ -679,8 +679,15 @@ pub struct UiPayableAccount {
pub age_s: u64,
#[serde(rename = "balanceGwei")]
pub balance_gwei: u64,
#[serde(rename = "pendingPayableHashOpt")]
pub pending_payable_hash_opt: Option<String>,
#[serde(rename = "txProcessingInfoOpt")]
pub tx_processing_info_opt: Option<TxProcessingInfo>,
}

#[derive(Serialize, Deserialize, Debug, PartialEq, Eq)]
pub struct TxProcessingInfo {
#[serde(rename = "pendingTxHashOpt")]
pub pending_tx_hash_opt: Option<String>,
pub failures: usize,
}

#[derive(Serialize, Deserialize, Debug, PartialEq, Eq)]
Expand Down
Loading