mirror of
https://github.com/luxfi/precompile.git
synced 2026-09-24 00:26:21 +00:00
The comparison circuit is the deepest one here, a bit-by-bit borrow chain, and under PN10QP27's 27-bit modulus its noise crossed the decryption threshold often enough to return the WRONG BOOLEAN. A soak of the comparison suite failed 4 of 8 runs, a different case each run -- lt, le, gt and ge all appeared, eq and ne never did. Under PN11QP54 the same soak failed 0 of 5 and the full suite passes. A comparison that is usually right is not a comparison, so the slower parameter set is the correct trade: Add 14.92s to 24.58s, Mul 77.74s to 239.99s on the same M1 Max the previous numbers were taken on. The gas constants are re-derived from those measurements rather than left describing a ring the code no longer uses. The literal was written out in four places, and the GPU NTT binding derived its own copy while its comment claimed to "track any future parameter change instead of hard-coding". It did not: it named the literal directly, so the CPU evaluator and the GPU context could bind different rings and silently disagree. There is one Params now, and the GPU test asserts N and Q derived from it rather than restating a pair that would keep passing after the ring moved. CI had never run any of this. The heavy tests skip under -short and `make test` passes -short, so the comparison suite -- the one that was failing half its runs -- was not executed. `make test-long` now runs in CI with a timeout that fits what these operations actually cost. Co-authored-by: Hanzo Dev <[email protected]>
69 lines
2.2 KiB
Go
69 lines
2.2 KiB
Go
// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
|
|
//go:build cgo
|
|
|
|
// See the file LICENSE for licensing terms.
|
|
|
|
package fhe
|
|
|
|
import (
|
|
"math/big"
|
|
"sync"
|
|
|
|
"github.com/luxfi/fhe"
|
|
)
|
|
|
|
// Test-only threshold-FHE simulation.
|
|
//
|
|
// In production the precompile holds ONLY public key material (installed via
|
|
// SetNetworkKeys) and CANNOT decrypt — decryption is an F-Chain threshold
|
|
// ceremony where no single party holds the secret key. To unit-test the
|
|
// encrypt/compute path end to end, the test harness stands in for the DKG: it
|
|
// generates a throwaway keypair, installs the PUBLIC half into the precompile,
|
|
// and keeps the SECRET half HERE (in the test process only, never in the
|
|
// precompile) to verify results out of band — exactly what a threshold
|
|
// decryptor does, minus the share distribution.
|
|
//
|
|
// initTFHE and tfheDecrypt are the shims the op-level tests call. They exist
|
|
// ONLY in the test build. The precompile itself has no secret key, no
|
|
// decryptor, and no keygen seed.
|
|
|
|
var (
|
|
testKeyOnce sync.Once
|
|
testDecryptor *fhe.BitwiseDecryptor // secret key lives ONLY here, in the test
|
|
testKeyErr error
|
|
)
|
|
|
|
// initTFHE installs a throwaway network keypair's PUBLIC half into the
|
|
// precompile and stashes the SECRET half in a test-only decryptor. Idempotent.
|
|
func initTFHE() error {
|
|
testKeyOnce.Do(func() {
|
|
params, err := fhe.NewParametersFromLiteral(Params)
|
|
if err != nil {
|
|
testKeyErr = err
|
|
return
|
|
}
|
|
kg := fhe.NewKeyGenerator(params)
|
|
sk, pk := kg.GenKeyPair()
|
|
bsk := kg.GenBootstrapKey(sk)
|
|
if err := SetNetworkKeys(pk, bsk); err != nil {
|
|
testKeyErr = err
|
|
return
|
|
}
|
|
// The secret key NEVER enters the precompile — it lives only in this
|
|
// test-side decryptor, mirroring the F-Chain threshold decryptor.
|
|
testDecryptor = fhe.NewBitwiseDecryptor(params, sk)
|
|
})
|
|
return testKeyErr
|
|
}
|
|
|
|
// tfheDecrypt is the test-side (threshold-stand-in) decryptor. The precompile
|
|
// has no decrypt capability; this uses the test-held secret key to confirm that
|
|
// encrypt/compute produced the right plaintext.
|
|
func tfheDecrypt(ct []byte, fheType uint8) *big.Int {
|
|
bc := deserializeBitCiphertext(ct)
|
|
if bc == nil || testDecryptor == nil {
|
|
return big.NewInt(0)
|
|
}
|
|
return new(big.Int).SetUint64(testDecryptor.DecryptUint64(bc))
|
|
}
|