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

feat: introduce Multihash::new which can be called from const contexts #331

Closed
wants to merge 8 commits into from

Conversation

aatifsyed
Copy link

@aatifsyed aatifsyed commented Jul 19, 2023

Resolves #330.

@aatifsyed
Copy link
Author

Last use of wrap standing is

Multihash::wrap((*self).into(), digest)

Which I think is fine, if not necessary because Error::invalid_size is pub(crate)

@thomaseizinger
Copy link
Contributor

Last use of wrap standing is

We should also change that to not generate code that is using deprecated functionality.

@thomaseizinger thomaseizinger changed the title Usable const constructor feat: introduce Multihash::new which can be called from const contexts Jul 19, 2023
Copy link
Contributor

@thomaseizinger thomaseizinger left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you!

This is fine with me but I'd like more input from other maintainers. cc @vmx @mxinden

src/multihash.rs Outdated Show resolved Hide resolved
src/multihash.rs Outdated Show resolved Hide resolved
src/multihash.rs Show resolved Hide resolved
src/multihash.rs Outdated Show resolved Hide resolved
@thomaseizinger
Copy link
Contributor

Do we have other const functions that might be affected by the same problem? I wonder what a long-term solution could be here.

In the issue, you said that the problem is the io::Error in Kind? I tried previously to remove that one because it only appears in certain functions. Perhaps that is worth exploring because most functions don't actually return an io::Error.

@aatifsyed
Copy link
Author

Last use of wrap standing is

We should also change that to not generate code that is using deprecated functionality.

This is not possible without further changes to Error, which has no public constructors. I think that's outside the scope of this PR. But, to outline the problem,
Error can only be created using methods like wrap.
It must be created to implement MultihashDigest.

Neither rustc -Dwarnings nor clippy -Dclippy::pedantic complain about the use of this method in derived code. I can add #[automatically_derived] or #[allow(deprecated)] if you would prefer.

As an aside, the docs for multihash-derive are mangled

@aatifsyed
Copy link
Author

aatifsyed commented Jul 19, 2023

Do we have other const functions that might be affected by the same problem? I wonder what a long-term solution could be here.

In the issue, you said that the problem is the io::Error in Kind? I tried previously to remove that one because it only appears in certain functions. Perhaps that is worth exploring because most functions don't actually return an io::Error.

If you'd like my opinion, you should impl From<YourErrorType> for std::io::Error, and have io functions return and io::Result, which may be InvalidData with a source = Some(YourErrorType), where YourErrorType is a slimmer Error

(This would be a breaking change.)
((I think that's fine, you're pre 1.0.0, and the API should be allowed to evolve past current limitations))

@aatifsyed
Copy link
Author

I've gone ahead and implemented the required changes:

  • DigestTooBig now contains a usize so it can match the error message from Error. I think this is an odd choice - you're telling the caller the length of the slice, but they can already find that out because they retain slice ownership.
  • Added a new constructor (From::from) for Error so that the derive macro doesn't use wrap

@thomaseizinger
Copy link
Contributor

If you'd like my opinion, you should impl From<YourErrorType> for std::io::Error, and have io functions return and io::Result, which may be InvalidData with a source = Some(YourErrorType), where YourErrorType is a slimmer Error

Good idea! Would you mind capturing this in an issue?

((I think that's fine, you're pre 1.0.0, and the API should be allowed to evolve past current limitations))

Yes-ish. multihash is a foundational crate of the libp2p ecosystem. It appears pretty much in all public APIs because multiaddr and PeerId depend on it. A breaking change is a breaking change for every dependency. We just released a breaking change a few weeks ago and I'd like to not go through that effort again for a couple of months unless really necessary.

Copy link
Member

@mxinden mxinden left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No objections from my end.

@aatifsyed aatifsyed marked this pull request as draft July 25, 2023 08:33
@vmx
Copy link
Member

vmx commented Jul 25, 2023

If you'd like my opinion, you should impl From<YourErrorType> for std::io::Error, and have io functions return and io::Result, which may be InvalidData with a source = Some(YourErrorType), where YourErrorType is a slimmer Error

That sounds like a better solution. I would actually prefer that and keep the wrap name and just doing a breaking change. I agree that breaking changes are a hassle, but it shouldn't prevent us from going for a clean API. As this is very specific, also project (like libp2p) don't need to upgrade, they can just keep using the current version as long as they are happy with it.

I'd also be OK with just having the proper solution merged into master and just keep it there before doing a release, in case more breaking changes are uncovered.

Comment on lines +96 to +98
if size > S {
return Err(DigestTooBig(size));
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess we could also flip it and say BufferTooSmall? The thing is, the size of the internal buffer is part of the public API and likely, cannot be changed as easily so it makes more sense that the digest is too long. Really, this is one of those "should almost never happen"-errors.

The buffer is likely 32 or 64 bytes so you can fit the digests of SHA256 or SHA512 in it.

@thomaseizinger
Copy link
Contributor

thomaseizinger commented Jul 31, 2023

If you'd like my opinion, you should impl From<YourErrorType> for std::io::Error, and have io functions return and io::Result, which may be InvalidData with a source = Some(YourErrorType), where YourErrorType is a slimmer Error

That sounds like a better solution. I would actually prefer that and keep the wrap name and just doing a breaking change. I agree that breaking changes are a hassle, but it shouldn't prevent us from going for a clean API. As this is very specific, also project (like libp2p) don't need to upgrade, they can just keep using the current version as long as they are happy with it.

I'd also be OK with just having the proper solution merged into master and just keep it there before doing a release, in case more breaking changes are uncovered.

The issue is that you will have to immediately make a release because I guess that @aatifsyed would want to use this right away.

Not upgrading to latest solution is at best a band-aid and means what we might have to do more work later by backporting PRs instead of just being able to merge them into master.

An alternative suggestion could be to temporarily introduce a wrap_const function, not deprecate wrap and remove it again on the next breaking change.

@aatifsyed
Copy link
Author

Sorry for the week's silence, I was on vacation.

because I guess that @aatifsyed would want to use this right away.

You're correct, but as the rust-cid folks pointed-out1, there's a useable workaround for Copy types 2:

const fn const_unwrap<T: Copy, E>(r: Result<T, E>) -> T {
    let v = match r {
        Ok(r) => r,
        Err(_) => panic!(),
    };
    mem::forget(r);
    v
}

That means forest features are no longer blocked - we can just use const_unwrap().

I think a lot of the discussion above about error types/API design is valuable, and maybe could be addressed in #332, but this PR (or something like it) can wait until after that.

Footnotes

  1. https://github.com/multiformats/rust-cid/issues/138#issuecomment-1646977310

  2. https://github.com/filecoin-project/builtin-actors/blob/f84baa92638f85845bebeed2399b83e9768a40bd/runtime/src/runtime/empty.rs#L8-L15

@thomaseizinger
Copy link
Contributor

I think a lot of the discussion above about error types/API design is valuable, and maybe could be addressed in #332, but this PR (or something like it) can wait until after that.

I agree with that. Thank you for the feedback and input on the error design. We'll incorporate it on the next breaking change :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

multihash const constructors unusable
4 participants