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

Allow addition and multiplication for references to secret shared values #787

Merged
merged 20 commits into from
Sep 29, 2023

Conversation

akoshelev
Copy link
Collaborator

This change brings an extra trait bound for linear secret sharing trait, to allow addition and multiplication on references, rather than owned types. Currently it is only possible to do a + b or a + &b, but not &a + b or &a + &b. This change makes it possible.

As usual, it is not all rosy here, because of the compiler issue: rust-lang/rust#20671. There is a need currently to specify the additional trait bound on every function, but I think it is worth it because not every .clone() call can be eliminated by the compiler, as it can be seen here. We're better off helping optimizer here. So it cost us additional trait bound, but gives a much nicer addition/multiplication w/o extra clones and makes us not worry about compiler not being able to optimize it.

Once (if?) that gnarly issue is fixed, we could probably just add for <'a, 'b> &'a Self: Add<'b Self>bound to ArithmeticOps trait and eliminate all extra helper traits along with extra bounds on functions.

Implementation notes

  • I could've probably used a macro to synthesize all extra Add/Sub/Mul implementations, but decided it is not worth it for just two types. Maybe if we had more secret sharing types ....

  • I tried hard to mimic num trait definition (Add new traits for reference and assignment operators rust-num/num#283), but ran into an error complaining that Add operation is not general enough:

error: implementation of `Add` is not general enough
  --> src/secret_sharing/mod.rs:97:49
   |
97 |         assert_eq!(AdditiveShare::<Fp31>::ZERO, sum_ref_ref(&AdditiveShare::<Fp31>::ZERO, &AdditiveShare::ZERO));
   |                                                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ implementation of `Add` is not general enough
   |
   = note: `Add<replicated::semi_honest::additive_share::AdditiveShare<fp31::Fp31>>` would have to be implemented for the type `&'0 replicated::semi_honest::additive_share::AdditiveShare<fp31::Fp31>`, for any lifetime `'0`...
   = note: ...but `Add<replicated::semi_honest::additive_share::AdditiveShare<fp31::Fp31>>` is actually implemented for the type `&'1 replicated::semi_honest::additive_share::AdditiveShare<fp31::Fp31>`, for some specific lifetime `'1`

it remains a mystery to me, why I was getting it. The same function worked just fine for u32 and primitive types had the same 4 Add overloads with reference ones defined exactly as ours for additive secret shares.

New trait bounds for arithmetic

but getting the following error

overflow evaluating the requirement `&ordered_float::OrderedFloat<_>: Neg`

I guess I need to constrain that type to `Linear`
If this plane did not have a WiFi, I'd never be able to do it.

It seems that `Neg` was tripping me off - adding it as a constraint to trait <A, B> and only constraining it to B, makes the compiler go to infinite loop trying to evaluate all possible types that can fit. At the end, I was getting the gnarly "overflow evaluating ..." error

So structure needs to be as follows:

* Traits that have Rhs and Output
* Traits that only have Output
* Traits that are valid for secret sharing (add, sub, neg)
* Traits that are valid for basic arithmetic (mul)
Add new trait bound to fix it
Copy link
Member

@martinthomson martinthomson left a comment

Choose a reason for hiding this comment

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

Some clarity about naming is probably worth trying to achieve.

src/ff/mod.rs Outdated
///
/// [`RefNum`]: https://docs.rs/num/0.4.1/num/traits/trait.RefNum.html
/// [`rust-issue`]: https://github.com/rust-lang/rust/issues/20671
pub trait RefOps<'a, Base: 'a, R: 'a>:
Copy link
Member

Choose a reason for hiding this comment

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

I think that ArithmeticOps is the better choice of name here, with the trait above being for ArithmeticAssignOps or something like that.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I'd love to keep Ref somewhere in the name to indicate that it only makes sense to implement/bound if you need l-value references

Copy link
Member

Choose a reason for hiding this comment

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

ArithmeticRefOps then, I guess.

src/ff/mod.rs Outdated
pub trait LocalAssignOps<Rhs = Self>: AddAssign<Rhs> + SubAssign<Rhs> {}
impl<T, Rhs> LocalAssignOps<Rhs> for T where T: AddAssign<Rhs> + SubAssign<Rhs> {}

/// Arithmetic operations that may or may not require communication.
Copy link
Member

Choose a reason for hiding this comment

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

Can these really involve communication? None of these traits are async, so this can't do that. That suggests that these are still local operations, but only those that involve value-based l-values.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I tried to distinguish operations that can be safely performed locally and confusing ones like Mul that someone can accidentally implement for secret shares as (a.0 * b.0, a.1 * b.1). Lets chat later today about it - I'd love to have better names for all of those traits

@@ -30,6 +31,6 @@ where
// = false_value
Ok(false_value.clone()
Copy link
Member

Choose a reason for hiding this comment

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

Missed a clone opportunity?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

oh my...

src/ff/mod.rs Outdated
/// Note: Neg operation is also local, but is causing issues when added as a bound to this trait.
/// The problem is that it does not use `Rhs` generic and rustc overflows trying to compile functions
/// that use HRTB generics bounded by `LocalArithmeticOps`.
pub trait LocalArithmeticOps<Rhs = Self, Output = Self>:
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

addsub?

thanks to mt@'s eagle eye vision.
It is too specific to be in the finite fields, basically nothing except `Linear` can conform to it
and rename RefOps to `LinearRefOps`
@akoshelev
Copy link
Collaborator Author

@martinthomson I think it is ready for review now

@martinthomson martinthomson merged commit 0599ba6 into main Sep 29, 2023
6 checks passed
@martinthomson martinthomson deleted the ref-refs branch September 29, 2023 02:38
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.

2 participants