deps: dex v1.5.20 + crypto v1.19.26, regen go.sum (re-tag staleness); commit safekat KAT tooling

- bump dex v1.5.16 -> v1.5.20 (native D-Chain: dex_get_* RPC, dexcore permissionless resolver)
- bump crypto v1.19.24 -> v1.19.26
- regenerate go.sum: upstream re-tagged chains v1.3.18 / dex / zap after the old go.sum
  was recorded (node-gosum-retag-staleness); rm+tidy re-records CURRENT verified hashes
  (NOT a GONOSUM bypass — full re-verification against origin)
- commit cmd/safekat{dump,verify}: ground-truth KAT generator + on-precompile verifier
  for Corona(0x012206)/Pulsar(0x012204)/Magnetar(0x012207)/CGGMP21; fix safekatdump for
  corona v0.7.9 two-value Round1 (*Round1Data, error)
All library packages + KAT tests green (GOWORK=off CGO off).
This commit is contained in:
2026-06-25 16:11:06 -07:00
parent 55d4828720
commit 5b7a06434c
4 changed files with 391 additions and 11 deletions
+305
View File
@@ -0,0 +1,305 @@
// safekatdump generates real known-answer test (KAT) vectors and the
// EXACT precompile input bytes for the Corona (0x012206), Pulsar (0x012204),
// Magnetar (0x012207) and CGGMP21 (0x0800..0003) verify precompiles, so the
// safe-{corona,pulsar,magnetar,cggmp21} Foundry tests can assert that their
// Solidity calldata framing matches the on-chain precompile wire format
// byte-for-byte.
//
// Output is JSON to stdout: one object per scheme with the precompile
// address, the verify context, the component fields, and the full framed
// `input` blob (== what the precompile's Run() parses).
package main
import (
"bytes"
"crypto/rand"
"crypto/sha256"
"encoding/binary"
"encoding/hex"
"encoding/json"
"fmt"
"math/big"
"os"
csign "github.com/luxfi/corona/sign"
cthr "github.com/luxfi/corona/threshold"
"github.com/luxfi/crypto"
"github.com/luxfi/crypto/mldsa"
"github.com/luxfi/crypto/slhdsa"
"github.com/luxfi/lattice/v7/ring"
)
type katOut struct {
Scheme string `json:"scheme"`
Address string `json:"address"`
Context string `json:"context"`
Mode string `json:"mode,omitempty"`
Threshold uint32 `json:"threshold,omitempty"`
Parties uint32 `json:"parties,omitempty"`
PubKeyHex string `json:"pubkey,omitempty"`
SigHex string `json:"sig"`
MsgHex string `json:"msg,omitempty"`
MsgHash string `json:"msgHash,omitempty"`
InputHex string `json:"input"`
}
func must(err error) {
if err != nil {
fmt.Fprintln(os.Stderr, "FATAL:", err)
os.Exit(1)
}
}
// ---- Pulsar (0x012204): byte-equal to single-party ML-DSA-65 -------------
// Wire: mode(1) || pk || msgLen:uint256(32) || msg || sig
func pulsarKAT() katOut {
priv, err := mldsa.GenerateKey(rand.Reader, mldsa.MLDSA65)
must(err)
ctx := []byte("lux-evm-precompile-pulsar-v1")
// The Safe message is always the 32-byte Safe-tx hash.
msg := sha256.Sum256([]byte("safe-pulsar KAT message"))
sigBytes, err := priv.SignCtx(rand.Reader, msg[:], ctx)
must(err)
pub := priv.PublicKey.Bytes()
var input bytes.Buffer
input.WriteByte(0x65) // ML-DSA-65 mode
input.Write(pub)
var ml [32]byte
binary.BigEndian.PutUint64(ml[24:], uint64(len(msg)))
input.Write(ml[:])
input.Write(msg[:])
input.Write(sigBytes)
return katOut{
Scheme: "pulsar",
Address: "0x0000000000000000000000000000000000012204",
Context: string(ctx),
Mode: "0x65",
PubKeyHex: hex.EncodeToString(pub),
SigHex: hex.EncodeToString(sigBytes),
MsgHash: hex.EncodeToString(msg[:]),
InputHex: hex.EncodeToString(input.Bytes()),
}
}
// ---- Magnetar (0x012207): byte-equal to single-party SLH-DSA-SHA2-128s ----
// Wire: mode(1) || pkLen:uint16 || pk || msgLen:uint16 || msg || sig
func magnetarKAT() katOut {
priv, err := slhdsa.GenerateKey(rand.Reader, slhdsa.SHA2_128s)
must(err)
ctx := []byte("lux-evm-precompile-magnetar-v1")
msg := sha256.Sum256([]byte("safe-magnetar KAT message"))
sigBytes, err := priv.SignCtx(rand.Reader, msg[:], ctx)
must(err)
pub := priv.PublicKey.Bytes()
var input bytes.Buffer
input.WriteByte(0x00) // SHA2-128s mode
var pl [2]byte
binary.BigEndian.PutUint16(pl[:], uint16(len(pub)))
input.Write(pl[:])
input.Write(pub)
var mlh [2]byte
binary.BigEndian.PutUint16(mlh[:], uint16(len(msg)))
input.Write(mlh[:])
input.Write(msg[:])
input.Write(sigBytes)
return katOut{
Scheme: "magnetar",
Address: "0x0000000000000000000000000000000000012207",
Context: string(ctx),
Mode: "0x00",
PubKeyHex: hex.EncodeToString(pub),
SigHex: hex.EncodeToString(sigBytes),
MsgHash: hex.EncodeToString(msg[:]),
InputHex: hex.EncodeToString(input.Bytes()),
}
}
// ---- Corona (0x012206): real 2-round LWE threshold signature -------------
// Wire: t:uint32 || n:uint32 || msgHash:32 || serializedSig
// serializedSig (per precompile contract.go deserializeSignature):
// c || z[N] || Delta[M] || A[M][N] || bTilde[M], each poly = N()*8 bytes BE.
func coronaKAT(t, n uint32) katOut {
shares, groupKey, err := cthr.GenerateKeys(int(t), int(n), rand.Reader)
must(err)
prfKey := make([]byte, csign.KeySize)
_, _ = rand.Read(prfKey)
signers := make([]int, n)
for i := range signers {
signers[i] = i
}
sessionID := 1
tsigners := make([]*cthr.Signer, n)
for i, sh := range shares {
tsigners[i] = cthr.NewSigner(sh)
}
// 32-byte message hash exactly as the precompile receives it.
var msgHash [32]byte
copy(msgHash[:], []byte("corona-kat-msg"))
signMessage := fmt.Sprintf("lux-evm-precompile-corona-v1|%x", msgHash[:])
round1 := make(map[int]*cthr.Round1Data)
for i, s := range tsigners {
r1, err := s.Round1(sessionID, prfKey, signers)
must(err)
round1[i] = r1
}
round2 := make(map[int]*cthr.Round2Data)
for i, s := range tsigners {
r2, err := s.Round2(sessionID, signMessage, prfKey, signers, round1)
must(err)
round2[i] = r2
}
sig, err := tsigners[0].Finalize(round2)
must(err)
if !cthr.Verify(groupKey, signMessage, sig) {
must(fmt.Errorf("corona sanity verify failed pre-serialization"))
}
sigBytes, err := coronaSerialize(groupKey.Params, sig, groupKey)
must(err)
var input bytes.Buffer
var tn [8]byte
binary.BigEndian.PutUint32(tn[0:4], t)
binary.BigEndian.PutUint32(tn[4:8], n)
input.Write(tn[:])
input.Write(msgHash[:])
input.Write(sigBytes)
return katOut{
Scheme: "corona",
Address: "0x0000000000000000000000000000000000012206",
Context: "lux-evm-precompile-corona-v1",
Threshold: t,
Parties: n,
SigHex: hex.EncodeToString(sigBytes),
MsgHash: hex.EncodeToString(msgHash[:]),
InputHex: hex.EncodeToString(input.Bytes()),
}
}
// coronaSerialize replicates precompile/corona/contract_test.go serializeSignature
// EXACTLY (8-byte big-endian coefficients, component order
// c || z[N] || Delta[M] || A[M][N] || bTilde[M]).
func coronaSerialize(params *cthr.Params, sig *cthr.Signature, groupKey *cthr.GroupKey) ([]byte, error) {
var buf bytes.Buffer
r := params.R
rXi := params.RXi
rNu := params.RNu
cCopy := *sig.C.CopyNew()
r.IMForm(cCopy, cCopy)
r.INTT(cCopy, cCopy)
if err := serializePoly(&buf, r, cCopy); err != nil {
return nil, err
}
for i := 0; i < csign.N; i++ {
zCopy := *sig.Z[i].CopyNew()
r.IMForm(zCopy, zCopy)
r.INTT(zCopy, zCopy)
if err := serializePoly(&buf, r, zCopy); err != nil {
return nil, err
}
}
for i := 0; i < csign.M; i++ {
if err := serializePoly(&buf, rNu, sig.Delta[i]); err != nil {
return nil, err
}
}
for i := 0; i < csign.M; i++ {
for j := 0; j < csign.N; j++ {
aCopy := *groupKey.A[i][j].CopyNew()
r.IMForm(aCopy, aCopy)
r.INTT(aCopy, aCopy)
if err := serializePoly(&buf, r, aCopy); err != nil {
return nil, err
}
}
}
for i := 0; i < csign.M; i++ {
if err := serializePoly(&buf, rXi, groupKey.BTilde[i]); err != nil {
return nil, err
}
}
return buf.Bytes(), nil
}
func serializePoly(buf *bytes.Buffer, r *ring.Ring, poly ring.Poly) error {
coeffs := make([]*big.Int, r.N())
r.PolyToBigint(poly, 1, coeffs)
for _, coeff := range coeffs {
cb := make([]byte, 8)
coeff.FillBytes(cb)
if _, err := buf.Write(cb); err != nil {
return err
}
}
return nil
}
// ---- CGGMP21 (0x0800..0003): threshold ECDSA, byte-equal to single-party --
// secp256k1 ECDSA. The precompile parses a standard ECDSA signature and
// asserts the recovered pubkey equals the supplied aggregated pubkey, so a
// real KAT is just a genuine secp256k1 signature whose key is the group key.
// Wire: t:uint32(4) || n:uint32(4) || pubkey(65: 0x04||x||y) || msgHash(32) ||
// sig(65: r||s||v).
func cggmp21KAT(t, n uint32) katOut {
priv, err := crypto.GenerateKey()
must(err)
// 32-byte message hash exactly as the precompile receives it.
msg := sha256.Sum256([]byte("safe-cggmp21 KAT message"))
// crypto.Sign produces [R || S || V] with V in {0,1}, the exact 65-byte
// layout the precompile parses at input[105:170].
sigBytes, err := crypto.Sign(msg[:], priv)
must(err)
// Uncompressed group public key: 0x04 || x || y (65 bytes).
pub := crypto.FromECDSAPub(&priv.PublicKey)
if len(pub) != 65 {
must(fmt.Errorf("cggmp21: unexpected pubkey length %d", len(pub)))
}
var input bytes.Buffer
var tn [8]byte
binary.BigEndian.PutUint32(tn[0:4], t)
binary.BigEndian.PutUint32(tn[4:8], n)
input.Write(tn[:])
input.Write(pub)
input.Write(msg[:])
input.Write(sigBytes)
return katOut{
Scheme: "cggmp21",
Address: "0x0800000000000000000000000000000000000003",
Context: "",
Threshold: t,
Parties: n,
PubKeyHex: hex.EncodeToString(pub),
SigHex: hex.EncodeToString(sigBytes),
MsgHash: hex.EncodeToString(msg[:]),
InputHex: hex.EncodeToString(input.Bytes()),
}
}
func main() {
out := []katOut{
pulsarKAT(),
magnetarKAT(),
coronaKAT(2, 3),
cggmp21KAT(2, 3),
}
enc := json.NewEncoder(os.Stdout)
enc.SetIndent("", " ")
must(enc.Encode(out))
}
+75
View File
@@ -0,0 +1,75 @@
// safekatverify feeds the KAT input blobs produced by safekatdump into the
// REAL precompile Run() implementations and asserts each returns the success
// word bytes32(1). This is the ground-truth check that the framed bytes the
// Solidity tests will assert against actually verify on-chain.
package main
import (
"encoding/hex"
"encoding/json"
"fmt"
"os"
"github.com/luxfi/geth/common"
cggmp21 "github.com/luxfi/precompile/cggmp21"
corona "github.com/luxfi/precompile/corona"
magnetar "github.com/luxfi/precompile/magnetar"
pulsar "github.com/luxfi/precompile/pulsar"
)
type katOut struct {
Scheme string `json:"scheme"`
InputHex string `json:"input"`
}
func main() {
var kats []katOut
if err := json.NewDecoder(os.Stdin).Decode(&kats); err != nil {
fmt.Fprintln(os.Stderr, "decode:", err)
os.Exit(1)
}
fail := false
for _, k := range kats {
input, err := hex.DecodeString(k.InputHex)
if err != nil {
fmt.Println(k.Scheme, "hex decode FAIL", err)
fail = true
continue
}
switch k.Scheme {
case "pulsar":
// pulsar Run returns (nil, gas, nil) on success, typed error on fail.
_, _, err := pulsar.PulsarVerifyPrecompile.Run(nil, common.Address{}, pulsar.ContractPulsarVerifyAddress, input, 50_000_000, true)
report("pulsar", err == nil, err, &fail)
case "magnetar":
out, _, err := magnetar.MagnetarVerifyPrecompile.Run(nil, common.Address{}, magnetar.ContractMagnetarVerifyAddress, input, 50_000_000, true)
ok := err == nil && len(out) == 32 && out[31] == 1
report("magnetar", ok, err, &fail)
case "corona":
out, _, err := corona.CoronaThresholdPrecompile.Run(nil, common.Address{}, corona.ContractCoronaThresholdAddress, input, 50_000_000, true)
ok := err == nil && len(out) == 32 && out[31] == 1
report("corona", ok, err, &fail)
case "cggmp21":
out, _, err := cggmp21.CGGMP21VerifyPrecompile.Run(nil, common.Address{}, cggmp21.ContractCGGMP21VerifyAddress, input, 50_000_000, true)
ok := err == nil && len(out) == 32 && out[31] == 1
report("cggmp21", ok, err, &fail)
default:
fmt.Println("unknown scheme", k.Scheme)
fail = true
}
}
if fail {
os.Exit(1)
}
fmt.Println("ALL KAT INPUTS VERIFY AGAINST REAL PRECOMPILES")
}
func report(name string, ok bool, err error, fail *bool) {
if ok {
fmt.Printf("%-10s VERIFY=1 (real precompile accepted KAT input)\n", name)
} else {
fmt.Printf("%-10s VERIFY=FAIL err=%v\n", name, err)
*fail = true
}
}
+3 -3
View File
@@ -13,9 +13,9 @@ require (
github.com/luxfi/ai v0.1.0
github.com/luxfi/chains v1.3.18
github.com/luxfi/corona v0.7.9
github.com/luxfi/crypto v1.19.24
github.com/luxfi/crypto v1.19.26
github.com/luxfi/database v1.20.4
github.com/luxfi/dex v1.5.16
github.com/luxfi/dex v1.5.20
github.com/luxfi/fhe v1.8.2
github.com/luxfi/geth v1.17.12
github.com/luxfi/lattice/v7 v7.1.4
@@ -131,7 +131,7 @@ require (
github.com/luxfi/cache v1.2.1 // indirect
github.com/luxfi/compress v0.0.5 // indirect
github.com/luxfi/concurrent v0.0.3 // indirect
github.com/luxfi/consensus v1.25.21 // indirect
github.com/luxfi/consensus v1.25.29 // indirect
github.com/luxfi/constants v1.5.8 // indirect
github.com/luxfi/container v0.0.4 // indirect
github.com/luxfi/crypto/ipa v1.2.4 // indirect
+8 -8
View File
@@ -205,28 +205,28 @@ github.com/luxfi/atomic v1.0.0 h1:xUV60MuzRvXngaQ1sM0yVC2v4TRoLlUGkkH7M9PS4yw=
github.com/luxfi/atomic v1.0.0/go.mod h1:0G2mTlQ6TXWHICUHrUUPu1/qAiIyR4gSZ2tva9ci/bI=
github.com/luxfi/cache v1.2.1 h1:kAzOS55/hmYeNKR+0HAKv4ma48Y6JjkI8UQeqdZ8bfI=
github.com/luxfi/cache v1.2.1/go.mod h1:co7JTxZZHpKT31Yh01LFp5aZOxmoUg157FhBLQdQHVU=
github.com/luxfi/chains v1.3.18 h1:CaKtVZ2LkPgBiaZkDEl+RPsO2iXTXsOfVDwSj2ukhdE=
github.com/luxfi/chains v1.3.18 h1:rmRQpstK3mbJzCfWOKDC0yWRP1YkwfxX8vnWnS2mr24=
github.com/luxfi/chains v1.3.18/go.mod h1:MArifjc+fRtZH6M009L47YPJFDMEjjJbHy2BJ/8nxTE=
github.com/luxfi/compress v0.0.5 h1:4tEUHw5MK1bu5UOjfYCt4OKMiH7yykIgmGPRA/BfJTM=
github.com/luxfi/compress v0.0.5/go.mod h1:Cc1yxD2pfzrvpO32W2GDwLKff+CylHEvzZh2Ko8RSIU=
github.com/luxfi/concurrent v0.0.3 h1:eJyv1fhaC0jMLMw6+QS774cUmp7GK+ouMgvLCqnC7cc=
github.com/luxfi/concurrent v0.0.3/go.mod h1:Aj/FR5NpM0cB2P4Nt3+tz9+dV6V+LUW4HuMgSjwq5hw=
github.com/luxfi/consensus v1.25.21 h1:izB/XRJ+jWEbXHc4ro1MAA6uuQApfzDq+SSNCJ69mac=
github.com/luxfi/consensus v1.25.21/go.mod h1:8gAYHLYsMvXoYz262gvusIoDRxHBrMqj6Fyj8uSnaYE=
github.com/luxfi/consensus v1.25.29 h1:z9UWFnuBY7XtTJ6klA7u8UV8ZV5JjHqh+jfKqcRcUPU=
github.com/luxfi/consensus v1.25.29/go.mod h1:cerfisfzmUJv8gbMcjcQUSdxlLpfn/+LdLY+4D95Nw0=
github.com/luxfi/constants v1.5.8 h1:iNP9AWNUcM4Tps7jYnx49CwtCWAC9mYRxJfGou2za0g=
github.com/luxfi/constants v1.5.8/go.mod h1:Pu5jWHdnUtQRbWC43yTUjU/pbIIKMDOd2a2yroSfo48=
github.com/luxfi/container v0.0.4 h1:BXhF82WyfqVP5mjlNcr7tP0Fcnvl0Ap1rkiu+rq5XuM=
github.com/luxfi/container v0.0.4/go.mod h1:Z3SpmMF5d4t77MM0nHYXURpn+EMVaeu1fhbd/3BGaek=
github.com/luxfi/corona v0.7.9 h1:NQe9V/80CdKLvbaVRE2uepxvxg9KHbWfcGRKWrzLSHc=
github.com/luxfi/corona v0.7.9/go.mod h1:SfS7xo/k4uoteEYwYy+QCMPzTU8EIEbLnbWKx5ENVCw=
github.com/luxfi/crypto v1.19.24 h1:bXhgA4g3x8Ac1RU6jYFQnP6IzUtDpFvyCsgvUbH0YmU=
github.com/luxfi/crypto v1.19.24/go.mod h1:0tfz+EbAjsW1QBWB0cte9kdjB5XhhYFmCr8BkZRux48=
github.com/luxfi/crypto v1.19.26 h1:+aHn/L479ak2ih7s/DkBZojjuhcyHBLqu3nYT81vcrU=
github.com/luxfi/crypto v1.19.26/go.mod h1:0DCU62kX8+zhYU2qeM07A4pifJyPkPujnUOfgc8TOFQ=
github.com/luxfi/crypto/ipa v1.2.4 h1:6xfwhI9/HrcDkF3Ti5/NxsNQIWbwYDJmRSNIHRQ/xfU=
github.com/luxfi/crypto/ipa v1.2.4/go.mod h1:43J6f6rcfUMrZt4cQectMOZb6Ps+fAEj8ZTPC3Kk+gE=
github.com/luxfi/database v1.20.4 h1:WOt2GIGJxf8AFpg49odMz8DZ8RFSLDrozGhZtmorN70=
github.com/luxfi/database v1.20.4/go.mod h1:S/LvmfzNYWVNslcEcZwDrntqUO2ksaL8ql1nRmLUA/Q=
github.com/luxfi/dex v1.5.16 h1:9I4FBucWGicvINlC5bZkOQwQUhLSrSYBgsIQsgWSVP4=
github.com/luxfi/dex v1.5.16/go.mod h1:bhLgq3IKuecwg5VamR1ER+k3VBSBTuzdw/TJ/B6h7iQ=
github.com/luxfi/dex v1.5.20 h1:Vu3D0iS1QcAPs1DtPjjtQpd+8bclkf5nisVYqaoEbkA=
github.com/luxfi/dex v1.5.20/go.mod h1:95k6Tl5HoyBRVd6IFzGlPOZE6SGqOyjnh4dSrJcplzM=
github.com/luxfi/fhe v1.8.2 h1:QllnObNFbi6D4mvFI6uQkepW8HgLtdy4RMR1TKYAInA=
github.com/luxfi/fhe v1.8.2/go.mod h1:16yxwhcnCez/rNcd/C9JjH9IjbEz73X+0tvlsONyLeA=
github.com/luxfi/geth v1.17.12 h1:UP/fhpcfbGPTrkOCwX3d88Oc3jVm5gTOgfjgq+lek6s=
@@ -275,7 +275,7 @@ github.com/luxfi/vm v1.2.5 h1:L1etY/gh68f9tns1BtyDUpZcBVqc3Ng1mqU3n38GyLo=
github.com/luxfi/vm v1.2.5/go.mod h1:TCCg4lDcQFCjxaxfXnxPIrpRSVAyyf2ucT4A4w654Hg=
github.com/luxfi/warp v1.19.5 h1:vigIDV4JxLz8bLxQ97dOIcZVM9FAXHmrKdmIh+/WWvk=
github.com/luxfi/warp v1.19.5/go.mod h1:t5upY5vhKvjapImmUsgPUlW8C8LZhvQpQg5WzAitQs0=
github.com/luxfi/zap v0.8.10 h1:z3tvD+2XnGJ6NhYTuZwvA9UdsUbDpXn8qbeAcoRSXXs=
github.com/luxfi/zap v0.8.10 h1:QKNTAsenkke+qQw/QGHVdVZdV48bzbPkoXq5SDCPhs0=
github.com/luxfi/zap v0.8.10/go.mod h1:JfqII8VtVQYLLTX6obU1DP9sjGqf9L24vfug5ifh0b8=
github.com/luxfi/zapdb v1.10.1 h1:XV3k4UTTKKxUMgbfC7woPXgUEIJd3P5nj2lGTQ88xeE=
github.com/luxfi/zapdb v1.10.1/go.mod h1:3Y0hH2A9kvjR+Bp9N2yEbtHnhXGHhqCQOLvBRkHrrM0=