Files
zeekay ca0bf6b303 precompile: seven packages to 96-99%, from lanes killed before they could commit
slhdsa 97.9 · mlkem 97.4 · mldsa 96.8 · xwing 96.2 · bls12381 97.2 ·
kzg4844 97.8 · v3 98.7. All build, vet and gofmt clean, all green under
-count=1, and preserved here rather than lost to a session limit.

v3's lane did report before it died: 78.1% to 98.7%, 70 of 70 mutations caught,
four of which survived a first pass and needed tests written. The two that
matter are rounding: burn rounding principal UP, and fee growth rounding UP,
neither of which any behaviour test could see -- a Q128.128 off-by-one is
2^-128 of a token and floors to zero through every payout path, so it took an
exact-value assertion. It also found that a full drain of a pool with
overlapping positions REVERTS rather than paying out, because deposits round
down once per position while payouts round down once per step over combined
liquidity, and floor(a+b) >= floor(a)+floor(b). Custody holds; not one unbacked
wei leaves. Liveness, not theft.

The other six carry no report -- their lanes were killed mid-run. Build, vet,
gofmt and the suite are all I can vouch for.
2026-08-28 13:27:00 -07:00

655 lines
19 KiB
Go

// Copyright (C) 2025, Lux Industries Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package bls12381
import (
"math/big"
"testing"
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"
"github.com/luxfi/geth/common"
"github.com/luxfi/precompile/contract"
"github.com/luxfi/precompile/modules"
"github.com/stretchr/testify/require"
)
// ===========================================================================
// Module registration: all 7 addresses must be registered
// ===========================================================================
func TestAllModulesRegistered(t *testing.T) {
tests := []struct {
name string
addr common.Address
key string
}{
{"G1Add", G1AddAddress, G1AddConfigKey},
{"G1Mul", G1MulAddress, G1MulConfigKey},
{"G1MSM", G1MSMAddress, G1MSMConfigKey},
{"G2Add", G2AddAddress, G2AddConfigKey},
{"G2Mul", G2MulAddress, G2MulConfigKey},
{"G2MSM", G2MSMAddress, G2MSMConfigKey},
{"Pairing", PairingAddress, PairingConfigKey},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
m, ok := modules.GetPrecompileModuleByAddress(tt.addr)
require.True(t, ok, "module not registered at address %s", tt.addr.Hex())
require.Equal(t, tt.key, m.ConfigKey)
})
}
}
func TestAddressValues(t *testing.T) {
require.Equal(t, common.HexToAddress("0x0b"), G1AddAddress)
require.Equal(t, common.HexToAddress("0x0c"), G1MulAddress)
require.Equal(t, common.HexToAddress("0x0d"), G1MSMAddress)
require.Equal(t, common.HexToAddress("0x0e"), G2AddAddress)
require.Equal(t, common.HexToAddress("0x0f"), G2MulAddress)
require.Equal(t, common.HexToAddress("0x10"), G2MSMAddress)
require.Equal(t, common.HexToAddress("0x11"), PairingAddress)
}
// ===========================================================================
// Helpers
// ===========================================================================
func g1Generator() bls12381.G1Affine {
_, _, g1, _ := bls12381.Generators()
return g1
}
func g2Generator() bls12381.G2Affine {
_, _, _, g2 := bls12381.Generators()
return g2
}
func encodeG1Pair(a, b *bls12381.G1Affine) []byte {
out := make([]byte, 2*G1PointLen)
copy(out[:G1PointLen], encodeG1(a))
copy(out[G1PointLen:], encodeG1(b))
return out
}
func encodeG2Pair(a, b *bls12381.G2Affine) []byte {
out := make([]byte, 2*G2PointLen)
copy(out[:G2PointLen], encodeG2(a))
copy(out[G2PointLen:], encodeG2(b))
return out
}
func encodePointScalar(pt []byte, s *fr.Element) []byte {
out := make([]byte, len(pt)+ScalarLen)
copy(out, pt)
sBytes := s.Bytes()
copy(out[len(pt):], sBytes[:])
return out
}
func infinityG1() []byte {
return make([]byte, G1PointLen)
}
func infinityG2() []byte {
return make([]byte, G2PointLen)
}
func runPrecompile(addr common.Address, input []byte, gas uint64) ([]byte, uint64, error) {
m, ok := modules.GetPrecompileModuleByAddress(addr)
if !ok {
return nil, gas, ErrInvalidInput
}
return m.Contract.Run(nil, common.Address{}, addr, input, gas, false)
}
// ===========================================================================
// G1Add (0x0B)
// ===========================================================================
func TestG1Add_GeneratorPlusGenerator(t *testing.T) {
g := g1Generator()
input := encodeG1Pair(&g, &g)
result, gas, err := runPrecompile(G1AddAddress, input, 10000)
require.NoError(t, err)
require.Equal(t, uint64(10000-GasG1Add), gas)
require.Len(t, result, G1PointLen)
// g + g = 2*g
var expected bls12381.G1Affine
expected.Add(&g, &g)
require.Equal(t, encodeG1(&expected), result)
}
func TestG1Add_Identity(t *testing.T) {
g := g1Generator()
input := make([]byte, 2*G1PointLen)
copy(input[:G1PointLen], infinityG1())
copy(input[G1PointLen:], encodeG1(&g))
result, _, err := runPrecompile(G1AddAddress, input, 10000)
require.NoError(t, err)
require.Equal(t, encodeG1(&g), result)
}
func TestG1Add_InsufficientGas(t *testing.T) {
g := g1Generator()
input := encodeG1Pair(&g, &g)
_, _, err := runPrecompile(G1AddAddress, input, GasG1Add-1)
require.Error(t, err)
}
func TestG1Add_ShortInput(t *testing.T) {
_, _, err := runPrecompile(G1AddAddress, make([]byte, G1PointLen-1), 10000)
require.Error(t, err)
}
// ===========================================================================
// G1Mul (0x0C)
// ===========================================================================
func TestG1Mul_ScalarOne(t *testing.T) {
g := g1Generator()
var one fr.Element
one.SetOne()
input := encodePointScalar(encodeG1(&g), &one)
result, gas, err := runPrecompile(G1MulAddress, input, 100000)
require.NoError(t, err)
require.Equal(t, uint64(100000-GasG1Mul), gas)
require.Equal(t, encodeG1(&g), result)
}
func TestG1Mul_ScalarTwo(t *testing.T) {
g := g1Generator()
var two fr.Element
two.SetUint64(2)
input := encodePointScalar(encodeG1(&g), &two)
result, _, err := runPrecompile(G1MulAddress, input, 100000)
require.NoError(t, err)
var expected bls12381.G1Affine
expected.ScalarMultiplication(&g, big.NewInt(2))
require.Equal(t, encodeG1(&expected), result)
}
func TestG1Mul_ScalarZero(t *testing.T) {
g := g1Generator()
var zero fr.Element // zero-initialized
input := encodePointScalar(encodeG1(&g), &zero)
result, _, err := runPrecompile(G1MulAddress, input, 100000)
require.NoError(t, err)
// Scalar 0 * G = infinity
var inf bls12381.G1Affine // zero = infinity
require.Equal(t, encodeG1(&inf), result)
}
func TestG1Mul_InsufficientGas(t *testing.T) {
g := g1Generator()
var one fr.Element
one.SetOne()
input := encodePointScalar(encodeG1(&g), &one)
_, _, err := runPrecompile(G1MulAddress, input, GasG1Mul-1)
require.Error(t, err)
}
// ===========================================================================
// G1MSM (0x0D)
// ===========================================================================
func TestG1MSM_TwoPairs(t *testing.T) {
g := g1Generator()
var s1, s2 fr.Element
s1.SetUint64(3)
s2.SetUint64(5)
input := make([]byte, 0, 2*(G1PointLen+ScalarLen))
input = append(input, encodePointScalar(encodeG1(&g), &s1)...)
input = append(input, encodePointScalar(encodeG1(&g), &s2)...)
result, _, err := runPrecompile(G1MSMAddress, input, 1000000)
require.NoError(t, err)
// 3*G + 5*G = 8*G
var expected bls12381.G1Affine
expected.ScalarMultiplication(&g, big.NewInt(8))
require.Equal(t, encodeG1(&expected), result)
}
func TestG1MSM_SinglePair(t *testing.T) {
g := g1Generator()
var s fr.Element
s.SetUint64(7)
input := encodePointScalar(encodeG1(&g), &s)
result, gas, err := runPrecompile(G1MSMAddress, input, 100000)
require.NoError(t, err)
// Single pair: no discount, base gas
require.Equal(t, uint64(100000-GasG1MSM), gas)
var expected bls12381.G1Affine
expected.ScalarMultiplication(&g, big.NewInt(7))
require.Equal(t, encodeG1(&expected), result)
}
func TestG1MSM_GasDiscount(t *testing.T) {
g := g1Generator()
var s fr.Element
s.SetUint64(1)
// 4 pairs should get discount (949/1000)
input := make([]byte, 0, 4*(G1PointLen+ScalarLen))
for range 4 {
input = append(input, encodePointScalar(encodeG1(&g), &s)...)
}
expectedGas := G1MSMGas(4)
require.Less(t, expectedGas, uint64(4*GasG1MSM)) // discount applied
_, gas, err := runPrecompile(G1MSMAddress, input, 1000000)
require.NoError(t, err)
require.Equal(t, uint64(1000000)-expectedGas, gas)
}
func TestG1MSM_InvalidLength(t *testing.T) {
// Not a multiple of pairSize
_, _, err := runPrecompile(G1MSMAddress, make([]byte, G1PointLen+ScalarLen+1), 1000000)
require.Error(t, err)
}
func TestG1MSM_Empty(t *testing.T) {
_, _, err := runPrecompile(G1MSMAddress, []byte{}, 1000000)
require.Error(t, err)
}
// ===========================================================================
// G2Add (0x0E)
// ===========================================================================
func TestG2Add_GeneratorPlusGenerator(t *testing.T) {
g := g2Generator()
input := encodeG2Pair(&g, &g)
result, gas, err := runPrecompile(G2AddAddress, input, 10000)
require.NoError(t, err)
require.Equal(t, uint64(10000-GasG2Add), gas)
var expected bls12381.G2Affine
expected.Add(&g, &g)
require.Equal(t, encodeG2(&expected), result)
}
func TestG2Add_Identity(t *testing.T) {
g := g2Generator()
input := make([]byte, 2*G2PointLen)
copy(input[:G2PointLen], infinityG2())
copy(input[G2PointLen:], encodeG2(&g))
result, _, err := runPrecompile(G2AddAddress, input, 10000)
require.NoError(t, err)
require.Equal(t, encodeG2(&g), result)
}
func TestG2Add_ShortInput(t *testing.T) {
_, _, err := runPrecompile(G2AddAddress, make([]byte, G2PointLen-1), 10000)
require.Error(t, err)
}
// ===========================================================================
// G2Mul (0x0F)
// ===========================================================================
func TestG2Mul_ScalarOne(t *testing.T) {
g := g2Generator()
var one fr.Element
one.SetOne()
input := encodePointScalar(encodeG2(&g), &one)
result, gas, err := runPrecompile(G2MulAddress, input, 100000)
require.NoError(t, err)
require.Equal(t, uint64(100000-GasG2Mul), gas)
require.Equal(t, encodeG2(&g), result)
}
func TestG2Mul_ScalarTwo(t *testing.T) {
g := g2Generator()
var two fr.Element
two.SetUint64(2)
input := encodePointScalar(encodeG2(&g), &two)
result, _, err := runPrecompile(G2MulAddress, input, 100000)
require.NoError(t, err)
var expected bls12381.G2Affine
expected.ScalarMultiplication(&g, big.NewInt(2))
require.Equal(t, encodeG2(&expected), result)
}
func TestG2Mul_InsufficientGas(t *testing.T) {
g := g2Generator()
var one fr.Element
one.SetOne()
input := encodePointScalar(encodeG2(&g), &one)
_, _, err := runPrecompile(G2MulAddress, input, GasG2Mul-1)
require.Error(t, err)
}
// ===========================================================================
// G2MSM (0x10)
// ===========================================================================
func TestG2MSM_TwoPairs(t *testing.T) {
g := g2Generator()
var s1, s2 fr.Element
s1.SetUint64(3)
s2.SetUint64(5)
input := make([]byte, 0, 2*(G2PointLen+ScalarLen))
input = append(input, encodePointScalar(encodeG2(&g), &s1)...)
input = append(input, encodePointScalar(encodeG2(&g), &s2)...)
result, _, err := runPrecompile(G2MSMAddress, input, 1000000)
require.NoError(t, err)
var expected bls12381.G2Affine
expected.ScalarMultiplication(&g, big.NewInt(8))
require.Equal(t, encodeG2(&expected), result)
}
// ===========================================================================
// Pairing (0x11)
// ===========================================================================
func TestPairing_TrivialCheck(t *testing.T) {
// e(G1, G2) * e(-G1, G2) = 1 (pairing check should return true)
g1 := g1Generator()
g2 := g2Generator()
var negG1 bls12381.G1Affine
negG1.Neg(&g1)
input := make([]byte, 2*PairingPair)
copy(input[0:G1PointLen], encodeG1(&g1))
copy(input[G1PointLen:PairingPair], encodeG2(&g2))
copy(input[PairingPair:PairingPair+G1PointLen], encodeG1(&negG1))
copy(input[PairingPair+G1PointLen:2*PairingPair], encodeG2(&g2))
result, gas, err := runPrecompile(PairingAddress, input, 1000000)
require.NoError(t, err)
// Gas: 65000 + 43000*2 = 151000
expectedGas := uint64(GasPairingBase + 2*GasPairingPerPair)
require.Equal(t, uint64(1000000)-expectedGas, gas)
// Result should be 1 (pairing check passes)
require.Len(t, result, 32)
require.Equal(t, byte(1), result[31])
}
func TestPairing_SinglePairFails(t *testing.T) {
// e(G1, G2) != 1 for non-trivial pair
g1 := g1Generator()
g2 := g2Generator()
input := make([]byte, PairingPair)
copy(input[0:G1PointLen], encodeG1(&g1))
copy(input[G1PointLen:PairingPair], encodeG2(&g2))
result, gas, err := runPrecompile(PairingAddress, input, 1000000)
require.NoError(t, err)
// Gas: 65000 + 43000*1 = 108000
expectedGas := uint64(GasPairingBase + GasPairingPerPair)
require.Equal(t, uint64(1000000)-expectedGas, gas)
// Result should be 0 (pairing check fails: e(G1,G2) != 1)
require.Equal(t, byte(0), result[31])
}
func TestPairing_EmptyInput(t *testing.T) {
_, _, err := runPrecompile(PairingAddress, []byte{}, 1000000)
require.ErrorIs(t, err, ErrInvalidPairingLen)
}
func TestPairing_InvalidLength(t *testing.T) {
_, _, err := runPrecompile(PairingAddress, make([]byte, PairingPair+1), 1000000)
require.ErrorIs(t, err, ErrInvalidPairingLen)
}
func TestPairing_InsufficientGas(t *testing.T) {
g1 := g1Generator()
g2 := g2Generator()
input := make([]byte, PairingPair)
copy(input[0:G1PointLen], encodeG1(&g1))
copy(input[G1PointLen:PairingPair], encodeG2(&g2))
need := PairingGas(len(input))
_, _, err := runPrecompile(PairingAddress, input, need-1)
require.ErrorIs(t, err, contract.ErrOutOfGas)
_, remaining, err := runPrecompile(PairingAddress, input, need)
require.NoError(t, err)
require.Zero(t, remaining)
}
func TestPairing_WithInfinity(t *testing.T) {
// e(0, G2) = 1, so a single pair with infinity G1 should pass
g2 := g2Generator()
input := make([]byte, PairingPair)
copy(input[:G1PointLen], infinityG1())
copy(input[G1PointLen:], encodeG2(&g2))
result, _, err := runPrecompile(PairingAddress, input, 1000000)
require.NoError(t, err)
require.Equal(t, byte(1), result[31])
}
// ===========================================================================
// Gas accounting
// ===========================================================================
// ===========================================================================
// Invalid inputs (not on curve, not in subgroup, bad padding)
// ===========================================================================
func TestG1Add_NotOnCurve(t *testing.T) {
// Point with valid padding but not on curve
input := make([]byte, 2*G1PointLen)
// Set x=1, y=1 (not on BLS12-381 curve)
input[63] = 1 // x = 1
input[127] = 1 // y = 1
_, _, err := runPrecompile(G1AddAddress, input, 10000)
require.Error(t, err)
}
func TestG1Add_BadPadding(t *testing.T) {
g := g1Generator()
input := encodeG1Pair(&g, &g)
// Corrupt the padding byte (first 16 bytes should be zero)
input[0] = 0xFF
_, _, err := runPrecompile(G1AddAddress, input, 10000)
require.ErrorIs(t, err, ErrInvalidFieldElem)
}
func TestG2Add_BadPadding(t *testing.T) {
g := g2Generator()
input := encodeG2Pair(&g, &g)
input[0] = 0xFF
_, _, err := runPrecompile(G2AddAddress, input, 10000)
require.ErrorIs(t, err, ErrInvalidFieldElem)
}
// ===========================================================================
// Encode/decode roundtrip
// ===========================================================================
func TestG1EncodeDecodeRoundtrip(t *testing.T) {
g := g1Generator()
encoded := encodeG1(&g)
decoded, err := decodeG1(encoded)
require.NoError(t, err)
require.True(t, g.Equal(&decoded))
}
func TestG2EncodeDecodeRoundtrip(t *testing.T) {
g := g2Generator()
encoded := encodeG2(&g)
decoded, err := decodeG2(encoded)
require.NoError(t, err)
require.True(t, g.Equal(&decoded))
}
func TestInfinityG1Roundtrip(t *testing.T) {
inf := infinityG1()
decoded, err := decodeG1(inf)
require.NoError(t, err)
require.True(t, decoded.IsInfinity())
}
func TestInfinityG2Roundtrip(t *testing.T) {
inf := infinityG2()
decoded, err := decodeG2(inf)
require.NoError(t, err)
require.True(t, decoded.IsInfinity())
}
// ===========================================================================
// Field element validation
// ===========================================================================
func TestDecodeG1_FieldElementTooLarge(t *testing.T) {
// Create input where x coordinate exceeds the field modulus.
// The BLS12-381 Fp modulus is:
// 0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab
// We put a value >= modulus in the x coordinate.
input := make([]byte, G1PointLen)
// Set x to be larger than modulus: 0xFF...FF in the valid region
for i := 16; i < 64; i++ {
input[i] = 0xFF
}
// y = 0 (valid padding)
_, err := decodeG1(input)
// gnark-crypto's SetBytes does modular reduction, so this won't fail at decode.
// But the resulting point won't be on the curve.
require.Error(t, err) // Either not on curve or not in subgroup
}
// ===========================================================================
// Cross-operation consistency
// ===========================================================================
func TestG1Mul_EqualsRepeatedAdd(t *testing.T) {
// 3*G via mul should equal G+G+G via add
g := g1Generator()
var three fr.Element
three.SetUint64(3)
input := encodePointScalar(encodeG1(&g), &three)
mulResult, _, err := runPrecompile(G1MulAddress, input, 100000)
require.NoError(t, err)
// G + G
addInput := encodeG1Pair(&g, &g)
twoG, _, err := runPrecompile(G1AddAddress, addInput, 10000)
require.NoError(t, err)
// 2G + G
var twoGPt bls12381.G1Affine
twoGPt, err = decodeG1(twoG)
require.NoError(t, err)
addInput2 := encodeG1Pair(&twoGPt, &g)
addResult, _, err := runPrecompile(G1AddAddress, addInput2, 10000)
require.NoError(t, err)
require.Equal(t, mulResult, addResult)
}
func TestG2Mul_EqualsRepeatedAdd(t *testing.T) {
g := g2Generator()
var three fr.Element
three.SetUint64(3)
input := encodePointScalar(encodeG2(&g), &three)
mulResult, _, err := runPrecompile(G2MulAddress, input, 100000)
require.NoError(t, err)
addInput := encodeG2Pair(&g, &g)
twoG, _, err := runPrecompile(G2AddAddress, addInput, 10000)
require.NoError(t, err)
var twoGPt bls12381.G2Affine
twoGPt, err = decodeG2(twoG)
require.NoError(t, err)
addInput2 := encodeG2Pair(&twoGPt, &g)
addResult, _, err := runPrecompile(G2AddAddress, addInput2, 10000)
require.NoError(t, err)
require.Equal(t, mulResult, addResult)
}
// ===========================================================================
// Benchmarks
// ===========================================================================
func BenchmarkG1Add(b *testing.B) {
g := g1Generator()
input := encodeG1Pair(&g, &g)
b.ResetTimer()
for i := 0; i < b.N; i++ {
runPrecompile(G1AddAddress, input, 10000)
}
}
func BenchmarkG1Mul(b *testing.B) {
g := g1Generator()
var s fr.Element
s.SetUint64(12345)
input := encodePointScalar(encodeG1(&g), &s)
b.ResetTimer()
for i := 0; i < b.N; i++ {
runPrecompile(G1MulAddress, input, 100000)
}
}
func BenchmarkG2Mul(b *testing.B) {
g := g2Generator()
var s fr.Element
s.SetUint64(12345)
input := encodePointScalar(encodeG2(&g), &s)
b.ResetTimer()
for i := 0; i < b.N; i++ {
runPrecompile(G2MulAddress, input, 100000)
}
}
func BenchmarkPairing_1Pair(b *testing.B) {
g1 := g1Generator()
g2 := g2Generator()
var negG1 bls12381.G1Affine
negG1.Neg(&g1)
input := make([]byte, 2*PairingPair)
copy(input[0:G1PointLen], encodeG1(&g1))
copy(input[G1PointLen:PairingPair], encodeG2(&g2))
copy(input[PairingPair:PairingPair+G1PointLen], encodeG1(&negG1))
copy(input[PairingPair+G1PointLen:], encodeG2(&g2))
b.ResetTimer()
for i := 0; i < b.N; i++ {
runPrecompile(PairingAddress, input, 1000000)
}
}
// Verify fp.Element padding constraint via a quick sanity check.
func TestFpModulusFits48Bytes(t *testing.T) {
// BLS12-381 Fp modulus is 381 bits = 48 bytes.
// The first 16 bytes of the 64-byte padded encoding must be zero.
var one fp.Element
one.SetOne()
b := one.Bytes()
require.Len(t, b, 48) // gnark-crypto returns exactly 48 bytes
}