-
Notifications
You must be signed in to change notification settings - Fork 161
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
Initial support for forest-cli info #2578
Conversation
09de7e8
to
fb0506c
Compare
70375df
to
c8d9dac
Compare
@creativcoder Have you finished the change checklist? |
d1ea5ee
to
b11cead
Compare
b11cead
to
c63bd8a
Compare
@creativcoder Could you show an example of the output? |
@lemmih So far we have the following:
|
This should be in the PR description. |
And concepts such as |
|
65a25e3
to
b4cb1a3
Compare
forest/cli/src/cli/info_cmd.rs
Outdated
cur_duration: Duration, | ||
blocks_per_tipset_last_finality: f64, | ||
head: TipsetJson, | ||
) -> anyhow::Result<NodeStatusInfo> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This function can no longer fail.
forest/cli/src/cli/info_cmd.rs
Outdated
if self.use_color { | ||
writeln!(&mut output, "Network: {}", self.network())?; | ||
writeln!(&mut output, "Uptime: {}", self.uptime(now))?; | ||
writeln!(&mut output, "Chain: {}", self.chain_status())?; | ||
writeln!(&mut output, "Chain health: {}", self.health())?; | ||
writeln!( | ||
&mut output, | ||
"Default wallet address: {} {}", | ||
self.wallet_address(), | ||
self.wallet_balance() | ||
)?; | ||
} else { | ||
writeln!(&mut output, "Network: {}", self.network().clear())?; | ||
writeln!(&mut output, "Uptime: {}", self.uptime(now).clear())?; | ||
writeln!(&mut output, "Chain: {}", self.chain_status().clear())?; | ||
writeln!(&mut output, "Chain health: {}", self.health().clear())?; | ||
writeln!( | ||
&mut output, | ||
"Default wallet address: {} {}", | ||
self.wallet_address().clear(), | ||
self.wallet_balance().clear() | ||
)?; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Refactor this to avoid duplicate code.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
forest/cli/src/cli/info_cmd.rs
Outdated
let blocks_per_tipset_last_finality = | ||
node_status.chain_status.blocks_per_tipset_last_finality; | ||
|
||
let mut node_status_info = NodeStatusInfo::new( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This doesn't need to be mut
if you refactor it slightly.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I get https://github.com/ChainSafe/forest/actions/runs/5256500061/jobs/9497916282?pr=2578#step:6:1508 when moving wallet and balance fields as params
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
refactored to remove use_color
field
forest/cli/src/cli/info_cmd.rs
Outdated
let balance_token_amount = TokenAmount::from_atto(bal.parse::<BigInt>()?); | ||
Ok(format!("{:.4}", balance_token_amount.pretty())) | ||
} else { | ||
Err(anyhow::anyhow!("error parsing balance")) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks off to me. It returns a parsing error if there's no default wallet.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Reworded error msg
dismissing to allow others to review this PR
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nits
forest/cli/src/cli/info_cmd.rs
Outdated
output.push(network); | ||
output.push(uptime); | ||
output.push(chain); | ||
output.push(chain_health); | ||
output.push(wallet_info); | ||
|
||
output |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just construct a vec with vec![..]
here.
It's surprising to return Vec<String>
- could you add a comment explaining why?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
was needed for color strings fmt, but not needed atm. Changed it to a String
forest/cli/src/cli/info_cmd.rs
Outdated
fn balance(balance: &Option<String>) -> anyhow::Result<String> { | ||
if let Some(bal) = balance { | ||
let balance_token_amount = TokenAmount::from_atto(bal.parse::<BigInt>()?); | ||
Ok(format!("{:.4}", balance_token_amount.pretty())) | ||
} else { | ||
Err(anyhow::anyhow!("could not find balance")) | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's very odd to have a function take in a single &Option<T>
- just make the inner function, and call one of the Option
combinators on the outer option
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Of course, in this example, the mapped function is trivial and should be inlined.
forest/cli/src/cli/info_cmd.rs
Outdated
let wallet_info = { | ||
let wallet_address = self | ||
.default_wallet_address | ||
.clone() | ||
.unwrap_or("address not set".to_string()); | ||
|
||
let wallet_balance = match balance(&self.default_wallet_address_balance) { | ||
Ok(bal) => format!("[balance: {}]", bal), | ||
Err(e) => e.to_string(), | ||
}; | ||
|
||
format!( | ||
"Default wallet address: {} [{}]", | ||
wallet_address, wallet_balance | ||
) | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Consider rewriting this as follows:
let wallet_info = { | |
let wallet_address = self | |
.default_wallet_address | |
.clone() | |
.unwrap_or("address not set".to_string()); | |
let wallet_balance = match balance(&self.default_wallet_address_balance) { | |
Ok(bal) => format!("[balance: {}]", bal), | |
Err(e) => e.to_string(), | |
}; | |
format!( | |
"Default wallet address: {} [{}]", | |
wallet_address, wallet_balance | |
) | |
}; | |
let wallet_info = match ( | |
&self.default_wallet_address, | |
&self.default_wallet_address_balance, | |
) { | |
(None, None) => String::from("<no default wallet address>"), | |
(Some(address), Some(balance)) => { | |
let attos = balance | |
.parse::<BigInt>() | |
.expect("RPC returned an invalid string for balance"); | |
let token_amount = TokenAmount::from_atto(attos); | |
format!( | |
"Default wallet address: {address} [{:.4}]", | |
token_amount.pretty() | |
) | |
} | |
(None, Some(_)) | (Some(_), None) => { | |
unreachable!("If there's a default address, we should have the balance") | |
} | |
}; |
Note a couple of things here:
- writing exhaustively and defensively has shown us that there's some bugs in our logic somewhere
- we should either have all the default wallet info, or none of it
- we should always have a valid wallet string
Okay, so now we look for the causes of those bugs:
- The
new
function should be fallible, we shouldn't have error strings in our format function. It should error if we've got an address but no balance (and vice versa). It should be fine for the caller to unwrap that - Our RPC API is weakly typed. If it ever returns an invalid wallet string, something as gone wrong. We should panic there, as it's a software bug. Else, we should carry one with strong types. That means the RPC API should return
TokenAmount
, not aString
. This is one of the main benefits of using a type system
The "principles" at play here are
- making illegal states unrepresentable
- fail fast
- "defensive programming"
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So we shouldn't really have strings anywhere in our NodeStatusInfo
- consider actual types
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not going to hold up the PR on this, because you'll be doing follow on work, and this has been open a while anyway
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Noted. Thanks for the detailed review. We did decide to refactor RPC APIs to return strongly typed values, that'll be part of the follow-up PR and also accordingly the wallet fmt code.
forest/cli/src/cli/info_cmd.rs
Outdated
.contains(&expected_status_fmt)); | ||
} | ||
|
||
// FIXME: enable/debug this failing when color output is supported. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd greatly prefer it if this uncommented code was removed before merging.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
removed the commented test
Summary of changes
Changes introduced in this pull request:
Add a new info subcommand to forest-cli with the following information:
This is ported from https://github.com/filecoin-project/lotus/blob/master/cli/info.go"
forest-cli info output:
Reference issue to close (if applicable)
Closes #2442
Other information and links
Change checklist