-
Couldn't load subscription status.
- Fork 563
Add allocation-free MultiscalarMul method
#790
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
base: main
Are you sure you want to change the base?
Add allocation-free MultiscalarMul method
#790
Conversation
|
As a data point, here's how this was handled in https://docs.rs/elliptic-curve/0.14.0-rc.10/elliptic_curve/ops/trait.LinearCombination.html See also: |
|
Its not exactly possible to adapt it to this model while keeping all of the current features in this case. As I mentioned above we could keep impl MultiscalarMul<Vec<?>> for Point {
...
}
impl<const N: usize> MultiscalarMul<[?; N]> for Point {
...
}We could maybe put <EdwardsPoint as MultiscalarMul<[?; 12]>>::multiscalar_mul(...);So yeah, I'm open to new ideas. Alternatively we could go down the |
I think you missed the two overlapping impls: The latter is notably: impl<C, const N: usize> LinearCombination<[(ProjectivePoint<C>, <C as CurveArithmetic>::Scalar); N]> for ProjectivePoint<C> |
|
The current trait looks like this: pub trait MultiscalarMul {
type Point;
fn multiscalar_alloc_mul<I, J>(scalars: I, points: J) -> Self::Point
where
I: IntoIterator,
I::Item: Borrow<Scalar>,
J: IntoIterator,
J::Item: Borrow<Self::Point>;
}So if we change the trait to be like pub trait MultiscalarMul<Input> {
type Point;
fn multiscalar_alloc_mul(input: Input) -> Self::Point;
}We can implement it with the current parameter types like this: impl<I: IntoIterator<Item: Borrow<Scalar>>, J: IntoIterator<Item: Borrow<EdwardsPoint>>> MultiscalarMul<(I, J)> for EdwardsPoint {
type Point = EdwardsPoint;
...
}However adding an implementation for fixed arrays won't work: impl<const N: usize> MultiscalarMul<(&[Scalar; N], &[EdwardsPoint; N])> for EdwardsPoint {
type Point = EdwardsPoint;
...
}Which yields the "conflicting implementations" error. The reason it works in We can do this here as well, but we will loose the functionality that Let me know if I missed something. |
|
The trait in Yes, as I said earlier the trait in Notably the |
I am definitely unable to use the current
Yes, I've outlined why this doesn't work while preserving the
I'm happy to do the same here! I don't seem to get what you are trying to tell me. If you are telling me that we can preserve the If you are telling me to use |
Again, I'm talking about the pub trait LinearCombination<PointsAndScalars>: CurveGroup
where
PointsAndScalars: AsRef<[(Self, Self::Scalar)]> + ?Sized,i.e. AsRef<[(Self, Self::Scalar)]>That allows you to get access to a slice of a 2-tuples of points and scalars, and the slice primitive type impls https://doc.rust-lang.org/std/primitive.slice.html#iteration So anything relying on iterating generically can use |
Ah, I see where the confusion is! I'm talking about the API side, not the implementation. The current functionality in The advantage of being able to pass iterators is that users don't have to put all the input in a contiguous slice of memory, but can just produce them in-place in an let values: HashMap<_, Foo> = ...;
let result = EdwardsPoint::multiscalar_mul(
values.iter().map(|foo| &foo.scalar),
values.iter().map(|foo| &foo.point),
);
let result = ProjectivePoint::lincomb(?);
// We can't produce a slice from a `HashMap` without allocating memory, e.g. collect them in a `Vec`. |
|
Let me just say again that I personally have no stake in the whole |
|
Instead of an |
|
@daxpedda here's a It's nearly API compatible with the old version except for the addition of a lifetime, which I might see if I can remove. Edit: hmm, removing it seems hard while remaining compatible with slices, since the slice iterator needs to borrow its items |
|
Alright, seems that PR isn't helpful after all since the existing trait is generic over iterators. Looking at some real-world code examples it seems most people store the inputs in a https://github.com/search?q=curve25519_dalek+multiscalar_mul+language%3ARust&type=code&l=Rust So we shouldn't remove that functionality either. |
|
I think at a minimum, we could change it from taking two iterators to one, while not taking away any functionality. Multiple sources can still be supported with |
|
Yeah, I'm definitely a big fan of eliminating the error case of mismatched iterator lengths by using a 2-tuple. I'm not sure how wild people will be about updating their code though. Perhaps there could be a deprecated legacy method that accepts the two iterators and builds an iterator over 2-tuples for you. |
|
I should probably also repeat here what I noted in RustCrypto/traits#1936, that there seems to be a fundamental tradeoff between having a trait that can work in both alloc and no-alloc cases, and a trait which is generic over iterators, since supporting the latter requires alloc. That said, we can conditionally impl |
|
So in the meantime is just adding a second method to |
|
I think we should probably consider changes to |
6f116ab to
3379c6a
Compare
3379c6a to
fafcf48
Compare
This renames
MultiscalarMul::multiscalar_mul()tomultiscalar_alloc_mul()and adds a newmultiscalar_mul()method taking fixed-sized arrays. This enables the usage of allocation-free multiscalar multiplication.To that end the following changes were made:
multiscalar_mul()andmultiscalar_alloc_mul()can share the same code.strausmodule is now exposed without thealloccrate feature.MultiscalarMul::multiscalar_alloc_mul()is now guarded behindcfg(feature = "alloc"). If we intend users to implementMultiscalarMulon their own custom curves having a required trait method behind a crate feature is pretty bad UX.This could be extended to cover
VartimeMultiscalarMulandVartimePrecomputedMultiscalarMul, but I don't have a use-case personally.The new fixed-array method also looses a lot of the flexibility the new
multiscalar_alloc_mul()offers. I'm happy to sync that flexibility, but it would require calling it like so:RistrettoPoint::multiscalar_mul::<12, _, _>(...). Basically the compiler can't inferconst N.This change isn't exactly small and wasn't discussed beforehand. I'm fully prepared to consider any large changes to implement this feature.