mirror of
https://github.com/luxfi/precompile.git
synced 2026-09-24 00:26:21 +00:00
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.
255 lines
6.8 KiB
Go
255 lines
6.8 KiB
Go
// Copyright (C) 2025, Lux Industries Inc. All rights reserved.
|
|
// See the file LICENSE for licensing terms.
|
|
|
|
package xwing
|
|
|
|
import (
|
|
"crypto/rand"
|
|
"encoding/binary"
|
|
"testing"
|
|
|
|
"github.com/cloudflare/circl/kem/xwing"
|
|
|
|
"github.com/luxfi/geth/common"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
func TestPrecompileAddress(t *testing.T) {
|
|
require.Equal(t, common.HexToAddress("0x0000000000000000000000000000000000002221"), ContractAddress)
|
|
}
|
|
|
|
func TestEncapsulate_ValidKey(t *testing.T) {
|
|
p := &xwingPrecompile{}
|
|
scheme := xwing.Scheme()
|
|
|
|
// Generate a key pair off-chain
|
|
pk, sk, err := scheme.GenerateKeyPair()
|
|
require.NoError(t, err)
|
|
|
|
pkBytes, err := pk.MarshalBinary()
|
|
require.NoError(t, err)
|
|
|
|
// Build precompile input: [OpEncapsulate][pk]
|
|
input := make([]byte, 1+SeedSize+len(pkBytes))
|
|
input[0] = OpEncapsulate
|
|
copy(input[1+SeedSize:], pkBytes)
|
|
|
|
gas := p.RequiredGas(input)
|
|
require.Equal(t, uint64(GasEncapsulate), gas)
|
|
|
|
ret, _, err := p.Run(nil, common.Address{}, ContractAddress, input, gas, false)
|
|
require.NoError(t, err)
|
|
require.NotNil(t, ret)
|
|
|
|
// Parse result: [2 bytes ct_len][ct][ss]
|
|
ctLen := int(binary.BigEndian.Uint16(ret[:2]))
|
|
require.Equal(t, scheme.CiphertextSize(), ctLen)
|
|
|
|
ct := ret[2 : 2+ctLen]
|
|
ssEncap := ret[2+ctLen:]
|
|
require.Len(t, ssEncap, scheme.SharedKeySize())
|
|
|
|
// Decapsulate off-chain and verify shared secrets match
|
|
ssDecap, err := scheme.Decapsulate(sk, ct)
|
|
require.NoError(t, err)
|
|
require.Equal(t, ssEncap, ssDecap, "encapsulated and decapsulated shared secrets must match")
|
|
}
|
|
|
|
func TestEncapsulate_Deterministic(t *testing.T) {
|
|
// Consensus safety: identical (caller, seed, pubkey) MUST produce identical
|
|
// ciphertext across every validator. The pre-fix test asserted the
|
|
// opposite — it required NotEqual — which was a chain-splitting bug.
|
|
p := &xwingPrecompile{}
|
|
scheme := xwing.Scheme()
|
|
|
|
pk, _, err := scheme.GenerateKeyPair()
|
|
require.NoError(t, err)
|
|
|
|
pkBytes, err := pk.MarshalBinary()
|
|
require.NoError(t, err)
|
|
|
|
input := make([]byte, 1+SeedSize+len(pkBytes))
|
|
input[0] = OpEncapsulate
|
|
// Seed: non-zero so test is meaningful
|
|
for i := 0; i < SeedSize; i++ {
|
|
input[1+i] = byte(i + 1)
|
|
}
|
|
copy(input[1+SeedSize:], pkBytes)
|
|
|
|
gas := p.RequiredGas(input)
|
|
ret1, _, err := p.Run(nil, common.Address{}, ContractAddress, input, gas, false)
|
|
require.NoError(t, err)
|
|
|
|
ret2, _, err := p.Run(nil, common.Address{}, ContractAddress, input, gas, false)
|
|
require.NoError(t, err)
|
|
|
|
require.Equal(t, ret1, ret2,
|
|
"same (caller, seed, pubkey) must produce identical ciphertext — consensus safety")
|
|
}
|
|
|
|
func TestEncapsulate_DifferentSeedsDiffer(t *testing.T) {
|
|
// Different seeds with same pubkey + caller → different ciphertexts.
|
|
p := &xwingPrecompile{}
|
|
scheme := xwing.Scheme()
|
|
|
|
pk, _, err := scheme.GenerateKeyPair()
|
|
require.NoError(t, err)
|
|
pkBytes, err := pk.MarshalBinary()
|
|
require.NoError(t, err)
|
|
|
|
buildInput := func(seedByte byte) []byte {
|
|
in := make([]byte, 1+SeedSize+len(pkBytes))
|
|
in[0] = OpEncapsulate
|
|
for i := 0; i < SeedSize; i++ {
|
|
in[1+i] = seedByte
|
|
}
|
|
copy(in[1+SeedSize:], pkBytes)
|
|
return in
|
|
}
|
|
|
|
in1 := buildInput(0x01)
|
|
in2 := buildInput(0x02)
|
|
ret1, _, err := p.Run(nil, common.Address{}, ContractAddress, in1, p.RequiredGas(in1), false)
|
|
require.NoError(t, err)
|
|
ret2, _, err := p.Run(nil, common.Address{}, ContractAddress, in2, p.RequiredGas(in2), false)
|
|
require.NoError(t, err)
|
|
require.NotEqual(t, ret1, ret2, "different seeds must produce different ciphertexts")
|
|
}
|
|
|
|
func TestEncapsulate_MultipleKeyPairs(t *testing.T) {
|
|
p := &xwingPrecompile{}
|
|
scheme := xwing.Scheme()
|
|
|
|
for i := range 3 {
|
|
pk, sk, err := scheme.GenerateKeyPair()
|
|
require.NoError(t, err)
|
|
|
|
pkBytes, err := pk.MarshalBinary()
|
|
require.NoError(t, err)
|
|
|
|
input := make([]byte, 1+SeedSize+len(pkBytes))
|
|
input[0] = OpEncapsulate
|
|
copy(input[1+SeedSize:], pkBytes)
|
|
|
|
gas := p.RequiredGas(input)
|
|
ret, _, err := p.Run(nil, common.Address{}, ContractAddress, input, gas, false)
|
|
require.NoError(t, err)
|
|
|
|
ctLen := int(binary.BigEndian.Uint16(ret[:2]))
|
|
ct := ret[2 : 2+ctLen]
|
|
ssEncap := ret[2+ctLen:]
|
|
|
|
ssDecap, err := scheme.Decapsulate(sk, ct)
|
|
require.NoError(t, err)
|
|
require.Equal(t, ssEncap, ssDecap, "key pair %d: shared secrets must match", i)
|
|
}
|
|
}
|
|
|
|
func TestEncapsulate_InputTooShort(t *testing.T) {
|
|
p := &xwingPrecompile{}
|
|
|
|
// Public key is too short (only 32 bytes, X-Wing needs 1216)
|
|
input := make([]byte, 1+32)
|
|
input[0] = OpEncapsulate
|
|
|
|
gas := p.RequiredGas(input)
|
|
_, _, err := p.Run(nil, common.Address{}, ContractAddress, input, gas, false)
|
|
require.Error(t, err)
|
|
}
|
|
|
|
func TestEncapsulate_MalformedKey(t *testing.T) {
|
|
p := &xwingPrecompile{}
|
|
scheme := xwing.Scheme()
|
|
|
|
// Garbage public key of correct length, in a correctly sized frame.
|
|
garbage := make([]byte, scheme.PublicKeySize())
|
|
_, err := rand.Read(garbage)
|
|
require.NoError(t, err)
|
|
|
|
input := make([]byte, 1+SeedSize+len(garbage))
|
|
input[0] = OpEncapsulate
|
|
copy(input[1+SeedSize:], garbage)
|
|
|
|
gas := p.RequiredGas(input)
|
|
ret, _, err := p.Run(nil, common.Address{}, ContractAddress, input, gas, false)
|
|
require.Error(t, err, "random bytes fail the FIPS 203 encapsulation-key check")
|
|
require.Nil(t, ret)
|
|
}
|
|
|
|
func TestInvalidOperation(t *testing.T) {
|
|
p := &xwingPrecompile{}
|
|
|
|
input := []byte{0xFF, 0x00}
|
|
gas := p.RequiredGas(input)
|
|
_, _, err := p.Run(nil, common.Address{}, ContractAddress, input, gas+10000, false)
|
|
require.Error(t, err)
|
|
require.ErrorIs(t, err, ErrInvalidOp)
|
|
}
|
|
|
|
func TestEmptyInput(t *testing.T) {
|
|
p := &xwingPrecompile{}
|
|
|
|
_, _, err := p.Run(nil, common.Address{}, ContractAddress, []byte{}, 100000, false)
|
|
require.Error(t, err)
|
|
}
|
|
|
|
func TestGasCost(t *testing.T) {
|
|
p := &xwingPrecompile{}
|
|
|
|
tests := []struct {
|
|
name string
|
|
input []byte
|
|
expected uint64
|
|
}{
|
|
{"encapsulate", []byte{OpEncapsulate}, GasEncapsulate},
|
|
{"invalid_op", []byte{0xFF}, GasRefuse},
|
|
{"empty", []byte{}, GasRefuse},
|
|
}
|
|
|
|
for _, tc := range tests {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
gas := p.RequiredGas(tc.input)
|
|
require.Equal(t, tc.expected, gas)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestOutOfGas(t *testing.T) {
|
|
p := &xwingPrecompile{}
|
|
scheme := xwing.Scheme()
|
|
|
|
pk, _, err := scheme.GenerateKeyPair()
|
|
require.NoError(t, err)
|
|
|
|
pkBytes, err := pk.MarshalBinary()
|
|
require.NoError(t, err)
|
|
|
|
input := make([]byte, 1+SeedSize+len(pkBytes))
|
|
input[0] = OpEncapsulate
|
|
copy(input[1+SeedSize:], pkBytes)
|
|
|
|
_, _, err = p.Run(nil, common.Address{}, ContractAddress, input, 100, false)
|
|
require.Error(t, err)
|
|
}
|
|
|
|
func BenchmarkEncapsulate(b *testing.B) {
|
|
p := &xwingPrecompile{}
|
|
scheme := xwing.Scheme()
|
|
|
|
pk, _, err := scheme.GenerateKeyPair()
|
|
require.NoError(b, err)
|
|
|
|
pkBytes, err := pk.MarshalBinary()
|
|
require.NoError(b, err)
|
|
|
|
input := make([]byte, 1+SeedSize+len(pkBytes))
|
|
input[0] = OpEncapsulate
|
|
copy(input[1+SeedSize:], pkBytes)
|
|
gas := p.RequiredGas(input)
|
|
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
_, _, _ = p.Run(nil, common.Address{}, ContractAddress, input, gas, false)
|
|
}
|
|
}
|