-
Notifications
You must be signed in to change notification settings - Fork 1.8k
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
fix(cast): Harden cast send
and provide more accurate error messages
#4874
Conversation
/// If the path is a directory then we try to find the first keystore file with the correct | ||
/// sender address | ||
/// if the path is a directory, it bails and asks the user to specify the keystore file | ||
/// directly. | ||
fn find_keystore_file(&self, path: impl AsRef<Path>) -> Result<PathBuf> { | ||
let path = path.as_ref(); | ||
if !path.exists() { | ||
bail!("Keystore file `{path:?}` does not exist") | ||
} | ||
|
||
if path.is_dir() { |
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.
Note that this is where compatibility with dapptools on this functionality is broken. Happy to accomodate suggestions on how we want to handle this as keystores do not have the expected address
property.
On it |
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.
@Evalir ty, this is my review
// Checking if signer isn't the default value | ||
// 00a329c0648769A73afAc7F9381E08FB43dBEA72. | ||
if resend { | ||
tx.nonce = Some(provider.get_transaction_count(config.sender, None).await?); |
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 is duplicated, can we extract a function?
async fn update_nonce(tx: &mut Tx...
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.
see #4874 (comment)
tx.nonce = Some(provider.get_transaction_count(config.sender, None).await?); | ||
} | ||
|
||
cast_send( |
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.
cast_send
is called twice.
Since cast_send
is our final intent, can we refactor to have just a call at the end of the fn block?
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.
Yeah the reason this is written like so is that provider
actually takes different types depending if you have a signer or not, which will also affect from
and the eth_*
method used. We want to clearly differentiate when we're falling back to eth_sendTransaction
vs using a signer and using eth_sendRawTransaction
, and so we have these two code paths. I actually think abstracting it all away so it looks like only one path could make this harder to reason about and could allow misleading error messages due to fall-through cases. Let me know what you think!
@@ -109,19 +157,6 @@ impl SendTxArgs { | |||
tx.nonce = Some(provider.get_transaction_count(from, None).await?); |
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.
Duplicated, see above
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.
Answered in #4874 (comment) — essentially want to keep these code paths simple instead of further abstracting them.
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.
+1
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.
changes lgtm, haven't tested it though,
pending @0xMelkor for final approval
@@ -109,19 +157,6 @@ impl SendTxArgs { | |||
tx.nonce = Some(provider.get_transaction_count(from, None).await?); |
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.
+1
Since foundry-rs/foundry#4874 the `--from` flag only works in combination with the `--unlocked` flag. This commit adds the missing `--unlocked` flag to an example that is using `--from`.
Since foundry-rs/foundry#4874 the `--from` flag only works in combination with the `--unlocked` flag. This commit adds the missing `--unlocked` flag to an example that is using `--from`.
Since foundry-rs/foundry#4874 the `--from` flag only works in combination with the `--unlocked` flag. This commit adds the missing `--unlocked` flag to an example that is using `--from`.
Motivation
Closes #4831.
cast send
right now assumes that if a signer can't be constructed, it must fall back toeth_sendTransaction
in all cases. This is not exactly true, and will result in misleading error messages depending on the combination of flags passed in for the wanted signing method. Particular highlights were:address
field that was being used to identify the correct keystore, so they were never found. This causedcast send
to fall back toeth_sendTransaction
hoping the remote RPC had the unlocked account to sign the tx.cast send
would again fall back toeth_sendTransaction
. This would happen similarly with other available local signing methods.--from
address is passed in and no other information is provided,cast send
would again fall back toeth_sendTransaction
. This is actually "correct" behavior, but the user is kept in the dark as to why this is and is not given enough context.Solution
The send command was lightly refactored and commented for future reference. The main changes are:
eth_sendTransaction
is now only possible by passing the--unlocked
flag, matchingforge script
's behavior. This flag also requires the--from
flag to be passed, to ensure enough information is passed in by the user.eth_sendTransaction
, we must now be able to construct a signer with the flags passed in, or we will bail and inform the user something is wrong.--keystore
flag, and instead bail and tell the user to provide the keystore file directly. This is because keystores are no longer generated with theaddress
inside of them. (This is a big discussion point, and while I favor being more strict, we could also try and decrypt all the keystores found and use the first one to succeed, but this has edge cases that could lead to bad scenarios).eth_sendTransaction
, we bail and inform the user that they must only have the ledger unlocked and connected with no other conflicting wallet apps open.EDIT: @DaniPopes @0xMelkor can't seem to be able to add you as reviewers, but as always, feel free to review!