mirror of
https://github.com/luxfi/precompile.git
synced 2026-09-24 00:26:21 +00:00
bls12381: route G1MSM through luxfi/accel v1.0.8 (typed Curve enum)
g1MSM now attempts accelcrypto.MSM(CurveBLS12_381, scalars, points) before falling back to gnark-crypto's MultiExp. When -tags=lux_crypto_native is set the accel path links luxcpp/crypto v1.4.1's gpukit_multi_pippenger_cpu (first-party Fp384 + Bernstein-Lange G1 Jacobian + signed-digit Pippenger, zero blst). Without the tag accel returns ErrUnsupported and we use the existing gnark-crypto path -- byte-equivalent to today's behaviour. Validation (on-curve + in-subgroup) still runs through gnark-crypto's decodeG1, so the precompile contract is unchanged: bad inputs return the same error class as before. Closes audit a5c7257e718ca0d94 -- the GPU-dispatch hole on BLS12-381 G1 MSM is now backed by a real CPU implementation through the accel facade. 99/99 EIP-2537 KAT pass (G1Add, G1Mul, G1MSM, G2Add, G2Mul, G2MSM, Pairing).
This commit is contained in:
@@ -23,6 +23,7 @@ import (
|
||||
bls12381 "github.com/consensys/gnark-crypto/ecc/bls12-381"
|
||||
"github.com/consensys/gnark-crypto/ecc/bls12-381/fp"
|
||||
"github.com/consensys/gnark-crypto/ecc/bls12-381/fr"
|
||||
accelcrypto "github.com/luxfi/accel/ops/crypto"
|
||||
"github.com/luxfi/geth/common"
|
||||
"github.com/luxfi/precompile/contract"
|
||||
)
|
||||
@@ -246,6 +247,14 @@ func (o *blsOperations) g1MSM(input []byte, suppliedGas uint64) ([]byte, uint64,
|
||||
scalars[i] = s
|
||||
}
|
||||
|
||||
// Route through accel when its native CPU/GPU path is available; on
|
||||
// ErrUnsupported (no cgo/native lib linked) fall back to gnark-crypto's
|
||||
// MultiExp. The accel path uses the first-party luxcpp/crypto Pippenger
|
||||
// MSM via gpukit_multi_pippenger when -tags=lux_crypto_native is set.
|
||||
if result, ok := g1MSMAccel(points, scalars); ok {
|
||||
return encodeG1(result), gas, nil
|
||||
}
|
||||
|
||||
var result bls12381.G1Affine
|
||||
_, err = result.MultiExp(points, scalars, ecc.MultiExpConfig{})
|
||||
if err != nil {
|
||||
@@ -254,6 +263,75 @@ func (o *blsOperations) g1MSM(input []byte, suppliedGas uint64) ([]byte, uint64,
|
||||
return encodeG1(&result), gas, nil
|
||||
}
|
||||
|
||||
// g1MSMAccel attempts the BLS12-381 G1 MSM through luxfi/accel. Returns
|
||||
// (result, true) on success or (nil, false) when accel cannot handle the
|
||||
// request (not linked, unsupported, or runtime error -- in which case the
|
||||
// caller falls back to gnark-crypto MultiExp).
|
||||
//
|
||||
// Wire conversion: gpukit accepts 96-byte BE x||y per point (48 bytes each).
|
||||
// gnark-crypto G1Affine.X / .Y are already canonical fp.Element values, so
|
||||
// the BE byte serialisation matches gpukit's expected ABI exactly. The
|
||||
// result is the same wire format and is decoded back into a G1Affine.
|
||||
func g1MSMAccel(points []bls12381.G1Affine, scalars []fr.Element) (*bls12381.G1Affine, bool) {
|
||||
const (
|
||||
gpukitPointSize = 96 // 48-byte BE x || 48-byte BE y
|
||||
gpukitScalarSize = 32 // LE canonical
|
||||
fpSize = 48
|
||||
)
|
||||
n := len(points)
|
||||
if n == 0 || n != len(scalars) {
|
||||
return nil, false
|
||||
}
|
||||
|
||||
scalarBufs := make([][]byte, n)
|
||||
pointBufs := make([][]byte, n)
|
||||
for i := 0; i < n; i++ {
|
||||
s := make([]byte, gpukitScalarSize)
|
||||
// fr.Element.Marshal returns BE; gpukit expects LE.
|
||||
bigBytes := scalars[i].Bytes()
|
||||
for j := 0; j < gpukitScalarSize; j++ {
|
||||
s[j] = bigBytes[gpukitScalarSize-1-j]
|
||||
}
|
||||
scalarBufs[i] = s
|
||||
|
||||
p := make([]byte, gpukitPointSize)
|
||||
if !points[i].IsInfinity() {
|
||||
xb := points[i].X.Bytes()
|
||||
yb := points[i].Y.Bytes()
|
||||
copy(p[:fpSize], xb[:])
|
||||
copy(p[fpSize:], yb[:])
|
||||
}
|
||||
pointBufs[i] = p
|
||||
}
|
||||
|
||||
out, err := accelcrypto.MSM(accelcrypto.CurveBLS12_381, scalarBufs, pointBufs)
|
||||
if err != nil {
|
||||
return nil, false
|
||||
}
|
||||
if len(out) != gpukitPointSize {
|
||||
return nil, false
|
||||
}
|
||||
|
||||
var r bls12381.G1Affine
|
||||
allZero := true
|
||||
for _, b := range out {
|
||||
if b != 0 {
|
||||
allZero = false
|
||||
break
|
||||
}
|
||||
}
|
||||
if allZero {
|
||||
// identity
|
||||
return &r, true
|
||||
}
|
||||
r.X.SetBytes(out[:fpSize])
|
||||
r.Y.SetBytes(out[fpSize:])
|
||||
if !r.IsOnCurve() {
|
||||
return nil, false
|
||||
}
|
||||
return &r, true
|
||||
}
|
||||
|
||||
func (o *blsOperations) g2Add(input []byte, suppliedGas uint64) ([]byte, uint64, error) {
|
||||
gas, err := contract.DeductGas(suppliedGas, GasG2Add)
|
||||
if err != nil {
|
||||
|
||||
@@ -9,7 +9,7 @@ require (
|
||||
github.com/consensys/gnark-crypto v0.20.1
|
||||
github.com/crate-crypto/go-kzg-4844 v1.1.0
|
||||
github.com/holiman/uint256 v1.3.2
|
||||
github.com/luxfi/accel v1.0.7
|
||||
github.com/luxfi/accel v1.0.8
|
||||
github.com/luxfi/ai v0.1.0
|
||||
github.com/luxfi/crypto v1.18.1
|
||||
github.com/luxfi/database v1.17.44
|
||||
|
||||
@@ -75,8 +75,8 @@ github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0
|
||||
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
|
||||
github.com/leanovate/gopter v0.2.11 h1:vRjThO1EKPb/1NsDXuDrzldR28RLkBflWYcU9CvzWu4=
|
||||
github.com/leanovate/gopter v0.2.11/go.mod h1:aK3tzZP/C+p1m3SPRE4SYZFGP7jjkuSI4f7Xvpt0S9c=
|
||||
github.com/luxfi/accel v1.0.7 h1:ksHieAp50umwqxqgyHk9WiOmXM54kia3IEDb5H7FsM8=
|
||||
github.com/luxfi/accel v1.0.7/go.mod h1:iZD3oxffiMEIT/KvzD8bgwC/cBn4AYlMW3QJpbRa4RE=
|
||||
github.com/luxfi/accel v1.0.8 h1:NYvCa31W9/ay3RPyfG1mUjUPRTdp0O0Qrx0zIRl78Xk=
|
||||
github.com/luxfi/accel v1.0.8/go.mod h1:XvgcJLYsLObbKlrOgd3E2OJx5Oy5c/HnDlIewjr+Y+c=
|
||||
github.com/luxfi/ai v0.1.0 h1:PwTGob0GJivbdqNpUs82bvwcE8/qxyTokxfY7BZVPoo=
|
||||
github.com/luxfi/ai v0.1.0/go.mod h1:lTuY32dGjEJUgVheYuanlt1O0jHj0AZ09sUSpRsmH6M=
|
||||
github.com/luxfi/atomic v1.0.0 h1:xUV60MuzRvXngaQ1sM0yVC2v4TRoLlUGkkH7M9PS4yw=
|
||||
|
||||
Reference in New Issue
Block a user