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

SignatureScheme trait bounds #148

Merged
merged 16 commits into from
Dec 7, 2022
Merged

SignatureScheme trait bounds #148

merged 16 commits into from
Dec 7, 2022

Conversation

tessico
Copy link
Contributor

@tessico tessico commented Nov 28, 2022

Description

Add trait bounds on SignatureScheme trait.
Bounds not yet added for SigningKey, pending discussion in espresso-common.

closes: #145, #155


Before we can merge this PR, please make sure that all the following items have been
checked off. If any of the checklist items are not applicable, please leave them but
write a little note why.

  • Targeted PR against correct branch (main)
  • Linked to GitHub issue with discussion and accepted design OR have an explanation in the PR that describes this work.
  • Wrote unit tests
  • Updated relevant documentation in the code
  • Added a relevant changelog entry to the Pending section in CHANGELOG.md
  • Re-reviewed Files changed in the GitHub PR explorer

@tessico tessico requested a review from a team November 29, 2022 14:51
@tessico
Copy link
Contributor Author

tessico commented Nov 29, 2022

@EspressoSystems/jellyfish

primitives/src/signatures/mod.rs Outdated Show resolved Hide resolved
primitives/src/signatures/bls.rs Outdated Show resolved Hide resolved
Comment on lines +68 to +74
impl core::ops::Deref for BLSSignature {
type Target = Signature;

fn deref(&self) -> &Self::Target {
&self.0
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

I don't see manual impl of Deref often, especially not for newtype, may I ask why do we need this?

(some say that implementing Deref for newtype is a bad idea)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It's a convenient way of allowing calls to the underlying value directly, e.g. sig.verify(...) instead of sig.0.verify(...).

When you say you don't see it done manually, do you mean it's mostly done via a derive, like derive-more crate? Or generally not done via Deref?
Here's a some points against using it: https://rust-unofficial.github.io/patterns/anti_patterns/deref.html
Though also in the discussion you linked there are some strong arguments for using Deref (not DerefMut) specifically for the purpose of newtypes.

In this specific case I don't see an immediate danger of implementing Deref though. If you do, I'm not gonna fight for keeping Deref and happy to use another solution.
Let's also hear other opinions?

Copy link
Contributor

@alxiong alxiong Nov 30, 2022

Choose a reason for hiding this comment

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

yeah I also agree there's no intermediate harm, but the anti-pattern does introduce mental load as

Most importantly this is a surprising idiom - future programmers reading this in code will not expect this to happen.

it's abusing the Deref which is supposed to be custom pointer type, not newtype accessing its inner value.
(another testimony of the same problem)

I don't mind too much about sig.0.verify(), it's internal details, won't get exposed to consumer anyway.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I've removed Deref as a trial in 7149b1a. We can still revert later :)

Copy link
Contributor

@alxiong alxiong Dec 5, 2022

Choose a reason for hiding this comment

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

So as I dig deeper, there are some subtleties involved.

TL;DR: yes, in our case, we can use Deref as you originally proposed. @tessico

this comment rust-lang/api-guidelines#249 (comment) convinced me that using Deref for transparent newtype is not only okay but also practically seen in std lib and some high-profile crates.
So is suggested in the Rust book itself.

What I'm also more sure of is:

  • we should NOT use Deref to simulate OOP-style inheritance (instead, we should wait for delegation to be available in rust, or use ambassador crate to achieve that)
  • we should be careful about the potential violation of intended visibility when implementing DerefMut (to access fn method(&mut self) of the underlying types). see my example here.

Jon Gjengset had updated a good summary here.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@alxiong Nice research! I knew I saw it in the Rust book somewhere, I must have missed it last time around.
It's encouraging to see it used in std :)

primitives/src/signatures/bls.rs Outdated Show resolved Hide resolved
@tessico tessico requested a review from a team November 30, 2022 12:45
@tessico tessico requested a review from alxiong November 30, 2022 17:02
alxiong
alxiong previously approved these changes Nov 30, 2022
Copy link
Contributor

@alxiong alxiong left a comment

Choose a reason for hiding this comment

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

LGTM!

Comment on lines 22 to 34
type SigningKey: Clone + Send + Sync + Zeroize;

/// Verification key
type VerificationKey;
type VerificationKey: Clone + Send + Sync + for<'a> Deserialize<'a> + Serialize;

/// Public Parameter
type PublicParameter;

/// Signature
type Signature;
type Signature: Clone + for<'a> Deserialize<'a> + Serialize;

/// A message is &\[MessageUnit\]
type MessageUnit;
Copy link
Contributor

Choose a reason for hiding this comment

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

follow-up comment:

  • all should have Debug + Sync + Send + Clone (so that they can be printed, and shared across threads), including MessageUnit should also have those traits.
  • all except MessageUnit should have ParitalEq + Eq for comparison.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It seems that blst::min_sig::SecretKey doesn't implement PartialEq for some reason (while PublicKey and Signature do), see an issue here. For now I've implemented it manually by comparing the output of serialization, which seems the only way currently.
Depending on what ppl say in the blst issue, we might either add the implementation to their crate and contribute upstream, or if there was a good reason for not including it, remove the bounds from our SignatureScheme trait.

Copy link
Contributor

Choose a reason for hiding this comment

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

oh we have overlapping work. I also implemented a (possibly constant-time) comparison for PartialEq in #156

@tessico tessico requested a review from alxiong December 7, 2022 11:03
Copy link
Contributor

@alxiong alxiong left a comment

Choose a reason for hiding this comment

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

LGTM!

(I will try to resolve the conflict with my PR later, somehow some of the overlap between our work slip my mind when I worked on my PR)

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.

Add trait bounds to types in Signature trait
2 participants