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

Consistency regarding from_bytes APIs #491

Closed
tarcieri opened this issue Jan 5, 2023 · 11 comments
Closed

Consistency regarding from_bytes APIs #491

tarcieri opened this issue Jan 5, 2023 · 11 comments

Comments

@tarcieri
Copy link
Contributor

tarcieri commented Jan 5, 2023

There are several APIs for converting various types from bytes which generally take the form:

TypeName::from_bytes(bytes: &[u8; N])

There's one exception to this:

Scalar:::from_bytes_mod_order(bytes: [u8; 32])

...which makes a copy of the byte array. It should probably be changed to borrow the array, especially as from_bytes_mod_order_wide takes a &[u8; 64].

Another question is how From/TryFrom impls should be defined for these types:

  1. If a type can be infallibly parsed from a byte array, should we add a From<[u8; N]> impl?
  2. If a type can fallibly be parsed from a byte array, should we add a TryFrom<[u8; N]> impl?
  3. Should we impl TryFrom<&[u8]>? (always fallible since the slice might be the wrong size)

An important consideration here is if multiple overlapping impls of the same trait are allowed. If we try to add that after a major release, it breaks inference, which I think is generally allowed under Rust SemVer, but can break downstream code and is generally annoying to deal with.

Edit: CompressedEdwardsY and CompressedRistretto both have a from_slice method that panics of the slice is the wrong length. We should perhaps consider returning a Result instead.

@tarcieri
Copy link
Contributor Author

tarcieri commented Jan 5, 2023

Note that right now ed25519-dalek also follows the borrowed byte array convention for from_bytes, and impls 3, but not 1 or 2.

@tarcieri
Copy link
Contributor Author

tarcieri commented Jan 5, 2023

Related is #490: should we add an EdwardsPoint::from_bytes API that avoids the need to use CompressedEdwardsY?

@SergeStrashko
Copy link
Contributor

SergeStrashko commented Jan 5, 2023

Scalar has more than one method from_* ([u8;32])

pub fn from_bytes_mod_order(bytes: [u8; 32]) -> Scalar 

then

    pub fn from_canonical_bytes(bytes: [u8; 32]) -> CtOption<Scalar> 

and

    pub const fn from_bits(bytes: [u8; 32]) -> Scalar

I think it might be confusing using From<&[u8;32]> for Scalar what to expect? Is it same as from_bits or from_bytes_mod_order ?

@tarcieri
Copy link
Contributor Author

tarcieri commented Jan 5, 2023

For Scalar specifically I think if we were to add a trait-based conversion, it would make sense to have TryFrom<[u8; 32]> which calls Scalar::from_canonical_bytes.

That would provide a nice convenience wrapper for cases where Scalar is non-secret and users don't want to deal with CtOption.

@tarcieri
Copy link
Contributor Author

tarcieri commented Jan 5, 2023

Well, now I see why Scalar:::from_bytes_mod_order(bytes: [u8; 32]) accepts its argument that way: the first thing it does is move it into the Scalar struct.

Sidebar: I'm a bit confused why Scalar is internally a [u8; 32] rather than a newtype for a (backend-specific) UnpackedScalar. It seems like the very first thing Scalar does before any arithmetic is unpack().

@tarcieri tarcieri closed this as completed Jan 5, 2023
@tarcieri tarcieri reopened this Jan 5, 2023
@tarcieri
Copy link
Contributor Author

tarcieri commented Jan 5, 2023

Whoops, didn't mean to close that quite yet.

I guess one advantage of the Scalar/UnpackedScalar is it makes const fn support a bit easier, since UnpackedScalar has to be done on a backend-by-backend basis, and it's not possible to make the SIMD backends const fn.

Opened #493 regarding const fn support.

@SergeStrashko
Copy link
Contributor

SergeStrashko commented Jan 7, 2023

I can make existing Scalar52 and Scalar29 most of the methods const
For reference please have a look SergeStrashko#1
It should allow Scalar(pub(crate) UnpackedScalar) at least for serial backends.
The PR above also changes Scalar to be Scalar(UnpackedScalar)

@nox
Copy link
Contributor

nox commented Feb 14, 2023

How do we get progress on this, to finally release 4.0.0?

@tarcieri
Copy link
Contributor Author

As noted above, upon further review the inherent methods seem to have good reasons for being the way they are.

So the main thing would be to survey places where TryFrom could be used where it presently isn't.

@hrxi
Copy link

hrxi commented Mar 7, 2023

So the main thing would be to survey places where TryFrom could be used where it presently isn't.

Since TryFrom implementations could be added later, maybe this could be made non-blocking for the release?

@tarcieri
Copy link
Contributor Author

tarcieri commented Mar 7, 2023

Yeah, only thing to worry about is overlapping impls causing inference failure when you go from 1 -> 2 or more, but that isn't technically a breaking change.

That said, I can close this to unblock a release.

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

No branches or pull requests

4 participants