secp256k1: Optimize precomp values to use affine.
This optimizes the pre-computed byte points used to accelerate scalar base multiplication to store the data in affine coordinates instead of Jacobian coordinates which reduces the memory usage requirement to 66% of what it current requires and also has the important benefit of further speeding up the computation. This is the case because projecting affine coordinates into Jacobian space is essentially free and the point doubling and addition routines have optimizations which allow them to avoid additional operations when the Z coordinate is 1, which is the case for an initial affine projection. Further, since the compressed table is stored in the string table of the binary, it also reduces the size the of final binary by ~385KiB. The following benchmark shows a before and after comparison of scalar base multiplication as well as how that translates to signature verification: name old time/op new time/op delta ------------------------------------------------------------------------- ScalarBaseMult 34.5µs ± 1% 24.7µs ± 1% -28.43% (p=0.008 n=5+5) ScalarBaseMultLarge 48.2µs ± 1% 38.0µs ± 1% -21.08% (p=0.008 n=5+5) SigVerify 181µs ± 5% 163µs ± 2% -9.86% (p=0.008 n=5+5) While 18 µs less per signature verification might not seem like much on the surface, consider that every transaction requires at least one signature operation, so there are a ton of them when doing no checkpoint syncs. For a concrete number, verifying 100 million signatures would take 30 minutes less time.
This commit is contained in:
parent
8cef307d9e
commit
fdfae1afd4
File diff suppressed because one or more lines are too long
@ -763,7 +763,7 @@ func ScalarBaseMultNonConst(k *ModNScalar, result *JacobianPoint) {
|
||||
p := bytePoints[i][byteVal]
|
||||
pt.X.Set(&p[0])
|
||||
pt.Y.Set(&p[1])
|
||||
pt.Z.Set(&p[2])
|
||||
pt.Z.SetInt(1)
|
||||
AddNonConst(&q, &pt, &q)
|
||||
}
|
||||
|
||||
|
||||
@ -38,33 +38,32 @@ func SerializedBytePoints() []byte {
|
||||
|
||||
// Separate the bits into byte-sized windows.
|
||||
curveByteSize := curveParams.BitSize / 8
|
||||
serialized := make([]byte, curveByteSize*256*3*10*4)
|
||||
serialized := make([]byte, curveByteSize*256*2*10*4)
|
||||
offset := 0
|
||||
for byteNum := 0; byteNum < curveByteSize; byteNum++ {
|
||||
// Grab the 8 bits that make up this byte from doublingPoints.
|
||||
// Grab the 8 bits that make up this byte from doubling points.
|
||||
startingBit := 8 * (curveByteSize - byteNum - 1)
|
||||
computingPoints := doublingPoints[startingBit : startingBit+8]
|
||||
windowPoints := doublingPoints[startingBit : startingBit+8]
|
||||
|
||||
// Compute all points in this window and serialize them.
|
||||
// Compute all points in this window, convert them to affine, and
|
||||
// serialize them.
|
||||
for i := 0; i < 256; i++ {
|
||||
var point JacobianPoint
|
||||
for j := 0; j < 8; j++ {
|
||||
if i>>uint(j)&1 == 1 {
|
||||
AddNonConst(&point, &computingPoints[j], &point)
|
||||
for bit := 0; bit < 8; bit++ {
|
||||
if i>>uint(bit)&1 == 1 {
|
||||
AddNonConst(&point, &windowPoints[bit], &point)
|
||||
}
|
||||
}
|
||||
for i := 0; i < 10; i++ {
|
||||
point.ToAffine()
|
||||
|
||||
for i := 0; i < len(point.X.n); i++ {
|
||||
binary.LittleEndian.PutUint32(serialized[offset:], point.X.n[i])
|
||||
offset += 4
|
||||
}
|
||||
for i := 0; i < 10; i++ {
|
||||
for i := 0; i < len(point.Y.n); i++ {
|
||||
binary.LittleEndian.PutUint32(serialized[offset:], point.Y.n[i])
|
||||
offset += 4
|
||||
}
|
||||
for i := 0; i < 10; i++ {
|
||||
binary.LittleEndian.PutUint32(serialized[offset:], point.Z.n[i])
|
||||
offset += 4
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -18,7 +18,7 @@ import (
|
||||
|
||||
// bytePointTable describes a table used to house pre-computed values for
|
||||
// accelerating scalar base multiplication.
|
||||
type bytePointTable [32][256][3]FieldVal
|
||||
type bytePointTable [32][256][2]FieldVal
|
||||
|
||||
// s256BytePoints houses pre-computed values used to accelerate scalar base
|
||||
// multiplication such that they are only loaded on first use.
|
||||
@ -64,7 +64,6 @@ var s256BytePoints = func() func() *bytePointTable {
|
||||
for i := 0; i < len(bytePoints[byteNum]); i++ {
|
||||
px := &bytePoints[byteNum][i][0]
|
||||
py := &bytePoints[byteNum][i][1]
|
||||
pz := &bytePoints[byteNum][i][2]
|
||||
for i := 0; i < len(px.n); i++ {
|
||||
px.n[i] = binary.LittleEndian.Uint32(serialized[offset:])
|
||||
offset += 4
|
||||
@ -73,10 +72,6 @@ var s256BytePoints = func() func() *bytePointTable {
|
||||
py.n[i] = binary.LittleEndian.Uint32(serialized[offset:])
|
||||
offset += 4
|
||||
}
|
||||
for i := 0; i < len(pz.n); i++ {
|
||||
pz.n[i] = binary.LittleEndian.Uint32(serialized[offset:])
|
||||
offset += 4
|
||||
}
|
||||
}
|
||||
}
|
||||
data = &bytePoints
|
||||
|
||||
Loading…
Reference in New Issue
Block a user