security: remove signing/keygen precompiles, add BLS PoP, cap aggregate sigs

Private key material must never appear in EVM calldata — it is visible
to all nodes and permanently stored on-chain.

- Remove ML-DSA Sign + KeyGen precompiles (0x0113-0x0118)
- Remove ML-KEM Decapsulate + KeyGen precompiles (0x0123-0x0128)
- Remove SLH-DSA Sign precompiles (0x0136-0x0138)
- Add BLSVerifyPoP precompile (0x0167) to prevent rogue key attacks
- Cap BLSFastAggregate and BLSAggregateVerify to 64 keys/sigs max
- Precompile set is now verify-only + encapsulate-only
This commit is contained in:
Hanzo AI
2025-12-27 16:54:20 -08:00
parent 50d06e10ae
commit ad7a2202fb
6 changed files with 126 additions and 233 deletions
+83 -1
View File
@@ -8,6 +8,7 @@ package precompile
import (
"encoding/binary"
"errors"
"fmt"
"math/big"
bls12381 "github.com/consensys/gnark-crypto/ecc/bls12-381"
@@ -28,6 +29,9 @@ var (
// BLS key operations
BLSPublicKeyAggregateAddress = HexToAddress("0x0000000000000000000000000000000000000165")
BLSHashToPointAddress = HexToAddress("0x0000000000000000000000000000000000000166")
// Proof-of-possession
BLSVerifyPoPAddress = HexToAddress("0x0000000000000000000000000000000000000167")
)
// Gas costs for BLS operations
@@ -39,6 +43,7 @@ const (
blsThresholdCombineGas = 180000
blsPublicKeyAggregateGas = 50000
blsHashToPointGas = 80000
blsVerifyPoPGas = 150000
// Per-item costs for aggregation
blsPerSignatureGas = 30000
@@ -47,6 +52,11 @@ const (
// Point sizes
g1Size = 48
g2Size = 96
// maxAggregateSigs caps the number of signatures in fast aggregate verify
// to bound gas consumption and prevent abuse. 64 covers any reasonable
// consensus committee size.
maxAggregateSigs = 64
)
// BLSVerify implements single BLS signature verification
@@ -128,6 +138,9 @@ func (b *BLSAggregateVerify) Run(input []byte) ([]byte, error) {
if numSigs == 0 {
return nil, errors.New("no signatures")
}
if numSigs > maxAggregateSigs {
return nil, fmt.Errorf("too many signatures: %d > %d", numSigs, maxAggregateSigs)
}
// Parse aggregate signature
var aggSig bls12381.G2Affine
@@ -201,7 +214,13 @@ func (b *BLSAggregateVerify) Run(input []byte) ([]byte, error) {
return result, nil
}
// BLSFastAggregate verifies aggregate signature with same message
// BLSFastAggregate verifies aggregate signature with same message.
//
// SECURITY: All public keys used in aggregation MUST have a verified
// proof-of-possession (BLSVerifyPoP) before being included. Without PoP,
// an attacker can perform a rogue key attack by choosing a crafted public
// key that cancels out honest participants' keys. Verify PoP on-chain
// before calling this precompile.
type BLSFastAggregate struct{}
func (b *BLSFastAggregate) RequiredGas(input []byte) uint64 {
@@ -222,6 +241,9 @@ func (b *BLSFastAggregate) Run(input []byte) ([]byte, error) {
if numKeys == 0 {
return nil, errors.New("no public keys")
}
if numKeys > maxAggregateSigs {
return nil, fmt.Errorf("too many keys: %d > %d", numKeys, maxAggregateSigs)
}
// Parse aggregate signature
var aggSig bls12381.G2Affine
@@ -540,6 +562,65 @@ func (b *BLSHashToPoint) Run(input []byte) ([]byte, error) {
return bytes[:], nil
}
// BLSVerifyPoP verifies proof-of-possession for a BLS public key.
// A PoP is a BLS signature over the serialized public key itself, proving the
// holder knows the corresponding secret key. This MUST be verified before any
// key is used in aggregation (BLSFastAggregate, BLSPublicKeyAggregate) to
// prevent rogue key attacks.
type BLSVerifyPoP struct{}
func (b *BLSVerifyPoP) RequiredGas(input []byte) uint64 {
return blsVerifyPoPGas
}
func (b *BLSVerifyPoP) Run(input []byte) ([]byte, error) {
// Input: [48 bytes public_key (G1)][96 bytes pop_signature (G2)]
if len(input) < g1Size+g2Size {
return nil, fmt.Errorf("input too short: need %d bytes, got %d", g1Size+g2Size, len(input))
}
pubKeyBytes := input[:g1Size]
popBytes := input[g1Size : g1Size+g2Size]
// Parse public key (G1 point)
var pubKey bls12381.G1Affine
if _, err := pubKey.SetBytes(pubKeyBytes); err != nil {
return nil, errors.New("invalid public key point")
}
// Parse PoP signature (G2 point)
var popSig bls12381.G2Affine
if _, err := popSig.SetBytes(popBytes); err != nil {
return nil, errors.New("invalid PoP signature point")
}
// PoP = Sign(sk, pubkey_bytes). Verify by checking the BLS signature
// over the raw public key bytes using that same public key.
msgPoint, err := bls12381.HashToG2(pubKeyBytes, nil)
if err != nil {
return nil, errors.New("hash to curve failed")
}
// Verify: e(pubKey, H(pubKeyBytes)) == e(G1, popSig)
_, _, g1Gen, _ := bls12381.Generators()
var negG1 bls12381.G1Affine
negG1.Neg(&g1Gen)
valid, err := bls12381.PairingCheck(
[]bls12381.G1Affine{pubKey, negG1},
[]bls12381.G2Affine{msgPoint, popSig},
)
if err != nil {
return nil, err
}
result := make([]byte, 32)
if valid {
result[31] = 0x01
}
return result, nil
}
// RegisterBLS registers all BLS precompiles
func RegisterBLS(registry *Registry) {
registry.Register(BLSVerifyAddress, &BLSVerify{})
@@ -549,6 +630,7 @@ func RegisterBLS(registry *Registry) {
registry.Register(BLSThresholdCombineAddress, &BLSThresholdCombine{})
registry.Register(BLSPublicKeyAggregateAddress, &BLSPublicKeyAggregate{})
registry.Register(BLSHashToPointAddress, &BLSHashToPoint{})
registry.Register(BLSVerifyPoPAddress, &BLSVerifyPoP{})
}
func init() {
+12 -9
View File
@@ -8,17 +8,18 @@ import (
// "github.com/luxfi/crypto" // removed to avoid import cycle
)
// GetAllPostQuantumPrecompiles returns all post-quantum precompiles
// This includes SHAKE, Lamport, and the three NIST standards
// GetAllPostQuantumPrecompiles returns all post-quantum and BLS precompiles.
// This includes SHAKE, Lamport, the three NIST standards (verify/encapsulate
// only), and BLS12-381 operations.
func GetAllPostQuantumPrecompiles() map[Address]PrecompiledContract {
registry := NewRegistry()
// Register all precompile types
RegisterSHAKE(registry)
RegisterLamport(registry)
RegisterMLDSA(registry)
RegisterMLKEM(registry)
RegisterSLHDSA(registry)
RegisterBLS(registry)
return registry.Contracts()
}
@@ -54,17 +55,19 @@ func Info() map[string]interface{} {
"cgo_enabled": EnableCGO(),
"standards": []string{
"FIPS 202 (SHAKE)",
"FIPS 203 (ML-KEM)",
"FIPS 204 (ML-DSA)",
"FIPS 205 (SLH-DSA)",
"FIPS 203 (ML-KEM, encapsulate only)",
"FIPS 204 (ML-DSA, verify only)",
"FIPS 205 (SLH-DSA, verify only)",
"Lamport OTS",
"BLS12-381",
},
"address_ranges": map[string]string{
"ml_dsa": "0x0110-0x0119",
"ml_kem": "0x0120-0x0129",
"slh_dsa": "0x0130-0x0139",
"ml_dsa": "0x0110-0x0112",
"ml_kem": "0x0120-0x0122",
"slh_dsa": "0x0130-0x0135",
"shake": "0x0140-0x0149",
"lamport": "0x0150-0x0159",
"bls": "0x0160-0x0167",
},
"shake_precompiles": []string{
"SHAKE128 (variable)",
+10 -83
View File
@@ -1,38 +1,31 @@
// Copyright (C) 2025, Lux Industries Inc. All rights reserved.
// ML-DSA (FIPS 204) precompiled contracts for EVM
// Provides on-chain post-quantum signature verification
//
// SECURITY: Only verification is exposed as a precompile. Signing and keygen
// are deliberately excluded — private key material must never appear in EVM
// calldata (it is visible to all nodes and permanently stored on-chain).
package precompile
import (
"crypto/rand"
"errors"
"github.com/luxfi/crypto/mldsa"
)
// ML-DSA precompile addresses (0x0110-0x0119)
// ML-DSA verification precompile addresses (0x0110-0x0112)
var (
MLDSA44VerifyAddress = HexToAddress("0x0000000000000000000000000000000000000110")
MLDSA65VerifyAddress = HexToAddress("0x0000000000000000000000000000000000000111")
MLDSA87VerifyAddress = HexToAddress("0x0000000000000000000000000000000000000112")
MLDSA44SignAddress = HexToAddress("0x0000000000000000000000000000000000000113")
MLDSA65SignAddress = HexToAddress("0x0000000000000000000000000000000000000114")
MLDSA87SignAddress = HexToAddress("0x0000000000000000000000000000000000000115")
MLDSA44KeyGenAddress = HexToAddress("0x0000000000000000000000000000000000000116")
MLDSA65KeyGenAddress = HexToAddress("0x0000000000000000000000000000000000000117")
MLDSA87KeyGenAddress = HexToAddress("0x0000000000000000000000000000000000000118")
MLDSA44VerifyAddress = HexToAddress("0x0000000000000000000000000000000000000110")
MLDSA65VerifyAddress = HexToAddress("0x0000000000000000000000000000000000000111")
MLDSA87VerifyAddress = HexToAddress("0x0000000000000000000000000000000000000112")
)
// Gas costs for ML-DSA operations
// Gas costs for ML-DSA verification
const (
mldsa44VerifyGas = 120000
mldsa65VerifyGas = 180000
mldsa87VerifyGas = 250000
mldsa44SignGas = 200000
mldsa65SignGas = 300000
mldsa87SignGas = 400000
mldsaKeyGenGas = 500000
)
// mldsaVerify implements ML-DSA signature verification for a given mode.
@@ -70,77 +63,11 @@ func (v *mldsaVerify) Run(input []byte) ([]byte, error) {
return result, nil
}
// mldsaSign implements ML-DSA signing for a given mode.
type mldsaSign struct {
mode mldsa.Mode
gas uint64
}
func (s *mldsaSign) RequiredGas(input []byte) uint64 { return s.gas }
func (s *mldsaSign) Run(input []byte) ([]byte, error) {
// Input: [private_key][message]
privKeySize := 0
switch s.mode {
case mldsa.MLDSA44:
privKeySize = mldsa.MLDSA44PrivateKeySize
case mldsa.MLDSA65:
privKeySize = mldsa.MLDSA65PrivateKeySize
case mldsa.MLDSA87:
privKeySize = mldsa.MLDSA87PrivateKeySize
default:
return nil, errors.New("invalid ML-DSA mode")
}
if len(input) < privKeySize+1 {
return nil, errors.New("input too short")
}
privKeyBytes := input[:privKeySize]
message := input[privKeySize:]
privKey, err := mldsa.PrivateKeyFromBytes(s.mode, privKeyBytes)
if err != nil {
return nil, err
}
return privKey.Sign(rand.Reader, message, nil)
}
// mldsaKeyGen implements ML-DSA key pair generation for a given mode.
type mldsaKeyGen struct {
mode mldsa.Mode
}
func (k *mldsaKeyGen) RequiredGas(input []byte) uint64 { return mldsaKeyGenGas }
func (k *mldsaKeyGen) Run(input []byte) ([]byte, error) {
privKey, err := mldsa.GenerateKey(rand.Reader, k.mode)
if err != nil {
return nil, err
}
pubBytes := privKey.PublicKey.Bytes()
privBytes := privKey.Bytes()
// Output: [public_key][private_key]
result := make([]byte, len(pubBytes)+len(privBytes))
copy(result, pubBytes)
copy(result[len(pubBytes):], privBytes)
return result, nil
}
// RegisterMLDSA registers all ML-DSA precompiles.
// RegisterMLDSA registers ML-DSA verification precompiles.
func RegisterMLDSA(registry *Registry) {
registry.Register(MLDSA44VerifyAddress, &mldsaVerify{mode: mldsa.MLDSA44, gas: mldsa44VerifyGas})
registry.Register(MLDSA65VerifyAddress, &mldsaVerify{mode: mldsa.MLDSA65, gas: mldsa65VerifyGas})
registry.Register(MLDSA87VerifyAddress, &mldsaVerify{mode: mldsa.MLDSA87, gas: mldsa87VerifyGas})
registry.Register(MLDSA44SignAddress, &mldsaSign{mode: mldsa.MLDSA44, gas: mldsa44SignGas})
registry.Register(MLDSA65SignAddress, &mldsaSign{mode: mldsa.MLDSA65, gas: mldsa65SignGas})
registry.Register(MLDSA87SignAddress, &mldsaSign{mode: mldsa.MLDSA87, gas: mldsa87SignGas})
registry.Register(MLDSA44KeyGenAddress, &mldsaKeyGen{mode: mldsa.MLDSA44})
registry.Register(MLDSA65KeyGenAddress, &mldsaKeyGen{mode: mldsa.MLDSA65})
registry.Register(MLDSA87KeyGenAddress, &mldsaKeyGen{mode: mldsa.MLDSA87})
}
func init() {
+7 -70
View File
@@ -1,6 +1,10 @@
// Copyright (C) 2025, Lux Industries Inc. All rights reserved.
// ML-KEM (FIPS 203) precompiled contracts for EVM
// Provides on-chain post-quantum key encapsulation
//
// SECURITY: Only encapsulation (public-key operation) is exposed. Decapsulation
// and keygen are deliberately excluded — private key material must never appear
// in EVM calldata (it is visible to all nodes and permanently stored on-chain).
package precompile
@@ -10,28 +14,18 @@ import (
"github.com/luxfi/crypto/mlkem"
)
// ML-KEM precompile addresses (0x0120-0x0129)
// ML-KEM encapsulation precompile addresses (0x0120-0x0122)
var (
MLKEM512EncapsulateAddress = HexToAddress("0x0000000000000000000000000000000000000120")
MLKEM768EncapsulateAddress = HexToAddress("0x0000000000000000000000000000000000000121")
MLKEM1024EncapsulateAddress = HexToAddress("0x0000000000000000000000000000000000000122")
MLKEM512DecapsulateAddress = HexToAddress("0x0000000000000000000000000000000000000123")
MLKEM768DecapsulateAddress = HexToAddress("0x0000000000000000000000000000000000000124")
MLKEM1024DecapsulateAddress = HexToAddress("0x0000000000000000000000000000000000000125")
MLKEM512KeyGenAddress = HexToAddress("0x0000000000000000000000000000000000000126")
MLKEM768KeyGenAddress = HexToAddress("0x0000000000000000000000000000000000000127")
MLKEM1024KeyGenAddress = HexToAddress("0x0000000000000000000000000000000000000128")
)
// Gas costs for ML-KEM operations
// Gas costs for ML-KEM encapsulation
const (
mlkem512EncapsulateGas = 80000
mlkem768EncapsulateGas = 100000
mlkem1024EncapsulateGas = 130000
mlkem512DecapsulateGas = 80000
mlkem768DecapsulateGas = 100000
mlkem1024DecapsulateGas = 130000
mlkemKeyGenGas = 200000
)
// mlkemEncapsulate implements ML-KEM encapsulation for a given mode.
@@ -66,68 +60,11 @@ func (e *mlkemEncapsulate) Run(input []byte) ([]byte, error) {
return result, nil
}
// mlkemDecapsulate implements ML-KEM decapsulation for a given mode.
type mlkemDecapsulate struct {
mode mlkem.Mode
gas uint64
}
func (d *mlkemDecapsulate) RequiredGas(input []byte) uint64 { return d.gas }
func (d *mlkemDecapsulate) Run(input []byte) ([]byte, error) {
// Input: [private_key][ciphertext]
privKeySize := mlkem.GetPrivateKeySize(d.mode)
ctSize := mlkem.GetCiphertextSize(d.mode)
if len(input) != privKeySize+ctSize {
return nil, errors.New("invalid input size")
}
privKeyBytes := input[:privKeySize]
ciphertext := input[privKeySize:]
sk, err := mlkem.PrivateKeyFromBytes(privKeyBytes, d.mode)
if err != nil {
return nil, err
}
return sk.Decapsulate(ciphertext)
}
// mlkemKeyGen implements ML-KEM key pair generation for a given mode.
type mlkemKeyGen struct {
mode mlkem.Mode
}
func (k *mlkemKeyGen) RequiredGas(input []byte) uint64 { return mlkemKeyGenGas }
func (k *mlkemKeyGen) Run(input []byte) ([]byte, error) {
pk, sk, err := mlkem.GenerateKey(k.mode)
if err != nil {
return nil, err
}
pubBytes := pk.Bytes()
privBytes := sk.Bytes()
// Output: [public_key][private_key]
result := make([]byte, len(pubBytes)+len(privBytes))
copy(result, pubBytes)
copy(result[len(pubBytes):], privBytes)
return result, nil
}
// RegisterMLKEM registers all ML-KEM precompiles.
// RegisterMLKEM registers ML-KEM encapsulation precompiles.
func RegisterMLKEM(registry *Registry) {
registry.Register(MLKEM512EncapsulateAddress, &mlkemEncapsulate{mode: mlkem.MLKEM512, gas: mlkem512EncapsulateGas})
registry.Register(MLKEM768EncapsulateAddress, &mlkemEncapsulate{mode: mlkem.MLKEM768, gas: mlkem768EncapsulateGas})
registry.Register(MLKEM1024EncapsulateAddress, &mlkemEncapsulate{mode: mlkem.MLKEM1024, gas: mlkem1024EncapsulateGas})
registry.Register(MLKEM512DecapsulateAddress, &mlkemDecapsulate{mode: mlkem.MLKEM512, gas: mlkem512DecapsulateGas})
registry.Register(MLKEM768DecapsulateAddress, &mlkemDecapsulate{mode: mlkem.MLKEM768, gas: mlkem768DecapsulateGas})
registry.Register(MLKEM1024DecapsulateAddress, &mlkemDecapsulate{mode: mlkem.MLKEM1024, gas: mlkem1024DecapsulateGas})
registry.Register(MLKEM512KeyGenAddress, &mlkemKeyGen{mode: mlkem.MLKEM512})
registry.Register(MLKEM768KeyGenAddress, &mlkemKeyGen{mode: mlkem.MLKEM768})
registry.Register(MLKEM1024KeyGenAddress, &mlkemKeyGen{mode: mlkem.MLKEM1024})
}
func init() {
+7 -7
View File
@@ -647,18 +647,18 @@ func TestPrecompileRegistry(t *testing.T) {
t.Run("AllPrecompilesRegistered", func(t *testing.T) {
// Check that all expected addresses are registered
expectedAddresses := []string{
// ML-DSA (FIPS 204)
"0x0110", "0x0111", "0x0112", "0x0113", "0x0114", "0x0115", "0x0116", "0x0117", "0x0118",
// ML-KEM (FIPS 203)
"0x0120", "0x0121", "0x0122", "0x0123", "0x0124", "0x0125", "0x0126", "0x0127", "0x0128",
// SLH-DSA (FIPS 205)
"0x0130", "0x0131", "0x0132", "0x0133", "0x0134", "0x0135", "0x0136", "0x0137", "0x0138",
// ML-DSA (FIPS 204) — verify only
"0x0110", "0x0111", "0x0112",
// ML-KEM (FIPS 203) — encapsulate only
"0x0120", "0x0121", "0x0122",
// SLH-DSA (FIPS 205) — verify only
"0x0130", "0x0131", "0x0132", "0x0133", "0x0134", "0x0135",
// SHAKE
"0x0140", "0x0141", "0x0142", "0x0143", "0x0144", "0x0145", "0x0146", "0x0147", "0x0148",
// Lamport
"0x0150", "0x0151", "0x0152", "0x0153", "0x0154",
// BLS
"0x0160", "0x0161", "0x0162", "0x0163", "0x0164", "0x0165", "0x0166",
"0x0160", "0x0161", "0x0162", "0x0163", "0x0164", "0x0165", "0x0166", "0x0167",
}
for _, addr := range expectedAddresses {
+7 -63
View File
@@ -1,17 +1,20 @@
// Copyright (C) 2025, Lux Industries Inc. All rights reserved.
// SLH-DSA (FIPS 205) precompiled contracts for EVM
// Provides on-chain post-quantum stateless hash-based signature verification
//
// SECURITY: Only verification is exposed as a precompile. Signing is
// deliberately excluded — private key material must never appear in EVM
// calldata (it is visible to all nodes and permanently stored on-chain).
package precompile
import (
"crypto/rand"
"errors"
"github.com/luxfi/crypto/slhdsa"
)
// SLH-DSA precompile addresses (0x0130-0x0139)
// SLH-DSA verification precompile addresses (0x0130-0x0135)
var (
SLHDSA128sVerifyAddress = HexToAddress("0x0000000000000000000000000000000000000130")
SLHDSA128fVerifyAddress = HexToAddress("0x0000000000000000000000000000000000000131")
@@ -19,12 +22,9 @@ var (
SLHDSA192fVerifyAddress = HexToAddress("0x0000000000000000000000000000000000000133")
SLHDSA256sVerifyAddress = HexToAddress("0x0000000000000000000000000000000000000134")
SLHDSA256fVerifyAddress = HexToAddress("0x0000000000000000000000000000000000000135")
SLHDSA128sSignAddress = HexToAddress("0x0000000000000000000000000000000000000136")
SLHDSA192sSignAddress = HexToAddress("0x0000000000000000000000000000000000000137")
SLHDSA256sSignAddress = HexToAddress("0x0000000000000000000000000000000000000138")
)
// Gas costs for SLH-DSA operations
// Gas costs for SLH-DSA verification
const (
slhdsa128sVerifyGas = 300000
slhdsa128fVerifyGas = 200000
@@ -32,7 +32,6 @@ const (
slhdsa192fVerifyGas = 300000
slhdsa256sVerifyGas = 500000
slhdsa256fVerifyGas = 400000
slhdsaSignGas = 1000000
)
// slhdsaVerify implements SLH-DSA signature verification for a given mode.
@@ -70,69 +69,14 @@ func (v *slhdsaVerify) Run(input []byte) ([]byte, error) {
return result, nil
}
// slhdsaSign implements SLH-DSA signing for a given mode.
type slhdsaSign struct {
mode slhdsa.Mode
gas uint64
}
func (s *slhdsaSign) RequiredGas(input []byte) uint64 { return s.gas }
func (s *slhdsaSign) Run(input []byte) ([]byte, error) {
// Input: [private_key_bytes][message]
// SLH-DSA private keys are mode-dependent in size; we need the full
// serialized private key as produced by PrivateKey.Bytes().
// Since we don't have a PrivateKeyFromBytes that detects size, callers
// must provide the correctly-sized key for the mode.
pubKeySize := slhdsa.GetPublicKeySize(s.mode)
if pubKeySize == 0 {
return nil, errors.New("invalid SLH-DSA mode")
}
// SLH-DSA private key is 4*n bytes where n depends on security level:
// 128-bit: n=16 -> 64 bytes, 192-bit: n=24 -> 96 bytes, 256-bit: n=32 -> 128 bytes
var privKeySize int
switch s.mode {
case slhdsa.SHA2_128s, slhdsa.SHAKE_128s, slhdsa.SHA2_128f, slhdsa.SHAKE_128f:
privKeySize = 64
case slhdsa.SHA2_192s, slhdsa.SHAKE_192s, slhdsa.SHA2_192f, slhdsa.SHAKE_192f:
privKeySize = 96
case slhdsa.SHA2_256s, slhdsa.SHAKE_256s, slhdsa.SHA2_256f, slhdsa.SHAKE_256f:
privKeySize = 128
default:
return nil, errors.New("invalid SLH-DSA mode")
}
if len(input) < privKeySize+1 {
return nil, errors.New("input too short")
}
privKeyBytes := input[:privKeySize]
message := input[privKeySize:]
privKey, err := slhdsa.PrivateKeyFromBytes(s.mode, privKeyBytes)
if err != nil {
return nil, err
}
return privKey.Sign(rand.Reader, message, nil)
}
// RegisterSLHDSA registers all SLH-DSA precompiles.
// RegisterSLHDSA registers SLH-DSA verification precompiles.
func RegisterSLHDSA(registry *Registry) {
// Verification precompiles (small and fast variants)
registry.Register(SLHDSA128sVerifyAddress, &slhdsaVerify{mode: slhdsa.SHA2_128s, gas: slhdsa128sVerifyGas})
registry.Register(SLHDSA128fVerifyAddress, &slhdsaVerify{mode: slhdsa.SHA2_128f, gas: slhdsa128fVerifyGas})
registry.Register(SLHDSA192sVerifyAddress, &slhdsaVerify{mode: slhdsa.SHA2_192s, gas: slhdsa192sVerifyGas})
registry.Register(SLHDSA192fVerifyAddress, &slhdsaVerify{mode: slhdsa.SHA2_192f, gas: slhdsa192fVerifyGas})
registry.Register(SLHDSA256sVerifyAddress, &slhdsaVerify{mode: slhdsa.SHA2_256s, gas: slhdsa256sVerifyGas})
registry.Register(SLHDSA256fVerifyAddress, &slhdsaVerify{mode: slhdsa.SHA2_256f, gas: slhdsa256fVerifyGas})
// Signing precompiles (small signature variants only -- fast variants are for
// off-chain use where gas is irrelevant)
registry.Register(SLHDSA128sSignAddress, &slhdsaSign{mode: slhdsa.SHA2_128s, gas: slhdsaSignGas})
registry.Register(SLHDSA192sSignAddress, &slhdsaSign{mode: slhdsa.SHA2_192s, gas: slhdsaSignGas})
registry.Register(SLHDSA256sSignAddress, &slhdsaSign{mode: slhdsa.SHA2_256s, gas: slhdsaSignGas})
}
func init() {