-
Notifications
You must be signed in to change notification settings - Fork 647
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
Change the signature of consensus_encode to return io::Error's #494
Conversation
Nice! I think this should get its own version bump, separate from other changes, because it requires some invasive but mechanical changes for downstream users. |
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.
Pls see one command with consideration of doing FromStr
impl as well, otherwise utACK
src/network/message.rs
Outdated
/// | ||
/// Returns an empty error if and only if the string is | ||
/// larger than 12 characters in length. | ||
pub fn from_str<S: Into<Cow<'static, str>>>(s: S) -> Result<CommandString, ()> { |
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.
Some methods rely on FromStr
implementation to do a conversion; for instance if use clap
for parsing commands from command-line parameters (which may be a case for cli tools operating with bitcoin P2P network). So I propose also to add impl FromStr for CommandString
and from it call this method directly
Can it not just go with the next breaking version bump? I mean it doesn't really matter if it has it's own bump, right? At some point people will have to do it if they want to keep updating. |
108528f
to
446553d
Compare
src/network/message.rs
Outdated
} | ||
} | ||
|
||
#[deprecated(since = "0.26.0", note = "use CommandString::from")] |
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.
Trying to understand why this and above are depricated. The message will be really surprising in practice: instead of something.from()
users are asked to use something::<CommandString>.from()
, but even that is not clear from the message... Because in most cases method is called on the object, without additional qualifiers, and it will be up to the compiler to decide wich implementation to call.
It seams reasonable just to remove this methods, and nothing bad will happen.
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 I removed it. I pushed a new commit cleaning up the CommandString API.
src/network/message.rs
Outdated
if f.len() > 12 { | ||
CommandString(f[0..12].into()) |
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 don't like this, I'd rather remove the From
impl or panic here then do unexpected things
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.
oh I see you deprecated it, hmm idk what's the right thing to do
also, deprecating a trait impl doesn't do anything UX wise:
https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=c67bc53c2372c636208adbfab79fa02e
rust-lang/rust#39935
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.
Hmm, that seems to be a cargo bug. I would prefer not to panic, so perhaps just removing the method is the best 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.
K I pushed a new commit that cleans up the CommandString API. I thin it's such a infrequently used type that all the fancy construction methods are not needed. So I removed all of them.
src/network/message.rs
Outdated
// Don't use CommandString::from to avoid allocating when | ||
// it's not needed. |
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.
You're saying don't call CommandString::from
to avoid allocating but you then call to_owned()
which allocates?
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.
Hmm, that might have been related to an earlier version of the impl or wrongly formulated.. I don't remember. I think we can't use CommandString::from
because &'a str
doesn't implement Into<Cow<'static, str>>
, but I'm not sure, let me try.
Ah k so
72 | Ok(CommandString(s.into()))
| ^^^^^^^^
= note: expected `std::borrow::Cow<'static, _>`
found `std::borrow::Cow<'_, _>`
I was right, and I don't know what to do here :| I'd be in favor of dropping FromStr
as well for this type and just have the basic constructor which has Into<Cow<..>>
.
464196c
to
9c20cf8
Compare
Rebased and added commit to clean up the CommandString API. |
Just noticed this randomly and I have to say I'm not a huge fan of I don't know the exact context here, so feel free to ignore me if it's unrelated. |
Yeah I don't think that's quite related. Like currently it's about the difference between returning an Also, this PR is really neutral about |
src/network/message.rs
Outdated
/// | ||
/// Returns an empty error if and only if the string is | ||
/// larger than 12 characters in length. | ||
pub fn from<S: Into<Cow<'static, str>>>(s: S) -> Result<CommandString, ()> { |
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.
nit, Calling the function from
outside the From
trait is somewhat weird
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 I couldn't think of a better name. I thought of create
but it's quite conventional to use from
or from_xxx
for converter methods. I also considered just new
. Open to opinions, though, don't feel strongly about this. I just thought from was good enough.
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.
Open to opinions
Personally, new
would probably be the first thing I would look for when trying to construct one of these.
Another thing to consider might be to name it try_from
given that you are returning a Result
.
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.
According to rust API guidelines we should use with_str
as a function name: https://rust-lang.github.io/api-guidelines/naming.html#casing-conforms-to-rfc-430-c-case
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 sure, the guildlines say with_more_details
, however in this case intput is not "more details", it's literally the source of informtion. My understanding is that with_more_details
is used for special cases (with_capacity
is an obvious one)
The function header also kinda leaks internal representation but I guess it's fine?
Implementing TryFrom
would be best, but AFAIR it's not in supported MSRV.
9c20cf8
to
876651b
Compare
Rebased. |
29ab439
to
0b54429
Compare
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.
ACK
Yes, but if we do it in the same bump as a bunch of other stuff, then they have to do these changes in a single commit alongside a bunch of other stuff. |
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.
Seems like there a few things potentially reducing stability of the software that I missed in my previous review
src/network/message.rs
Outdated
/// | ||
/// Returns an empty error if and only if the string is | ||
/// larger than 12 characters in length. | ||
pub fn from<S: Into<Cow<'static, str>>>(s: S) -> Result<CommandString, ()> { |
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.
According to rust API guidelines we should use with_str
as a function name: https://rust-lang.github.io/api-guidelines/naming.html#casing-conforms-to-rfc-430-c-case
src/network/message.rs
Outdated
if strbytes.len() > 12 { | ||
return Err(encode::Error::UnrecognizedNetworkCommand(self.0.clone().into_owned())); | ||
} |
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 change will lead to a panic if the length of the CommandString is larger than 12. Why not to use ENAMETOOLONG
io::Error
equivalent?
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 don't think so. The PR removes the uncheck instantiation path for CommandString
through From
and replaces it with CommandString::new
, which checks that the string is <=12 bytes long.
src/network/message.rs
Outdated
@@ -202,7 +206,7 @@ impl NetworkMessage { | |||
|
|||
/// Return the CommandString for the message command. | |||
pub fn command(&self) -> CommandString { | |||
self.cmd().into() | |||
CommandString::from(self.cmd()).expect("valid cmd string") |
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 opens an attack vector to the rust-bitcoin
-based software by just sending them invalid command string causing a crash
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 don't think so, the return value of .cmd()
is a static string defines by the enum variant, not attacker controlled.
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 think the message in expect
should say invalid. The complete message sounding like "program panicked because cmd string is valid" is strange.
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 seems to be on hold for now but I really like the changes, does away with a lot of weirdness. Will do a more thorough review once it's merge-ready.
src/network/message.rs
Outdated
@@ -202,7 +206,7 @@ impl NetworkMessage { | |||
|
|||
/// Return the CommandString for the message command. | |||
pub fn command(&self) -> CommandString { | |||
self.cmd().into() | |||
CommandString::from(self.cmd()).expect("valid cmd string") |
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 don't think so, the return value of .cmd()
is a static string defines by the enum variant, not attacker controlled.
src/network/message.rs
Outdated
if strbytes.len() > 12 { | ||
return Err(encode::Error::UnrecognizedNetworkCommand(self.0.clone().into_owned())); | ||
} |
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 don't think so. The PR removes the uncheck instantiation path for CommandString
through From
and replaces it with CommandString::new
, which checks that the string is <=12 bytes long.
Setting to 0.26 milestone. Originally we had planned to punt to 0.27 because it is an API-breaking-only change, but I this we should prioritize it since our code currently unwraps these objects whenever we encode into hash engines, for example, so it would be better to eliminate theoretical panics. On the other hand if this winds up blocking 0.26 then I'll probably move it to 0.27 :) Needs rebase. |
This is instead of encode::Errors because the encoders should not be allowed to return errors that don't originate in the writer they are writing into. This is a part of the method definition that has been relied upon for a while already.
0b54429
to
61918df
Compare
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.
ack 61918df
post-ack |
This is instead of encode::Errors because the encoders should
not be allowed to return errors that don't originate in the writer
they are writing into.
This is a part of the method definition that has been relied upon for a
while already.