Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Gh 236 #2

Open
wants to merge 14 commits into
base: master
Choose a base branch
from
Open
79 changes: 22 additions & 57 deletions USER-INTERFACE-INTERFACE.md
Original file line number Diff line number Diff line change
Expand Up @@ -564,80 +564,45 @@ field will be null or absent.
##### Correspondent: Node
##### Layout:
```
"payload": {
"payableMinimumAmount" = <nonnegative integer>,
"payableMaximumAge" = <nonnegative integer>,
"receivableMinimumAmount" = <nonnegative integer>,
"receivableMaximumAge" = <nonnegative integer>
}
"payload": {}
```
##### Description:
Requests a financial report from the Node.

In most cases, there will be many records in the database, most of them irrelevant because of amount or age.
Therefore, when the UI requests a financial report, it should specify minimum amounts and maximum ages. Records
with amounts smaller than the minimums, or older than the maximums, won't be included in the results, although
their values will be included in the totals.
Requests financial statistics from the Node.

This request will result in a cluster of queries to the database, which are quick but not instantaneous,
especially on old databases that contain lots of records. A UI that makes this request too many times per
second will perceptibly degrade the performance of the Node.

Amounts are specified in gwei (billions of wei); ages are specified in seconds. Values less than zero or
greater than 64 bits long will cause undefined behavior.
This request will report back information about a Node's historical financial operations. This will include both
services ordered from other Nodes and services provided for other Nodes, represented as monetary values owed and
paid to and from this Node's wallets.

#### `financials`
##### Direction: Response
##### Correspondent: Node
##### Layout:
```
"payload": {
"payables": [
{
"wallet": <string>,
"age": <nonnegative integer>,
"amount": <nonnegative integer>,
"pendingPayableHashOpt": <optional string>
},
< ... >
],
"totalPayable": <nonnegative integer>,
"receivables": [
{
"wallet": <string>,
"age": <nonnegative integer>,
"amount": <nonnegative integer>
},
< ... >
],
"totalReceivable": <nonnegative integer>
"totalUnpaidAndPendingPayable": <integer>
"totalPaidPayable": <nonnegative integer>
"totalUnpaidReceivable": <integer>
"totalPaidReceivable": <nonnegative integer>
}
```
##### Description:
Contains a financial report from the Node.

In most cases, there will be accounts in the database that are too old, or whose balances are too low, to
show up in this report. The `totalPayable` and `totalReceivable` fields will be accurate, but they will
probably be larger than the sums of the `payables` and `receivables` `amount` fields. The UI may choose to
ignore this discrepancy, or it may generate an "Other" account in each case to make up the difference.

The `wallet` fields will consist of 40 hexadecimal digits, prefixed by "0x".

The `age` fields contain the age in seconds, at the time the request was received, of the most recent transaction
on the associated account. The value will not be less than zero or longer than 64 bits.
Contains the requested financial statistics.

The `amount` fields contain the total amount in gwei owed to or due from the associated account at the time the
request was received. The value will not be less than zero or longer than 64 bits.
`totalUnpaidAndPendingPayable` is the number of Gwei we believe we owe to other Nodes and that those other Nodes have
not yet received, as far as we know. This includes both bills we haven't yet paid and bills we have paid, but whose
transactions we have not yet seen confirmed on the blockchain.

The `pendingPayableHashOpt` fields, if present, indicate that an obligation has been paid, but the payment is not
yet confirmed on the blockchain. If they appear, they will be standard 64-digit hexadecimal transaction numbers,
prefixed by "0x". If no `pendingPayableHashOpt` is given, then there were no pending payments on that account
at the time the request was received.
`totalPaidPayable` is the number of Gwei we have successfully paid to our creditors and seen confirmed during the time
the current instance of the Node has been running. In the future, this number may become cumulative over more time than
just the current Node run.

The `payables` and `receivables` arrays are not in any particular order.
`totalUnpaidReceivable` is the number of Gwei we believe other Nodes owe to us, but have not yet been included in
payments we have seen confirmed on the blockchain. This includes both payments that have never been made and also
payments that have been made but not yet confirmed.

For security reasons, the Node does not keep track of individual blockchain transactions, with the exception
of payments that have not yet been confirmed. Only cumulative account balances are retained.
`totalPaidReceivable` is the number of Gwei we have successfully received in confirmed payments from our debtors during
the time the current instance of the Node has been running. In the future, this number may become cumulative over more
time than just the current Node run.

#### `generateWallets`
##### Direction: Request
Expand Down
2 changes: 2 additions & 0 deletions masq/src/command_factory.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use crate::commands::commands_common::Command;
use crate::commands::configuration_command::ConfigurationCommand;
use crate::commands::crash_command::CrashCommand;
use crate::commands::descriptor_command::DescriptorCommand;
use crate::commands::financials_command::FinancialsCommand;
use crate::commands::generate_wallets_command::GenerateWalletsCommand;
use crate::commands::recover_wallets_command::RecoverWalletsCommand;
use crate::commands::set_configuration_command::SetConfigurationCommand;
Expand Down Expand Up @@ -48,6 +49,7 @@ impl CommandFactory for CommandFactoryReal {
Err(msg) => return Err(CommandSyntax(msg)),
},
"descriptor" => Box::new(DescriptorCommand::new()),
"financials" => Box::new(FinancialsCommand::new()),
"generate-wallets" => match GenerateWalletsCommand::new(pieces) {
Ok(command) => Box::new(command),
Err(msg) => return Err(CommandSyntax(msg)),
Expand Down
13 changes: 13 additions & 0 deletions masq/src/commands/commands_common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,10 @@ use masq_lib::ui_gateway::MessageBody;
use std::any::Any;
use std::fmt::Debug;
use std::fmt::Display;
use std::io::Write;

pub const STANDARD_COMMAND_TIMEOUT_MILLIS: u64 = 1000;
pub const STANDARD_COLUMN_WIDTH: usize = 33;

#[derive(Debug, PartialEq)]
pub enum CommandError {
Expand Down Expand Up @@ -95,6 +97,16 @@ impl From<ContextError> for CommandError {
}
}

pub(in crate::commands) fn dump_parameter_line(stream: &mut dyn Write, name: &str, value: &str) {
short_writeln!(
stream,
"{:width$} {}",
name,
value,
width = STANDARD_COLUMN_WIDTH
);
}

#[cfg(test)]
mod tests {
use super::*;
Expand All @@ -110,6 +122,7 @@ mod tests {
#[test]
fn constants_have_correct_values() {
assert_eq!(STANDARD_COMMAND_TIMEOUT_MILLIS, 1000);
assert_eq!(STANDARD_COLUMN_WIDTH, 33)
}

#[test]
Expand Down
34 changes: 15 additions & 19 deletions masq/src/commands/configuration_command.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
use crate::command_context::CommandContext;
use crate::commands::commands_common::CommandError::Payload;
use crate::commands::commands_common::{
transaction, Command, CommandError, STANDARD_COMMAND_TIMEOUT_MILLIS,
dump_parameter_line, transaction, Command, CommandError, STANDARD_COMMAND_TIMEOUT_MILLIS,
};
use clap::{App, Arg, SubCommand};
use masq_lib::as_any_impl;
Expand Down Expand Up @@ -80,47 +80,47 @@ impl ConfigurationCommand {
}

fn dump_configuration(stream: &mut dyn Write, configuration: UiConfigurationResponse) {
Self::dump_configuration_line(stream, "NAME", "VALUE");
Self::dump_configuration_line(
dump_parameter_line(stream, "NAME", "VALUE");
dump_parameter_line(
stream,
"Blockchain service URL:",
&configuration
.blockchain_service_url_opt
.unwrap_or_else(|| "[?]".to_string()),
);
Self::dump_configuration_line(stream, "Chain:", &configuration.chain_name);
Self::dump_configuration_line(
dump_parameter_line(stream, "Chain:", &configuration.chain_name);
dump_parameter_line(
stream,
"Clandestine port:",
&configuration.clandestine_port.to_string(),
);
Self::dump_configuration_line(
dump_parameter_line(
stream,
"Consuming wallet private key:",
&Self::interpret_option(&configuration.consuming_wallet_private_key_opt),
);
Self::dump_configuration_line(
dump_parameter_line(
stream,
"Current schema version:",
&configuration.current_schema_version,
);
Self::dump_configuration_line(
dump_parameter_line(
stream,
"Earning wallet address:",
&Self::interpret_option(&configuration.earning_wallet_address_opt),
);
Self::dump_configuration_line(stream, "Gas price:", &configuration.gas_price.to_string());
Self::dump_configuration_line(
dump_parameter_line(stream, "Gas price:", &configuration.gas_price.to_string());
dump_parameter_line(
stream,
"Neighborhood mode:",
&configuration.neighborhood_mode,
);
Self::dump_configuration_line(
dump_parameter_line(
stream,
"Port mapping protocol:",
&Self::interpret_option(&configuration.port_mapping_protocol_opt),
);
Self::dump_configuration_line(
dump_parameter_line(
stream,
"Start block:",
&configuration.start_block.to_string(),
Expand Down Expand Up @@ -165,24 +165,20 @@ impl ConfigurationCommand {

fn dump_value_list(stream: &mut dyn Write, name: &str, values: &[String]) {
if values.is_empty() {
Self::dump_configuration_line(stream, name, "[?]");
dump_parameter_line(stream, name, "[?]");
return;
}
let mut name_row = true;
values.iter().for_each(|value| {
if name_row {
Self::dump_configuration_line(stream, name, value);
dump_parameter_line(stream, name, value);
name_row = false;
} else {
Self::dump_configuration_line(stream, "", value);
dump_parameter_line(stream, "", value);
}
})
}

fn dump_configuration_line(stream: &mut dyn Write, name: &str, value: &str) {
short_writeln!(stream, "{:width$} {}", name, value, width = COLUMN_WIDTH);
}

fn interpret_option(value_opt: &Option<String>) -> String {
match value_opt {
None => "[?]".to_string(),
Expand Down
5 changes: 1 addition & 4 deletions masq/src/commands/descriptor_command.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,6 @@ mod tests {
use crate::commands::commands_common::CommandError::ConnectionProblem;
use crate::test_utils::mocks::CommandContextMock;
use masq_lib::messages::{ToMessageBody, UiDescriptorRequest, UiDescriptorResponse};
use masq_lib::utils::find_free_port;
use std::sync::{Arc, Mutex};

#[test]
Expand Down Expand Up @@ -187,11 +186,9 @@ mod tests {
#[test]
fn descriptor_command_sad_path() {
let transact_params_arc = Arc::new(Mutex::new(vec![]));
let port = find_free_port();
let mut context = CommandContextMock::new()
.transact_params(&transact_params_arc)
.transact_result(Err(ConnectionDropped("Booga".to_string())))
.active_port_result(Some(port));
.transact_result(Err(ConnectionDropped("Booga".to_string())));
let stdout_arc = context.stdout_arc();
let stderr_arc = context.stderr_arc();
let subject = DescriptorCommand::new();
Expand Down
Loading