-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathpoint_mul_multi.go
117 lines (97 loc) · 2.89 KB
/
point_mul_multi.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
// Copyright (c) 2023 Yawning Angel
//
// SPDX-License-Identifier: BSD-3-Clause
package secp256k1
// These are useful for hipster crypto such as batch Schnorr verification,
// Bulletproofs, MuSig2, etc.
//
// Notes:
// - The Vartime variant is currently an extremely marginal gain on amd64
// due to the vectorized constant time table lookup. However the gain
// is more substantial on other platforms, and splitting the API allows
// optimizing each variant independently in the future without breaking
// downstream code.
// - Pippenger's algorithm is superior to Straus's for large batches,
// but this is significantly easier to understand, and it's also always
// a gain.
// - The appropriate single ScalarMult is called if the size of the batch
// is 1, to leverage GLV decomposition. Decomposition is not worth it
// in the other cases (GLV has overhead that scales with batch-size,
// while the amount of work saved is fixed).
// MultiScalarMult sets `v = sum(scalars[i] * points[i])`, and returns `v`.
func (v *Point) MultiScalarMult(scalars []*Scalar, points []*Point) *Point { //nolint:dupl
l := len(scalars)
if l != len(points) {
panic("secp256k1: len(scalars) != len(points)")
}
if l == 1 {
return v.ScalarMult(scalars[0], points[0])
}
pTbls := make([]projectivePointMultTable, l)
sBytes := make([][ScalarSize]byte, l)
for i := 0; i < l; i++ {
pTbls[i] = newProjectivePointMultTable(points[i])
scalars[i].getBytes(&sBytes[i])
}
v.Identity()
for i := 0; i < ScalarSize; i++ {
if i != 0 {
v.doubleComplete(v)
v.doubleComplete(v)
v.doubleComplete(v)
v.doubleComplete(v)
}
for j := 0; j < l; j++ {
b := sBytes[j][i]
pTbls[j].SelectAndAdd(v, uint64(b>>4))
}
v.doubleComplete(v)
v.doubleComplete(v)
v.doubleComplete(v)
v.doubleComplete(v)
for j := 0; j < l; j++ {
b := sBytes[j][i]
pTbls[j].SelectAndAdd(v, uint64(b&0xf))
}
}
return v
}
// MultiScalarMultVartime sets `v = sum(scalars[i] * points[i])`, and returns
// `v` in variable time.
func (v *Point) MultiScalarMultVartime(scalars []*Scalar, points []*Point) *Point { //nolint:dupl
l := len(scalars)
if l != len(points) {
panic("secp256k1: len(scalars) != len(points)")
}
if l == 1 {
return v.scalarMultVartimeGLV(scalars[0], points[0])
}
pTbls := make([]projectivePointMultTable, l)
sBytes := make([][ScalarSize]byte, l)
for i := 0; i < l; i++ {
pTbls[i] = newProjectivePointMultTable(points[i])
scalars[i].getBytes(&sBytes[i])
}
v.Identity()
for i := 0; i < ScalarSize; i++ {
if i != 0 {
v.doubleComplete(v)
v.doubleComplete(v)
v.doubleComplete(v)
v.doubleComplete(v)
}
for j := 0; j < l; j++ {
b := sBytes[j][i]
pTbls[j].SelectAndAddVartime(v, uint64(b>>4))
}
v.doubleComplete(v)
v.doubleComplete(v)
v.doubleComplete(v)
v.doubleComplete(v)
for j := 0; j < l; j++ {
b := sBytes[j][i]
pTbls[j].SelectAndAddVartime(v, uint64(b&0xf))
}
}
return v
}