mirror of
https://github.com/luxfi/precompile.git
synced 2026-09-24 00:26:21 +00:00
fix: P0-P2 hardening — consensus safety, OOM prevention, error unification
P0 consensus-critical:
- FHE: deterministic keygen from SHA-256("LUX_FHE_KEYGEN_v1"), StateDB
ciphertext storage (replaces ephemeral in-memory ctStore), remove
crypto/rand fallback
- time.Now() eliminated from 20 state-modifying paths across bridge (12),
dex (7), attestation (5) — all now use blockTimestamp parameter
- WebSocket: 1024-client semaphore, 64KB read limit, 60s deadline, pong
handler, origin check on chain/dag/evm indexer subscribers
P1 correctness:
- I-Chain indexer: identity.Health → identity.getLatestBlock
- Graph pagination: min(limit, 1000) cap across 25 resolver files
- Error sentinels: 31 packages unified to contract.ErrInvalidInput
P2 standardization:
- 14 packages migrated from manual gas checks to contract.DeductGas()
- Gas naming: {Name}Gas → Gas{Name} in secp256r1, ed25519, math, quasar
- Registry address format comment clarified (PCII vs legacy ranges)
37/37 precompile tests pass.
This commit is contained in:
+1
-1
@@ -36,7 +36,7 @@ var (
|
||||
|
||||
_ contract.StatefulPrecompiledContract = &anchorPrecompile{}
|
||||
|
||||
ErrInvalidInput = errors.New("anchor: invalid input")
|
||||
ErrInvalidInput = contract.ErrInvalidInput
|
||||
ErrInvalidSelector = errors.New("anchor: unknown function selector")
|
||||
ErrHeightNotMonotonic = errors.New("anchor: height must exceed latest")
|
||||
ErrAnchorNotFound = errors.New("anchor: not found")
|
||||
|
||||
@@ -22,6 +22,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/luxfi/ai/pkg/attestation"
|
||||
"github.com/luxfi/precompile/contract"
|
||||
)
|
||||
|
||||
// Precompile addresses (0x0301-0x030F reserved for attestation)
|
||||
@@ -44,7 +45,7 @@ const (
|
||||
|
||||
// Errors
|
||||
var (
|
||||
ErrInvalidInput = errors.New("invalid input data")
|
||||
ErrInvalidInput = contract.ErrInvalidInput
|
||||
ErrInvalidGPUEvidence = errors.New("invalid GPU attestation evidence")
|
||||
ErrInvalidTPMQuote = errors.New("invalid TPM attestation quote")
|
||||
ErrInvalidComputeProof = errors.New("invalid compute attestation proof")
|
||||
|
||||
@@ -64,7 +64,7 @@ var (
|
||||
|
||||
_ contract.StatefulPrecompiledContract = &babyJubJubPrecompile{}
|
||||
|
||||
ErrInvalidInput = errors.New("invalid babyjubjub input")
|
||||
ErrInvalidInput = contract.ErrInvalidInput
|
||||
ErrInvalidOp = errors.New("invalid babyjubjub operation")
|
||||
ErrNotOnCurve = errors.New("point not on baby jubjub curve")
|
||||
)
|
||||
|
||||
+4
-4
@@ -39,7 +39,7 @@ var (
|
||||
|
||||
_ contract.StatefulPrecompiledContract = &blake3Precompile{}
|
||||
|
||||
ErrInvalidInput = errors.New("invalid blake3 input")
|
||||
ErrInvalidInput = contract.ErrInvalidInput
|
||||
ErrInvalidOperation = errors.New("invalid operation selector")
|
||||
ErrOutputTooLarge = errors.New("requested output exceeds maximum")
|
||||
ErrInvalidDataLength = errors.New("invalid data length")
|
||||
@@ -138,10 +138,10 @@ func (p *blake3Precompile) Run(
|
||||
) (ret []byte, remainingGas uint64, err error) {
|
||||
// Calculate required gas
|
||||
requiredGas := p.RequiredGas(input)
|
||||
if suppliedGas < requiredGas {
|
||||
return nil, 0, contract.ErrOutOfGas
|
||||
remainingGas, err = contract.DeductGas(suppliedGas, requiredGas)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
remainingGas = suppliedGas - requiredGas
|
||||
|
||||
if len(input) < 1 {
|
||||
return nil, remainingGas, ErrInvalidInput
|
||||
|
||||
@@ -40,7 +40,7 @@ var (
|
||||
// blsOps is the shared operation implementation used by all 7 precompile structs.
|
||||
blsOps = &blsOperations{}
|
||||
|
||||
ErrInvalidInput = errors.New("invalid BLS12-381 input")
|
||||
ErrInvalidInput = contract.ErrInvalidInput
|
||||
ErrPointNotOnCurve = errors.New("point not on curve")
|
||||
ErrPointNotInSubgrp = errors.New("point not in correct subgroup")
|
||||
ErrInvalidFieldElem = errors.New("invalid field element")
|
||||
|
||||
+8
-7
@@ -24,7 +24,7 @@ var (
|
||||
|
||||
_ contract.StatefulPrecompiledContract = &cggmp21VerifyPrecompile{}
|
||||
|
||||
ErrInvalidInputLength = errors.New("invalid input length")
|
||||
ErrInvalidInputLength = contract.ErrInvalidInput
|
||||
ErrInvalidThreshold = errors.New("invalid threshold: t must be > 0 and <= n")
|
||||
ErrInvalidPublicKey = errors.New("invalid public key")
|
||||
ErrInvalidSignature = errors.New("invalid signature")
|
||||
@@ -84,8 +84,9 @@ func (p *cggmp21VerifyPrecompile) Run(
|
||||
) ([]byte, uint64, error) {
|
||||
// Calculate required gas
|
||||
gasCost := p.RequiredGas(input)
|
||||
if suppliedGas < gasCost {
|
||||
return nil, 0, contract.ErrOutOfGas
|
||||
remainingGas, err := contract.DeductGas(suppliedGas, gasCost)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
// Input format:
|
||||
@@ -96,7 +97,7 @@ func (p *cggmp21VerifyPrecompile) Run(
|
||||
// [105:170] = ECDSA signature (65 bytes: r || s || v)
|
||||
|
||||
if len(input) < MinInputSize {
|
||||
return nil, suppliedGas - gasCost, fmt.Errorf("%w: expected at least %d bytes, got %d",
|
||||
return nil, remainingGas, fmt.Errorf("%w: expected at least %d bytes, got %d",
|
||||
ErrInvalidInputLength, MinInputSize, len(input))
|
||||
}
|
||||
|
||||
@@ -106,7 +107,7 @@ func (p *cggmp21VerifyPrecompile) Run(
|
||||
|
||||
// Validate threshold
|
||||
if threshold == 0 || threshold > totalSigners {
|
||||
return nil, suppliedGas - gasCost, ErrInvalidThreshold
|
||||
return nil, remainingGas, ErrInvalidThreshold
|
||||
}
|
||||
|
||||
// Parse public key, message hash, and signature
|
||||
@@ -128,7 +129,7 @@ func (p *cggmp21VerifyPrecompile) Run(
|
||||
var err error
|
||||
valid, err = verifyECDSASignature(publicKeyBytes, messageHash, signatureBytes)
|
||||
if err != nil {
|
||||
return nil, suppliedGas - gasCost, err
|
||||
return nil, remainingGas, err
|
||||
}
|
||||
}
|
||||
|
||||
@@ -138,7 +139,7 @@ func (p *cggmp21VerifyPrecompile) Run(
|
||||
result[31] = 1
|
||||
}
|
||||
|
||||
return result, suppliedGas - gasCost, nil
|
||||
return result, remainingGas, nil
|
||||
}
|
||||
|
||||
// verifyECDSASignature verifies an ECDSA signature
|
||||
|
||||
+1
-1
@@ -40,7 +40,7 @@ var (
|
||||
|
||||
_ contract.StatefulPrecompiledContract = Precompile
|
||||
|
||||
ErrInvalidInput = errors.New("invalid compute input")
|
||||
ErrInvalidInput = contract.ErrInvalidInput
|
||||
ErrUnknownOp = errors.New("unknown compute operation")
|
||||
ErrNotProvider = errors.New("caller is not registered provider")
|
||||
ErrJobNotFound = errors.New("job not found")
|
||||
|
||||
+6
-2
@@ -13,8 +13,12 @@ import (
|
||||
"github.com/luxfi/geth/accounts/abi"
|
||||
)
|
||||
|
||||
// ErrOutOfGas is returned when the gas is exhausted
|
||||
var ErrOutOfGas = errors.New("out of gas")
|
||||
// Shared error sentinels for all precompiles.
|
||||
var (
|
||||
ErrOutOfGas = errors.New("out of gas")
|
||||
ErrInvalidInput = errors.New("invalid input")
|
||||
ErrInvalidOp = errors.New("invalid operation")
|
||||
)
|
||||
|
||||
// Gas costs for stateful precompiles
|
||||
const (
|
||||
|
||||
+9
-8
@@ -28,7 +28,7 @@ var (
|
||||
|
||||
_ contract.StatefulPrecompiledContract = &coronaThresholdPrecompile{}
|
||||
|
||||
ErrInvalidInputLength = errors.New("invalid input length")
|
||||
ErrInvalidInputLength = contract.ErrInvalidInput
|
||||
ErrInvalidThreshold = errors.New("invalid threshold: t must be > 0 and <= n")
|
||||
ErrInvalidSignature = errors.New("signature verification failed")
|
||||
ErrInsufficientParties = errors.New("insufficient parties for threshold")
|
||||
@@ -98,8 +98,9 @@ func (p *coronaThresholdPrecompile) Run(
|
||||
) ([]byte, uint64, error) {
|
||||
// Calculate required gas
|
||||
gasCost := p.RequiredGas(input)
|
||||
if suppliedGas < gasCost {
|
||||
return nil, 0, contract.ErrOutOfGas
|
||||
remainingGas, err := contract.DeductGas(suppliedGas, gasCost)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
// Input format:
|
||||
@@ -109,7 +110,7 @@ func (p *coronaThresholdPrecompile) Run(
|
||||
// [40:...] = threshold signature (variable, ~4KB for default params)
|
||||
|
||||
if len(input) < MinInputSize {
|
||||
return nil, suppliedGas - gasCost, fmt.Errorf("%w: expected at least %d bytes, got %d",
|
||||
return nil, remainingGas, fmt.Errorf("%w: expected at least %d bytes, got %d",
|
||||
ErrInvalidInputLength, MinInputSize, len(input))
|
||||
}
|
||||
|
||||
@@ -120,21 +121,21 @@ func (p *coronaThresholdPrecompile) Run(
|
||||
|
||||
// Validate threshold
|
||||
if thresholdVal == 0 || thresholdVal > totalParties {
|
||||
return nil, suppliedGas - gasCost, fmt.Errorf("%w: t=%d, n=%d",
|
||||
return nil, remainingGas, fmt.Errorf("%w: t=%d, n=%d",
|
||||
ErrInvalidThreshold, thresholdVal, totalParties)
|
||||
}
|
||||
|
||||
// Extract signature bytes
|
||||
signatureBytes := input[MinInputSize:]
|
||||
if len(signatureBytes) < ExpectedSignatureSize {
|
||||
return nil, suppliedGas - gasCost, fmt.Errorf("%w: expected at least %d bytes, got %d",
|
||||
return nil, remainingGas, fmt.Errorf("%w: expected at least %d bytes, got %d",
|
||||
ErrInvalidInputLength, ExpectedSignatureSize, len(signatureBytes))
|
||||
}
|
||||
|
||||
// Verify the threshold signature
|
||||
valid, err := verifyThresholdSignature(thresholdVal, totalParties, messageHash, signatureBytes)
|
||||
if err != nil {
|
||||
return nil, suppliedGas - gasCost, fmt.Errorf("verification error: %w", err)
|
||||
return nil, remainingGas, fmt.Errorf("verification error: %w", err)
|
||||
}
|
||||
|
||||
// Return result as 32-byte word (1 = valid, 0 = invalid)
|
||||
@@ -143,7 +144,7 @@ func (p *coronaThresholdPrecompile) Run(
|
||||
result[31] = 1
|
||||
}
|
||||
|
||||
return result, suppliedGas - gasCost, nil
|
||||
return result, remainingGas, nil
|
||||
}
|
||||
|
||||
// verifyThresholdSignature verifies a Corona threshold signature
|
||||
|
||||
@@ -13,6 +13,7 @@ import (
|
||||
|
||||
"github.com/luxfi/geth/common"
|
||||
"github.com/luxfi/lattice/v7/ring"
|
||||
"github.com/luxfi/precompile/contract"
|
||||
"github.com/luxfi/corona/sign"
|
||||
"github.com/luxfi/corona/threshold"
|
||||
"github.com/stretchr/testify/require"
|
||||
@@ -158,7 +159,7 @@ func TestCoronaThresholdVerify_InputTooShort(t *testing.T) {
|
||||
precompile := &coronaThresholdPrecompile{}
|
||||
_, _, err := precompile.Run(nil, common.Address{}, precompile.Address(), input, 1_000_000, true)
|
||||
require.Error(t, err)
|
||||
require.Contains(t, err.Error(), "invalid input length")
|
||||
require.ErrorIs(t, err, contract.ErrInvalidInput)
|
||||
}
|
||||
|
||||
// TestCoronaThresholdVerify_GasCost tests gas cost calculation
|
||||
|
||||
@@ -33,7 +33,7 @@ var (
|
||||
|
||||
_ contract.StatefulPrecompiledContract = &curve25519Precompile{}
|
||||
|
||||
ErrInvalidInput = errors.New("invalid curve25519 input")
|
||||
ErrInvalidInput = contract.ErrInvalidInput
|
||||
ErrInvalidOp = errors.New("invalid curve25519 operation")
|
||||
ErrInvalidPoint = errors.New("invalid Edwards25519 point")
|
||||
)
|
||||
|
||||
+1
-1
@@ -79,7 +79,7 @@ var (
|
||||
ErrInvalidAddress = errors.New("invalid address: cannot be zero")
|
||||
ErrDisabled = errors.New("dead precompile is disabled")
|
||||
ErrInsufficientGas = errors.New("insufficient gas")
|
||||
ErrInvalidInput = errors.New("invalid input")
|
||||
ErrInvalidInput = contract.ErrInvalidInput
|
||||
)
|
||||
|
||||
// DeadPrecompile implements the stateful precompiled contract interface
|
||||
|
||||
+7
-8
@@ -10,7 +10,6 @@ package ed25519
|
||||
|
||||
import (
|
||||
"crypto/ed25519"
|
||||
"errors"
|
||||
|
||||
accelcrypto "github.com/luxfi/accel/ops/crypto"
|
||||
"github.com/luxfi/geth/common"
|
||||
@@ -18,9 +17,9 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
// Ed25519VerifyGas is the gas cost for signature verification
|
||||
// GasEd25519Verify is the gas cost for signature verification
|
||||
// Ed25519 is ~2x faster than secp256k1 ECDSA in practice
|
||||
Ed25519VerifyGas = 3_000
|
||||
GasEd25519Verify = 3_000
|
||||
|
||||
// InputLength is the required input length (128 bytes)
|
||||
// 32 (message hash) + 64 (signature) + 32 (public key)
|
||||
@@ -49,7 +48,7 @@ var (
|
||||
successResult = common.LeftPadBytes([]byte{1}, 32)
|
||||
|
||||
// Errors
|
||||
ErrInvalidInputLength = errors.New("ed25519: invalid input length")
|
||||
ErrInvalidInputLength = contract.ErrInvalidInput
|
||||
)
|
||||
|
||||
// ed25519VerifyPrecompile implements StatefulPrecompiledContract for Ed25519 verification.
|
||||
@@ -57,7 +56,7 @@ type ed25519VerifyPrecompile struct{}
|
||||
|
||||
// RequiredGas returns the gas required to execute the precompile
|
||||
func (c *ed25519VerifyPrecompile) RequiredGas(input []byte) uint64 {
|
||||
return Ed25519VerifyGas
|
||||
return GasEd25519Verify
|
||||
}
|
||||
|
||||
// Run executes the Ed25519 signature verification.
|
||||
@@ -79,10 +78,10 @@ func (c *ed25519VerifyPrecompile) Run(
|
||||
readOnly bool,
|
||||
) ([]byte, uint64, error) {
|
||||
gasCost := c.RequiredGas(input)
|
||||
if suppliedGas < gasCost {
|
||||
return nil, 0, contract.ErrOutOfGas
|
||||
remainingGas, err := contract.DeductGas(suppliedGas, gasCost)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
remainingGas := suppliedGas - gasCost
|
||||
|
||||
if len(input) != InputLength {
|
||||
return nil, remainingGas, nil
|
||||
|
||||
@@ -13,8 +13,8 @@ import (
|
||||
const testGas = 100_000
|
||||
|
||||
func TestRequiredGas(t *testing.T) {
|
||||
if gas := Ed25519VerifyPrecompile.RequiredGas(nil); gas != Ed25519VerifyGas {
|
||||
t.Errorf("expected gas %d, got %d", Ed25519VerifyGas, gas)
|
||||
if gas := Ed25519VerifyPrecompile.RequiredGas(nil); gas != GasEd25519Verify {
|
||||
t.Errorf("expected gas %d, got %d", GasEd25519Verify, gas)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,8 +36,8 @@ func TestValidSignature(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if remainingGas != testGas-Ed25519VerifyGas {
|
||||
t.Errorf("expected remaining gas %d, got %d", testGas-Ed25519VerifyGas, remainingGas)
|
||||
if remainingGas != testGas-GasEd25519Verify {
|
||||
t.Errorf("expected remaining gas %d, got %d", testGas-GasEd25519Verify, remainingGas)
|
||||
}
|
||||
if len(result) != 32 || result[31] != 1 {
|
||||
t.Error("expected valid signature to return success")
|
||||
@@ -109,7 +109,7 @@ func TestInvalidInputLength(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestOutOfGas(t *testing.T) {
|
||||
_, _, err := Ed25519VerifyPrecompile.Run(nil, ContractAddress, ContractAddress, make([]byte, 128), Ed25519VerifyGas-1, true)
|
||||
_, _, err := Ed25519VerifyPrecompile.Run(nil, ContractAddress, ContractAddress, make([]byte, 128), GasEd25519Verify-1, true)
|
||||
if err == nil {
|
||||
t.Error("expected out of gas error")
|
||||
}
|
||||
|
||||
@@ -61,9 +61,9 @@ func TestH07_Ed25519ImplementsStatefulInterface(t *testing.T) {
|
||||
"Gas must be deducted for verification (remaining=%d, supplied=%d)",
|
||||
remainingGas, suppliedGas)
|
||||
|
||||
expectedRemaining := suppliedGas - Ed25519VerifyGas
|
||||
expectedRemaining := suppliedGas - GasEd25519Verify
|
||||
require.Equal(t, expectedRemaining, remainingGas,
|
||||
"Remaining gas must equal supplied - Ed25519VerifyGas")
|
||||
"Remaining gas must equal supplied - GasEd25519Verify")
|
||||
}
|
||||
|
||||
// H-07: Ed25519 must reject invalid signatures via StatefulPrecompiledContract.
|
||||
@@ -99,7 +99,7 @@ func TestH07_Ed25519OutOfGas(t *testing.T) {
|
||||
input := make([]byte, InputLength)
|
||||
|
||||
c := Ed25519VerifyPrecompile
|
||||
_, _, err := c.Run(nil, ContractAddress, ContractAddress, input, Ed25519VerifyGas-1, false)
|
||||
_, _, err := c.Run(nil, ContractAddress, ContractAddress, input, GasEd25519Verify-1, false)
|
||||
require.Error(t, err, "Must fail with insufficient gas")
|
||||
}
|
||||
|
||||
|
||||
+147
-106
@@ -7,10 +7,10 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"sync"
|
||||
|
||||
"github.com/luxfi/accel"
|
||||
accelfhe "github.com/luxfi/accel/ops/fhe"
|
||||
"github.com/luxfi/crypto"
|
||||
"github.com/luxfi/geth/common"
|
||||
"github.com/luxfi/precompile/contract"
|
||||
)
|
||||
@@ -62,7 +62,7 @@ const (
|
||||
)
|
||||
|
||||
var (
|
||||
ErrInvalidInput = errors.New("invalid input")
|
||||
ErrInvalidInput = contract.ErrInvalidInput
|
||||
ErrTypeMismatch = errors.New("ciphertext type mismatch")
|
||||
ErrOperationFailed = errors.New("FHE operation failed")
|
||||
ErrNotImplemented = errors.New("operation not implemented")
|
||||
@@ -250,7 +250,7 @@ func (c *FHEContract) handleAdd(state contract.AccessibleState, caller common.Ad
|
||||
handle2 := common.BytesToHash(data[32:64])
|
||||
|
||||
// Delegate to Z-Chain FHE coprocessor
|
||||
result := performFHEOperation("add", handle1, handle2, caller)
|
||||
result := performFHEOperation(state.GetStateDB(), "add", handle1, handle2, caller)
|
||||
|
||||
return result.Bytes(), gas - GasAdd, nil
|
||||
}
|
||||
@@ -266,7 +266,7 @@ func (c *FHEContract) handleSub(state contract.AccessibleState, caller common.Ad
|
||||
handle1 := common.BytesToHash(data[:32])
|
||||
handle2 := common.BytesToHash(data[32:64])
|
||||
|
||||
result := performFHEOperation("sub", handle1, handle2, caller)
|
||||
result := performFHEOperation(state.GetStateDB(), "sub", handle1, handle2, caller)
|
||||
|
||||
return result.Bytes(), gas - GasSub, nil
|
||||
}
|
||||
@@ -282,7 +282,7 @@ func (c *FHEContract) handleMul(state contract.AccessibleState, caller common.Ad
|
||||
handle1 := common.BytesToHash(data[:32])
|
||||
handle2 := common.BytesToHash(data[32:64])
|
||||
|
||||
result := performFHEOperation("mul", handle1, handle2, caller)
|
||||
result := performFHEOperation(state.GetStateDB(), "mul", handle1, handle2, caller)
|
||||
|
||||
return result.Bytes(), gas - GasMul, nil
|
||||
}
|
||||
@@ -298,7 +298,7 @@ func (c *FHEContract) handleLt(state contract.AccessibleState, caller common.Add
|
||||
handle1 := common.BytesToHash(data[:32])
|
||||
handle2 := common.BytesToHash(data[32:64])
|
||||
|
||||
result := performFHEOperation("lt", handle1, handle2, caller)
|
||||
result := performFHEOperation(state.GetStateDB(), "lt", handle1, handle2, caller)
|
||||
|
||||
return result.Bytes(), gas - GasLt, nil
|
||||
}
|
||||
@@ -314,7 +314,7 @@ func (c *FHEContract) handleGt(state contract.AccessibleState, caller common.Add
|
||||
handle1 := common.BytesToHash(data[:32])
|
||||
handle2 := common.BytesToHash(data[32:64])
|
||||
|
||||
result := performFHEOperation("gt", handle1, handle2, caller)
|
||||
result := performFHEOperation(state.GetStateDB(), "gt", handle1, handle2, caller)
|
||||
|
||||
return result.Bytes(), gas - GasGt, nil
|
||||
}
|
||||
@@ -330,7 +330,7 @@ func (c *FHEContract) handleEq(state contract.AccessibleState, caller common.Add
|
||||
handle1 := common.BytesToHash(data[:32])
|
||||
handle2 := common.BytesToHash(data[32:64])
|
||||
|
||||
result := performFHEOperation("eq", handle1, handle2, caller)
|
||||
result := performFHEOperation(state.GetStateDB(), "eq", handle1, handle2, caller)
|
||||
|
||||
return result.Bytes(), gas - GasEq, nil
|
||||
}
|
||||
@@ -347,7 +347,7 @@ func (c *FHEContract) handleSelect(state contract.AccessibleState, caller common
|
||||
ifTrue := common.BytesToHash(data[32:64])
|
||||
ifFalse := common.BytesToHash(data[64:96])
|
||||
|
||||
result := performFHESelect(condition, ifTrue, ifFalse, caller)
|
||||
result := performFHESelect(state.GetStateDB(), condition, ifTrue, ifFalse, caller)
|
||||
|
||||
return result.Bytes(), gas - GasSelect, nil
|
||||
}
|
||||
@@ -362,7 +362,7 @@ func (c *FHEContract) handleAsEuint64(state contract.AccessibleState, caller com
|
||||
|
||||
value := new(big.Int).SetBytes(data[:32])
|
||||
|
||||
result := encryptValue(value.Uint64(), TypeEuint64, caller)
|
||||
result := encryptValue(state.GetStateDB(), value.Uint64(), TypeEuint64, caller)
|
||||
|
||||
return result.Bytes(), gas - GasEncrypt, nil
|
||||
}
|
||||
@@ -377,7 +377,7 @@ func (c *FHEContract) handleAsEaddress(state contract.AccessibleState, caller co
|
||||
|
||||
addr := common.BytesToAddress(data[12:32])
|
||||
|
||||
result := encryptAddress(addr, caller)
|
||||
result := encryptAddress(state.GetStateDB(), addr, caller)
|
||||
|
||||
return result.Bytes(), gas - GasEncrypt, nil
|
||||
}
|
||||
@@ -393,7 +393,7 @@ func (c *FHEContract) handleMax(state contract.AccessibleState, caller common.Ad
|
||||
handle1 := common.BytesToHash(data[:32])
|
||||
handle2 := common.BytesToHash(data[32:64])
|
||||
|
||||
result := performFHEOperation("max", handle1, handle2, caller)
|
||||
result := performFHEOperation(state.GetStateDB(), "max", handle1, handle2, caller)
|
||||
|
||||
return result.Bytes(), gas - GasMax, nil
|
||||
}
|
||||
@@ -409,7 +409,7 @@ func (c *FHEContract) handleMin(state contract.AccessibleState, caller common.Ad
|
||||
handle1 := common.BytesToHash(data[:32])
|
||||
handle2 := common.BytesToHash(data[32:64])
|
||||
|
||||
result := performFHEOperation("min", handle1, handle2, caller)
|
||||
result := performFHEOperation(state.GetStateDB(), "min", handle1, handle2, caller)
|
||||
|
||||
return result.Bytes(), gas - GasMin, nil
|
||||
}
|
||||
@@ -425,7 +425,7 @@ func (c *FHEContract) handleAnd(state contract.AccessibleState, caller common.Ad
|
||||
handle1 := common.BytesToHash(data[:32])
|
||||
handle2 := common.BytesToHash(data[32:64])
|
||||
|
||||
result := performFHEOperation("and", handle1, handle2, caller)
|
||||
result := performFHEOperation(state.GetStateDB(), "and", handle1, handle2, caller)
|
||||
|
||||
return result.Bytes(), gas - GasAnd, nil
|
||||
}
|
||||
@@ -441,7 +441,7 @@ func (c *FHEContract) handleOr(state contract.AccessibleState, caller common.Add
|
||||
handle1 := common.BytesToHash(data[:32])
|
||||
handle2 := common.BytesToHash(data[32:64])
|
||||
|
||||
result := performFHEOperation("or", handle1, handle2, caller)
|
||||
result := performFHEOperation(state.GetStateDB(), "or", handle1, handle2, caller)
|
||||
|
||||
return result.Bytes(), gas - GasOr, nil
|
||||
}
|
||||
@@ -456,7 +456,7 @@ func (c *FHEContract) handleNot(state contract.AccessibleState, caller common.Ad
|
||||
|
||||
handle := common.BytesToHash(data[:32])
|
||||
|
||||
result := performFHEUnaryOperation("not", handle, caller)
|
||||
result := performFHEUnaryOperation(state.GetStateDB(), "not", handle, caller)
|
||||
|
||||
return result.Bytes(), gas - GasNot, nil
|
||||
}
|
||||
@@ -471,7 +471,7 @@ func (c *FHEContract) handleNeg(state contract.AccessibleState, caller common.Ad
|
||||
|
||||
handle := common.BytesToHash(data[:32])
|
||||
|
||||
result := performFHEUnaryOperation("neg", handle, caller)
|
||||
result := performFHEUnaryOperation(state.GetStateDB(), "neg", handle, caller)
|
||||
|
||||
return result.Bytes(), gas - GasNeg, nil
|
||||
}
|
||||
@@ -486,7 +486,7 @@ func (c *FHEContract) handleRand(state contract.AccessibleState, caller common.A
|
||||
|
||||
ctType := data[0]
|
||||
|
||||
result := generateEncryptedRandom(ctType, caller)
|
||||
result := generateEncryptedRandom(state.GetStateDB(), ctType, caller)
|
||||
|
||||
return result.Bytes(), gas - GasRand, nil
|
||||
}
|
||||
@@ -504,7 +504,7 @@ func (c *FHEContract) handleDiv(state contract.AccessibleState, caller common.Ad
|
||||
handle1 := common.BytesToHash(data[:32])
|
||||
handle2 := common.BytesToHash(data[32:64])
|
||||
|
||||
result := performFHEOperation("div", handle1, handle2, caller)
|
||||
result := performFHEOperation(state.GetStateDB(), "div", handle1, handle2, caller)
|
||||
|
||||
return result.Bytes(), gas - GasDiv, nil
|
||||
}
|
||||
@@ -520,7 +520,7 @@ func (c *FHEContract) handleRem(state contract.AccessibleState, caller common.Ad
|
||||
handle1 := common.BytesToHash(data[:32])
|
||||
handle2 := common.BytesToHash(data[32:64])
|
||||
|
||||
result := performFHEOperation("rem", handle1, handle2, caller)
|
||||
result := performFHEOperation(state.GetStateDB(), "rem", handle1, handle2, caller)
|
||||
|
||||
return result.Bytes(), gas - GasRem, nil
|
||||
}
|
||||
@@ -538,7 +538,7 @@ func (c *FHEContract) handleScalarAdd(state contract.AccessibleState, caller com
|
||||
handle := common.BytesToHash(data[:32])
|
||||
scalar := new(big.Int).SetBytes(data[32:64])
|
||||
|
||||
result := performFHEScalarOperation("scalarAdd", handle, scalar, caller)
|
||||
result := performFHEScalarOperation(state.GetStateDB(), "scalarAdd", handle, scalar, caller)
|
||||
|
||||
return result.Bytes(), gas - GasAdd, nil
|
||||
}
|
||||
@@ -554,7 +554,7 @@ func (c *FHEContract) handleScalarSub(state contract.AccessibleState, caller com
|
||||
handle := common.BytesToHash(data[:32])
|
||||
scalar := new(big.Int).SetBytes(data[32:64])
|
||||
|
||||
result := performFHEScalarOperation("scalarSub", handle, scalar, caller)
|
||||
result := performFHEScalarOperation(state.GetStateDB(), "scalarSub", handle, scalar, caller)
|
||||
|
||||
return result.Bytes(), gas - GasSub, nil
|
||||
}
|
||||
@@ -570,7 +570,7 @@ func (c *FHEContract) handleScalarMul(state contract.AccessibleState, caller com
|
||||
handle := common.BytesToHash(data[:32])
|
||||
scalar := new(big.Int).SetBytes(data[32:64])
|
||||
|
||||
result := performFHEScalarOperation("scalarMul", handle, scalar, caller)
|
||||
result := performFHEScalarOperation(state.GetStateDB(), "scalarMul", handle, scalar, caller)
|
||||
|
||||
return result.Bytes(), gas - GasMul, nil
|
||||
}
|
||||
@@ -586,7 +586,7 @@ func (c *FHEContract) handleScalarDiv(state contract.AccessibleState, caller com
|
||||
handle := common.BytesToHash(data[:32])
|
||||
scalar := new(big.Int).SetBytes(data[32:64])
|
||||
|
||||
result := performFHEScalarOperation("scalarDiv", handle, scalar, caller)
|
||||
result := performFHEScalarOperation(state.GetStateDB(), "scalarDiv", handle, scalar, caller)
|
||||
|
||||
return result.Bytes(), gas - GasDiv, nil
|
||||
}
|
||||
@@ -602,7 +602,7 @@ func (c *FHEContract) handleScalarRem(state contract.AccessibleState, caller com
|
||||
handle := common.BytesToHash(data[:32])
|
||||
scalar := new(big.Int).SetBytes(data[32:64])
|
||||
|
||||
result := performFHEScalarOperation("scalarRem", handle, scalar, caller)
|
||||
result := performFHEScalarOperation(state.GetStateDB(), "scalarRem", handle, scalar, caller)
|
||||
|
||||
return result.Bytes(), gas - GasRem, nil
|
||||
}
|
||||
@@ -620,7 +620,7 @@ func (c *FHEContract) handleLe(state contract.AccessibleState, caller common.Add
|
||||
handle1 := common.BytesToHash(data[:32])
|
||||
handle2 := common.BytesToHash(data[32:64])
|
||||
|
||||
result := performFHEOperation("le", handle1, handle2, caller)
|
||||
result := performFHEOperation(state.GetStateDB(), "le", handle1, handle2, caller)
|
||||
|
||||
return result.Bytes(), gas - GasLe, nil
|
||||
}
|
||||
@@ -636,7 +636,7 @@ func (c *FHEContract) handleGe(state contract.AccessibleState, caller common.Add
|
||||
handle1 := common.BytesToHash(data[:32])
|
||||
handle2 := common.BytesToHash(data[32:64])
|
||||
|
||||
result := performFHEOperation("ge", handle1, handle2, caller)
|
||||
result := performFHEOperation(state.GetStateDB(), "ge", handle1, handle2, caller)
|
||||
|
||||
return result.Bytes(), gas - GasGe, nil
|
||||
}
|
||||
@@ -652,7 +652,7 @@ func (c *FHEContract) handleNe(state contract.AccessibleState, caller common.Add
|
||||
handle1 := common.BytesToHash(data[:32])
|
||||
handle2 := common.BytesToHash(data[32:64])
|
||||
|
||||
result := performFHEOperation("ne", handle1, handle2, caller)
|
||||
result := performFHEOperation(state.GetStateDB(), "ne", handle1, handle2, caller)
|
||||
|
||||
return result.Bytes(), gas - GasNe, nil
|
||||
}
|
||||
@@ -670,7 +670,7 @@ func (c *FHEContract) handleXor(state contract.AccessibleState, caller common.Ad
|
||||
handle1 := common.BytesToHash(data[:32])
|
||||
handle2 := common.BytesToHash(data[32:64])
|
||||
|
||||
result := performFHEOperation("xor", handle1, handle2, caller)
|
||||
result := performFHEOperation(state.GetStateDB(), "xor", handle1, handle2, caller)
|
||||
|
||||
return result.Bytes(), gas - GasXor, nil
|
||||
}
|
||||
@@ -688,7 +688,7 @@ func (c *FHEContract) handleShl(state contract.AccessibleState, caller common.Ad
|
||||
handle := common.BytesToHash(data[:32])
|
||||
shift := int(data[32])
|
||||
|
||||
result := performFHEShiftOperation("shl", handle, shift, caller)
|
||||
result := performFHEShiftOperation(state.GetStateDB(), "shl", handle, shift, caller)
|
||||
|
||||
return result.Bytes(), gas - GasShl, nil
|
||||
}
|
||||
@@ -704,7 +704,7 @@ func (c *FHEContract) handleShr(state contract.AccessibleState, caller common.Ad
|
||||
handle := common.BytesToHash(data[:32])
|
||||
shift := int(data[32])
|
||||
|
||||
result := performFHEShiftOperation("shr", handle, shift, caller)
|
||||
result := performFHEShiftOperation(state.GetStateDB(), "shr", handle, shift, caller)
|
||||
|
||||
return result.Bytes(), gas - GasShr, nil
|
||||
}
|
||||
@@ -720,7 +720,7 @@ func (c *FHEContract) handleRotl(state contract.AccessibleState, caller common.A
|
||||
handle := common.BytesToHash(data[:32])
|
||||
shift := int(data[32])
|
||||
|
||||
result := performFHEShiftOperation("rotl", handle, shift, caller)
|
||||
result := performFHEShiftOperation(state.GetStateDB(), "rotl", handle, shift, caller)
|
||||
|
||||
return result.Bytes(), gas - GasRotl, nil
|
||||
}
|
||||
@@ -736,7 +736,7 @@ func (c *FHEContract) handleRotr(state contract.AccessibleState, caller common.A
|
||||
handle := common.BytesToHash(data[:32])
|
||||
shift := int(data[32])
|
||||
|
||||
result := performFHEShiftOperation("rotr", handle, shift, caller)
|
||||
result := performFHEShiftOperation(state.GetStateDB(), "rotr", handle, shift, caller)
|
||||
|
||||
return result.Bytes(), gas - GasRotr, nil
|
||||
}
|
||||
@@ -754,7 +754,7 @@ func (c *FHEContract) handleCast(state contract.AccessibleState, caller common.A
|
||||
handle := common.BytesToHash(data[:32])
|
||||
toType := data[32]
|
||||
|
||||
result := performFHECast(handle, toType, caller)
|
||||
result := performFHECast(state.GetStateDB(), handle, toType, caller)
|
||||
|
||||
return result.Bytes(), gas - GasCast, nil
|
||||
}
|
||||
@@ -773,7 +773,7 @@ func (c *FHEContract) handleAsEbool(state contract.AccessibleState, caller commo
|
||||
boolVal = 1
|
||||
}
|
||||
|
||||
result := encryptValue(boolVal, TypeEbool, caller)
|
||||
result := encryptValue(state.GetStateDB(), boolVal, TypeEbool, caller)
|
||||
|
||||
return result.Bytes(), gas - GasEncrypt, nil
|
||||
}
|
||||
@@ -788,7 +788,7 @@ func (c *FHEContract) handleAsEuint4(state contract.AccessibleState, caller comm
|
||||
|
||||
value := new(big.Int).SetBytes(data[:32])
|
||||
|
||||
result := encryptValue(value.Uint64()&0xF, TypeEuint4, caller)
|
||||
result := encryptValue(state.GetStateDB(), value.Uint64()&0xF, TypeEuint4, caller)
|
||||
|
||||
return result.Bytes(), gas - GasEncrypt, nil
|
||||
}
|
||||
@@ -803,7 +803,7 @@ func (c *FHEContract) handleAsEuint8(state contract.AccessibleState, caller comm
|
||||
|
||||
value := new(big.Int).SetBytes(data[:32])
|
||||
|
||||
result := encryptValue(value.Uint64()&0xFF, TypeEuint8, caller)
|
||||
result := encryptValue(state.GetStateDB(), value.Uint64()&0xFF, TypeEuint8, caller)
|
||||
|
||||
return result.Bytes(), gas - GasEncrypt, nil
|
||||
}
|
||||
@@ -818,7 +818,7 @@ func (c *FHEContract) handleAsEuint16(state contract.AccessibleState, caller com
|
||||
|
||||
value := new(big.Int).SetBytes(data[:32])
|
||||
|
||||
result := encryptValue(value.Uint64()&0xFFFF, TypeEuint16, caller)
|
||||
result := encryptValue(state.GetStateDB(), value.Uint64()&0xFFFF, TypeEuint16, caller)
|
||||
|
||||
return result.Bytes(), gas - GasEncrypt, nil
|
||||
}
|
||||
@@ -833,7 +833,7 @@ func (c *FHEContract) handleAsEuint32(state contract.AccessibleState, caller com
|
||||
|
||||
value := new(big.Int).SetBytes(data[:32])
|
||||
|
||||
result := encryptValue(value.Uint64()&0xFFFFFFFF, TypeEuint32, caller)
|
||||
result := encryptValue(state.GetStateDB(), value.Uint64()&0xFFFFFFFF, TypeEuint32, caller)
|
||||
|
||||
return result.Bytes(), gas - GasEncrypt, nil
|
||||
}
|
||||
@@ -848,7 +848,7 @@ func (c *FHEContract) handleAsEuint128(state contract.AccessibleState, caller co
|
||||
|
||||
value := new(big.Int).SetBytes(data[:32])
|
||||
|
||||
result := encryptBigIntValue(value, TypeEuint128, caller)
|
||||
result := encryptBigIntValue(state.GetStateDB(), value, TypeEuint128, caller)
|
||||
|
||||
return result.Bytes(), gas - GasEncrypt, nil
|
||||
}
|
||||
@@ -863,7 +863,7 @@ func (c *FHEContract) handleAsEuint256(state contract.AccessibleState, caller co
|
||||
|
||||
value := new(big.Int).SetBytes(data[:32])
|
||||
|
||||
result := encryptBigIntValue(value, TypeEuint256, caller)
|
||||
result := encryptBigIntValue(state.GetStateDB(), value, TypeEuint256, caller)
|
||||
|
||||
return result.Bytes(), gas - GasEncrypt, nil
|
||||
}
|
||||
@@ -880,7 +880,7 @@ func (c *FHEContract) handleDecrypt(state contract.AccessibleState, caller commo
|
||||
|
||||
handle := common.BytesToHash(data[:32])
|
||||
|
||||
result := performFHEDecrypt(handle, caller)
|
||||
result := performFHEDecrypt(state.GetStateDB(), handle, caller)
|
||||
|
||||
return result.Bytes(), gas - GasDecryptRequest, nil
|
||||
}
|
||||
@@ -896,7 +896,7 @@ func (c *FHEContract) handleVerify(state contract.AccessibleState, caller common
|
||||
ctType := data[0]
|
||||
inputHandle := data[1:]
|
||||
|
||||
result := performFHEVerify(inputHandle, ctType, caller)
|
||||
result := performFHEVerify(state.GetStateDB(), inputHandle, ctType, caller)
|
||||
|
||||
return result.Bytes(), gas - GasEncrypt, nil
|
||||
}
|
||||
@@ -912,54 +912,95 @@ func (c *FHEContract) handleSealOutput(state contract.AccessibleState, caller co
|
||||
handle := common.BytesToHash(data[:32])
|
||||
publicKey := data[32:]
|
||||
|
||||
result := performFHESealOutput(handle, publicKey, caller)
|
||||
result := performFHESealOutput(state.GetStateDB(), handle, publicKey, caller)
|
||||
|
||||
return result, gas - GasEncrypt, nil
|
||||
}
|
||||
|
||||
// ctStore guards ciphertextStore and ciphertextTypes against concurrent access.
|
||||
// Access pattern is read-heavy (getCiphertext called 2x per binary op,
|
||||
// storeCiphertext 1x) so RWMutex with RLock on reads is the correct choice.
|
||||
var ctStore = struct {
|
||||
sync.RWMutex
|
||||
data map[common.Hash][]byte
|
||||
types map[common.Hash]uint8
|
||||
}{
|
||||
data: make(map[common.Hash][]byte),
|
||||
types: make(map[common.Hash]uint8),
|
||||
// Ciphertext storage layout in StateDB:
|
||||
//
|
||||
// For a ciphertext with handle H (= keccak256(ct_bytes)):
|
||||
// slot H -> type (uint8) in low byte, length (uint32) in bytes 1-4
|
||||
// slot keccak(H, i) -> 32-byte chunk i of the ciphertext data
|
||||
//
|
||||
// This persists ciphertexts to the blockchain state trie, ensuring they survive
|
||||
// across blocks and are identical on all validators.
|
||||
|
||||
// keccak256Hash computes Keccak256 and returns as common.Hash.
|
||||
func keccak256Hash(data ...[]byte) common.Hash {
|
||||
return common.BytesToHash(crypto.Keccak256(data...))
|
||||
}
|
||||
|
||||
// storeCiphertext saves ciphertext and returns its hash
|
||||
func storeCiphertext(ct []byte, ctType uint8) common.Hash {
|
||||
hash := common.BytesToHash(ct)
|
||||
ctStore.Lock()
|
||||
ctStore.data[hash] = ct
|
||||
ctStore.types[hash] = ctType
|
||||
ctStore.Unlock()
|
||||
return hash
|
||||
// storeCiphertext persists a ciphertext to the EVM state trie and returns its handle.
|
||||
func storeCiphertext(stateDB contract.StateDB, ct []byte, ctType uint8) common.Hash {
|
||||
handle := keccak256Hash(ct)
|
||||
|
||||
// Pack type (1 byte) + length (4 bytes) into a single slot.
|
||||
var meta common.Hash
|
||||
meta[0] = ctType
|
||||
meta[1] = byte(len(ct) >> 24)
|
||||
meta[2] = byte(len(ct) >> 16)
|
||||
meta[3] = byte(len(ct) >> 8)
|
||||
meta[4] = byte(len(ct))
|
||||
stateDB.SetState(ContractAddress, handle, meta)
|
||||
|
||||
// Store ciphertext bytes in 32-byte chunks.
|
||||
for i := 0; i < len(ct); i += 32 {
|
||||
slotIndex := common.BigToHash(big.NewInt(int64(i / 32)))
|
||||
slot := keccak256Hash(handle.Bytes(), slotIndex.Bytes())
|
||||
|
||||
var chunk common.Hash
|
||||
end := i + 32
|
||||
if end > len(ct) {
|
||||
end = len(ct)
|
||||
}
|
||||
copy(chunk[:], ct[i:end])
|
||||
stateDB.SetState(ContractAddress, slot, chunk)
|
||||
}
|
||||
|
||||
return handle
|
||||
}
|
||||
|
||||
// getCiphertext retrieves ciphertext by hash
|
||||
func getCiphertext(hash common.Hash) ([]byte, uint8, bool) {
|
||||
ctStore.RLock()
|
||||
ct, ok := ctStore.data[hash]
|
||||
if !ok {
|
||||
ctStore.RUnlock()
|
||||
// getCiphertext reads a ciphertext from the EVM state trie.
|
||||
func getCiphertext(stateDB contract.StateDB, handle common.Hash) ([]byte, uint8, bool) {
|
||||
meta := stateDB.GetState(ContractAddress, handle)
|
||||
|
||||
// Zero meta means no ciphertext stored at this handle.
|
||||
if meta == (common.Hash{}) {
|
||||
return nil, 0, false
|
||||
}
|
||||
ctType := ctStore.types[hash]
|
||||
ctStore.RUnlock()
|
||||
|
||||
ctType := meta[0]
|
||||
ctLen := int(meta[1])<<24 | int(meta[2])<<16 | int(meta[3])<<8 | int(meta[4])
|
||||
|
||||
if ctLen == 0 {
|
||||
return nil, 0, false
|
||||
}
|
||||
|
||||
ct := make([]byte, ctLen)
|
||||
for i := 0; i < ctLen; i += 32 {
|
||||
slotIndex := common.BigToHash(big.NewInt(int64(i / 32)))
|
||||
slot := keccak256Hash(handle.Bytes(), slotIndex.Bytes())
|
||||
chunk := stateDB.GetState(ContractAddress, slot)
|
||||
|
||||
end := i + 32
|
||||
if end > ctLen {
|
||||
end = ctLen
|
||||
}
|
||||
copy(ct[i:end], chunk[:end-i])
|
||||
}
|
||||
|
||||
return ct, ctType, true
|
||||
}
|
||||
|
||||
// performFHEOperation executes FHE binary operations using GPU acceleration
|
||||
// when available, falling back to TFHE library on CPU.
|
||||
func performFHEOperation(op string, handle1, handle2 common.Hash, caller common.Address) common.Hash {
|
||||
lhs, lhsType, ok := getCiphertext(handle1)
|
||||
func performFHEOperation(stateDB contract.StateDB, op string, handle1, handle2 common.Hash, caller common.Address) common.Hash {
|
||||
lhs, lhsType, ok := getCiphertext(stateDB, handle1)
|
||||
if !ok {
|
||||
return common.Hash{}
|
||||
}
|
||||
rhs, _, ok := getCiphertext(handle2)
|
||||
rhs, _, ok := getCiphertext(stateDB, handle2)
|
||||
if !ok {
|
||||
return common.Hash{}
|
||||
}
|
||||
@@ -970,7 +1011,7 @@ func performFHEOperation(op string, handle1, handle2 common.Hash, caller common.
|
||||
if op == "lt" || op == "gt" || op == "eq" || op == "ne" || op == "le" || op == "ge" {
|
||||
resultType = TypeEbool
|
||||
}
|
||||
return storeCiphertext(result, resultType)
|
||||
return storeCiphertext(stateDB, result, resultType)
|
||||
}
|
||||
|
||||
// CPU fallback
|
||||
@@ -1018,7 +1059,7 @@ func performFHEOperation(op string, handle1, handle2 common.Hash, caller common.
|
||||
resultType = TypeEbool
|
||||
}
|
||||
|
||||
return storeCiphertext(result, resultType)
|
||||
return storeCiphertext(stateDB, result, resultType)
|
||||
}
|
||||
|
||||
// fheOpGPU attempts GPU-accelerated FHE operations.
|
||||
@@ -1091,16 +1132,16 @@ func uint64ToBytes(u []uint64) []byte {
|
||||
}
|
||||
|
||||
// performFHESelect executes conditional selection using real TFHE library
|
||||
func performFHESelect(condition, ifTrue, ifFalse common.Hash, caller common.Address) common.Hash {
|
||||
ctControl, _, ok := getCiphertext(condition)
|
||||
func performFHESelect(stateDB contract.StateDB, condition, ifTrue, ifFalse common.Hash, caller common.Address) common.Hash {
|
||||
ctControl, _, ok := getCiphertext(stateDB, condition)
|
||||
if !ok {
|
||||
return common.Hash{}
|
||||
}
|
||||
ctTrue, trueType, ok := getCiphertext(ifTrue)
|
||||
ctTrue, trueType, ok := getCiphertext(stateDB, ifTrue)
|
||||
if !ok {
|
||||
return common.Hash{}
|
||||
}
|
||||
ctFalse, _, ok := getCiphertext(ifFalse)
|
||||
ctFalse, _, ok := getCiphertext(stateDB, ifFalse)
|
||||
if !ok {
|
||||
return common.Hash{}
|
||||
}
|
||||
@@ -1110,12 +1151,12 @@ func performFHESelect(condition, ifTrue, ifFalse common.Hash, caller common.Addr
|
||||
return common.Hash{}
|
||||
}
|
||||
|
||||
return storeCiphertext(result, trueType)
|
||||
return storeCiphertext(stateDB, result, trueType)
|
||||
}
|
||||
|
||||
// performFHEUnaryOperation executes FHE unary operations using real TFHE library
|
||||
func performFHEUnaryOperation(op string, handle common.Hash, caller common.Address) common.Hash {
|
||||
ct, ctType, ok := getCiphertext(handle)
|
||||
func performFHEUnaryOperation(stateDB contract.StateDB, op string, handle common.Hash, caller common.Address) common.Hash {
|
||||
ct, ctType, ok := getCiphertext(stateDB, handle)
|
||||
if !ok {
|
||||
return common.Hash{}
|
||||
}
|
||||
@@ -1134,43 +1175,43 @@ func performFHEUnaryOperation(op string, handle common.Hash, caller common.Addre
|
||||
return common.Hash{}
|
||||
}
|
||||
|
||||
return storeCiphertext(result, ctType)
|
||||
return storeCiphertext(stateDB, result, ctType)
|
||||
}
|
||||
|
||||
// encryptValue encrypts a plaintext value using real TFHE library
|
||||
func encryptValue(value uint64, ctType uint8, caller common.Address) common.Hash {
|
||||
func encryptValue(stateDB contract.StateDB, value uint64, ctType uint8, caller common.Address) common.Hash {
|
||||
ct := tfheTrivialEncrypt(new(big.Int).SetUint64(value), ctType)
|
||||
if ct == nil {
|
||||
return common.Hash{}
|
||||
}
|
||||
return storeCiphertext(ct, ctType)
|
||||
return storeCiphertext(stateDB, ct, ctType)
|
||||
}
|
||||
|
||||
// encryptAddress encrypts an address using real TFHE library
|
||||
func encryptAddress(addr common.Address, caller common.Address) common.Hash {
|
||||
func encryptAddress(stateDB contract.StateDB, addr common.Address, caller common.Address) common.Hash {
|
||||
// Address is 160 bits
|
||||
value := new(big.Int).SetBytes(addr.Bytes())
|
||||
ct := tfheTrivialEncrypt(value, TypeEaddress)
|
||||
if ct == nil {
|
||||
return common.Hash{}
|
||||
}
|
||||
return storeCiphertext(ct, TypeEaddress)
|
||||
return storeCiphertext(stateDB, ct, TypeEaddress)
|
||||
}
|
||||
|
||||
// generateEncryptedRandom generates random encrypted value using real TFHE library
|
||||
func generateEncryptedRandom(ctType uint8, caller common.Address) common.Hash {
|
||||
func generateEncryptedRandom(stateDB contract.StateDB, ctType uint8, caller common.Address) common.Hash {
|
||||
// Use caller address as part of seed for determinism
|
||||
seed := new(big.Int).SetBytes(caller.Bytes()).Uint64()
|
||||
ct := tfheRandom(ctType, seed)
|
||||
if ct == nil {
|
||||
return common.Hash{}
|
||||
}
|
||||
return storeCiphertext(ct, ctType)
|
||||
return storeCiphertext(stateDB, ct, ctType)
|
||||
}
|
||||
|
||||
// performFHEScalarOperation executes FHE scalar operations using real TFHE library
|
||||
func performFHEScalarOperation(op string, handle common.Hash, scalar *big.Int, caller common.Address) common.Hash {
|
||||
ct, ctType, ok := getCiphertext(handle)
|
||||
func performFHEScalarOperation(stateDB contract.StateDB, op string, handle common.Hash, scalar *big.Int, caller common.Address) common.Hash {
|
||||
ct, ctType, ok := getCiphertext(stateDB, handle)
|
||||
if !ok {
|
||||
return common.Hash{}
|
||||
}
|
||||
@@ -1195,12 +1236,12 @@ func performFHEScalarOperation(op string, handle common.Hash, scalar *big.Int, c
|
||||
return common.Hash{}
|
||||
}
|
||||
|
||||
return storeCiphertext(result, ctType)
|
||||
return storeCiphertext(stateDB, result, ctType)
|
||||
}
|
||||
|
||||
// performFHEShiftOperation executes FHE shift operations using real TFHE library
|
||||
func performFHEShiftOperation(op string, handle common.Hash, shift int, caller common.Address) common.Hash {
|
||||
ct, ctType, ok := getCiphertext(handle)
|
||||
func performFHEShiftOperation(stateDB contract.StateDB, op string, handle common.Hash, shift int, caller common.Address) common.Hash {
|
||||
ct, ctType, ok := getCiphertext(stateDB, handle)
|
||||
if !ok {
|
||||
return common.Hash{}
|
||||
}
|
||||
@@ -1223,12 +1264,12 @@ func performFHEShiftOperation(op string, handle common.Hash, shift int, caller c
|
||||
return common.Hash{}
|
||||
}
|
||||
|
||||
return storeCiphertext(result, ctType)
|
||||
return storeCiphertext(stateDB, result, ctType)
|
||||
}
|
||||
|
||||
// performFHECast executes type casting using real TFHE library
|
||||
func performFHECast(handle common.Hash, toType uint8, caller common.Address) common.Hash {
|
||||
ct, fromType, ok := getCiphertext(handle)
|
||||
func performFHECast(stateDB contract.StateDB, handle common.Hash, toType uint8, caller common.Address) common.Hash {
|
||||
ct, fromType, ok := getCiphertext(stateDB, handle)
|
||||
if !ok {
|
||||
return common.Hash{}
|
||||
}
|
||||
@@ -1238,21 +1279,21 @@ func performFHECast(handle common.Hash, toType uint8, caller common.Address) com
|
||||
return common.Hash{}
|
||||
}
|
||||
|
||||
return storeCiphertext(result, toType)
|
||||
return storeCiphertext(stateDB, result, toType)
|
||||
}
|
||||
|
||||
// encryptBigIntValue encrypts a big.Int value for types > 64 bits
|
||||
func encryptBigIntValue(value *big.Int, ctType uint8, caller common.Address) common.Hash {
|
||||
func encryptBigIntValue(stateDB contract.StateDB, value *big.Int, ctType uint8, caller common.Address) common.Hash {
|
||||
ct := tfheTrivialEncrypt(value, ctType)
|
||||
if ct == nil {
|
||||
return common.Hash{}
|
||||
}
|
||||
return storeCiphertext(ct, ctType)
|
||||
return storeCiphertext(stateDB, ct, ctType)
|
||||
}
|
||||
|
||||
// performFHEDecrypt decrypts a ciphertext (returns as big.Int bytes)
|
||||
func performFHEDecrypt(handle common.Hash, caller common.Address) *big.Int {
|
||||
ct, ctType, ok := getCiphertext(handle)
|
||||
func performFHEDecrypt(stateDB contract.StateDB, handle common.Hash, caller common.Address) *big.Int {
|
||||
ct, ctType, ok := getCiphertext(stateDB, handle)
|
||||
if !ok {
|
||||
return big.NewInt(0)
|
||||
}
|
||||
@@ -1261,16 +1302,16 @@ func performFHEDecrypt(handle common.Hash, caller common.Address) *big.Int {
|
||||
}
|
||||
|
||||
// performFHEVerify verifies and stores an input ciphertext
|
||||
func performFHEVerify(inputHandle []byte, ctType uint8, caller common.Address) common.Hash {
|
||||
func performFHEVerify(stateDB contract.StateDB, inputHandle []byte, ctType uint8, caller common.Address) common.Hash {
|
||||
if !tfheVerify(inputHandle, ctType) {
|
||||
return common.Hash{}
|
||||
}
|
||||
return storeCiphertext(inputHandle, ctType)
|
||||
return storeCiphertext(stateDB, inputHandle, ctType)
|
||||
}
|
||||
|
||||
// performFHESealOutput seals output for a specific public key
|
||||
func performFHESealOutput(handle common.Hash, publicKey []byte, caller common.Address) []byte {
|
||||
ct, ctType, ok := getCiphertext(handle)
|
||||
func performFHESealOutput(stateDB contract.StateDB, handle common.Hash, publicKey []byte, caller common.Address) []byte {
|
||||
ct, ctType, ok := getCiphertext(stateDB, handle)
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
|
||||
+18
-8
@@ -6,7 +6,7 @@
|
||||
package fhe
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"crypto/sha256"
|
||||
"encoding/binary"
|
||||
"math/big"
|
||||
"sync"
|
||||
@@ -26,7 +26,12 @@ var (
|
||||
initErr error
|
||||
)
|
||||
|
||||
// Initialize TFHE components
|
||||
// fheKeygenSeed is the domain-separated seed for deterministic FHE keygen.
|
||||
// All validators must use the same seed to produce identical keys.
|
||||
// Changing this value invalidates all existing ciphertexts on-chain.
|
||||
var fheKeygenSeed = sha256.Sum256([]byte("LUX_FHE_KEYGEN_v1"))
|
||||
|
||||
// Initialize TFHE components with deterministic keygen for consensus.
|
||||
func initTFHE() error {
|
||||
tfheOnce.Do(func() {
|
||||
var err error
|
||||
@@ -38,8 +43,14 @@ func initTFHE() error {
|
||||
return
|
||||
}
|
||||
|
||||
// Generate keys
|
||||
kg := fhe.NewKeyGenerator(params)
|
||||
// Generate keys deterministically from a fixed seed so every
|
||||
// validator produces identical FHE keys. Using crypto/rand here
|
||||
// would make each node's keys different, breaking consensus.
|
||||
kg, err := fhe.NewKeyGeneratorFromSeed(params, fheKeygenSeed[:])
|
||||
if err != nil {
|
||||
initErr = err
|
||||
return
|
||||
}
|
||||
secretKey, publicKey = kg.GenKeyPair()
|
||||
bsk := kg.GenBootstrapKey(secretKey)
|
||||
|
||||
@@ -623,10 +634,9 @@ func tfheGetNetworkPublicKey() []byte {
|
||||
|
||||
data, err := publicKey.MarshalBinary()
|
||||
if err != nil {
|
||||
// Return random bytes as fallback
|
||||
result := make([]byte, 32)
|
||||
rand.Read(result)
|
||||
return result
|
||||
// Returning random bytes here would be a consensus violation:
|
||||
// each node would return different data for the same call.
|
||||
return nil
|
||||
}
|
||||
|
||||
return data
|
||||
|
||||
+85
-11
@@ -9,11 +9,65 @@ import (
|
||||
"math/big"
|
||||
"testing"
|
||||
|
||||
"github.com/holiman/uint256"
|
||||
"github.com/luxfi/fhe"
|
||||
"github.com/luxfi/geth/common"
|
||||
"github.com/luxfi/geth/core/tracing"
|
||||
ethtypes "github.com/luxfi/geth/core/types"
|
||||
"github.com/luxfi/precompile/contract"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
// testStateDB is a minimal in-memory StateDB for FHE tests.
|
||||
type testStateDB struct {
|
||||
state map[common.Address]map[common.Hash]common.Hash
|
||||
}
|
||||
|
||||
func newTestStateDB() *testStateDB {
|
||||
return &testStateDB{state: make(map[common.Address]map[common.Hash]common.Hash)}
|
||||
}
|
||||
|
||||
func (s *testStateDB) GetState(addr common.Address, key common.Hash) common.Hash {
|
||||
if m, ok := s.state[addr]; ok {
|
||||
return m[key]
|
||||
}
|
||||
return common.Hash{}
|
||||
}
|
||||
|
||||
func (s *testStateDB) SetState(addr common.Address, key, value common.Hash) common.Hash {
|
||||
if s.state[addr] == nil {
|
||||
s.state[addr] = make(map[common.Hash]common.Hash)
|
||||
}
|
||||
prev := s.state[addr][key]
|
||||
s.state[addr][key] = value
|
||||
return prev
|
||||
}
|
||||
|
||||
func (s *testStateDB) SetNonce(common.Address, uint64, tracing.NonceChangeReason) {}
|
||||
func (s *testStateDB) GetNonce(common.Address) uint64 { return 0 }
|
||||
func (s *testStateDB) GetBalance(common.Address) *uint256.Int { return uint256.NewInt(0) }
|
||||
func (s *testStateDB) AddBalance(common.Address, *uint256.Int, tracing.BalanceChangeReason) uint256.Int {
|
||||
return *uint256.NewInt(0)
|
||||
}
|
||||
func (s *testStateDB) SubBalance(common.Address, *uint256.Int, tracing.BalanceChangeReason) uint256.Int {
|
||||
return *uint256.NewInt(0)
|
||||
}
|
||||
func (s *testStateDB) GetBalanceMultiCoin(common.Address, common.Hash) *big.Int { return big.NewInt(0) }
|
||||
func (s *testStateDB) AddBalanceMultiCoin(common.Address, common.Hash, *big.Int) {}
|
||||
func (s *testStateDB) SubBalanceMultiCoin(common.Address, common.Hash, *big.Int) {}
|
||||
func (s *testStateDB) CreateAccount(common.Address) {}
|
||||
func (s *testStateDB) Exist(common.Address) bool { return false }
|
||||
func (s *testStateDB) AddLog(*ethtypes.Log) {}
|
||||
func (s *testStateDB) Logs() []*ethtypes.Log { return nil }
|
||||
func (s *testStateDB) GetPredicateStorageSlots(common.Address, int) ([]byte, bool) {
|
||||
return nil, false
|
||||
}
|
||||
func (s *testStateDB) TxHash() common.Hash { return common.Hash{} }
|
||||
func (s *testStateDB) Snapshot() int { return 0 }
|
||||
func (s *testStateDB) RevertToSnapshot(int) {}
|
||||
|
||||
var _ contract.StateDB = (*testStateDB)(nil)
|
||||
|
||||
// TestTFHEInitialization tests that the TFHE components initialize correctly
|
||||
func TestTFHEInitialization(t *testing.T) {
|
||||
err := initTFHE()
|
||||
@@ -531,21 +585,22 @@ func TestGetNetworkPublicKey(t *testing.T) {
|
||||
require.Greater(t, len(pk), 0)
|
||||
}
|
||||
|
||||
// TestCiphertextStore tests the ciphertext storage mechanism
|
||||
// TestCiphertextStore tests the StateDB-backed ciphertext storage
|
||||
func TestCiphertextStore(t *testing.T) {
|
||||
err := initTFHE()
|
||||
require.NoError(t, err)
|
||||
db := newTestStateDB()
|
||||
|
||||
// Create ciphertext
|
||||
ct := tfheTrivialEncrypt(big.NewInt(42), TypeEuint8)
|
||||
require.NotNil(t, ct)
|
||||
|
||||
// Store it
|
||||
handle := storeCiphertext(ct, TypeEuint8)
|
||||
handle := storeCiphertext(db, ct, TypeEuint8)
|
||||
require.NotEqual(t, common.Hash{}, handle)
|
||||
|
||||
// Retrieve it
|
||||
retrieved, ctType, ok := getCiphertext(handle)
|
||||
retrieved, ctType, ok := getCiphertext(db, handle)
|
||||
require.True(t, ok)
|
||||
require.Equal(t, TypeEuint8, ctType)
|
||||
require.Equal(t, ct, retrieved)
|
||||
@@ -555,6 +610,7 @@ func TestCiphertextStore(t *testing.T) {
|
||||
func TestPerformFHEOperation(t *testing.T) {
|
||||
err := initTFHE()
|
||||
require.NoError(t, err)
|
||||
db := newTestStateDB()
|
||||
|
||||
caller := common.HexToAddress("0x1234567890123456789012345678901234567890")
|
||||
|
||||
@@ -562,14 +618,14 @@ func TestPerformFHEOperation(t *testing.T) {
|
||||
ct1 := tfheTrivialEncrypt(big.NewInt(10), TypeEuint8)
|
||||
ct2 := tfheTrivialEncrypt(big.NewInt(3), TypeEuint8)
|
||||
|
||||
handle1 := storeCiphertext(ct1, TypeEuint8)
|
||||
handle2 := storeCiphertext(ct2, TypeEuint8)
|
||||
handle1 := storeCiphertext(db, ct1, TypeEuint8)
|
||||
handle2 := storeCiphertext(db, ct2, TypeEuint8)
|
||||
|
||||
// Test add operation
|
||||
resultHandle := performFHEOperation("add", handle1, handle2, caller)
|
||||
resultHandle := performFHEOperation(db, "add", handle1, handle2, caller)
|
||||
require.NotEqual(t, common.Hash{}, resultHandle)
|
||||
|
||||
resultCt, _, ok := getCiphertext(resultHandle)
|
||||
resultCt, _, ok := getCiphertext(db, resultHandle)
|
||||
require.True(t, ok)
|
||||
|
||||
decrypted := tfheDecrypt(resultCt, TypeEuint8)
|
||||
@@ -580,13 +636,14 @@ func TestPerformFHEOperation(t *testing.T) {
|
||||
func TestEncryptValue(t *testing.T) {
|
||||
err := initTFHE()
|
||||
require.NoError(t, err)
|
||||
db := newTestStateDB()
|
||||
|
||||
caller := common.HexToAddress("0x1234567890123456789012345678901234567890")
|
||||
|
||||
handle := encryptValue(42, TypeEuint8, caller)
|
||||
handle := encryptValue(db, 42, TypeEuint8, caller)
|
||||
require.NotEqual(t, common.Hash{}, handle)
|
||||
|
||||
ct, ctType, ok := getCiphertext(handle)
|
||||
ct, ctType, ok := getCiphertext(db, handle)
|
||||
require.True(t, ok)
|
||||
require.Equal(t, TypeEuint8, ctType)
|
||||
|
||||
@@ -598,16 +655,17 @@ func TestEncryptValue(t *testing.T) {
|
||||
func TestEncryptAddress(t *testing.T) {
|
||||
err := initTFHE()
|
||||
require.NoError(t, err)
|
||||
db := newTestStateDB()
|
||||
|
||||
caller := common.HexToAddress("0x1234567890123456789012345678901234567890")
|
||||
// Use a small address that fits in uint64 for testing
|
||||
// Full 160-bit addresses require proper radix integer encryption
|
||||
addr := common.HexToAddress("0x0000000000000000000000000000000012345678")
|
||||
|
||||
handle := encryptAddress(addr, caller)
|
||||
handle := encryptAddress(db, addr, caller)
|
||||
require.NotEqual(t, common.Hash{}, handle)
|
||||
|
||||
ct, ctType, ok := getCiphertext(handle)
|
||||
ct, ctType, ok := getCiphertext(db, handle)
|
||||
require.True(t, ok)
|
||||
require.Equal(t, TypeEaddress, ctType)
|
||||
|
||||
@@ -636,3 +694,19 @@ func TestFHEVerify(t *testing.T) {
|
||||
invalid := tfheVerify(garbage, TypeEuint8)
|
||||
require.False(t, invalid)
|
||||
}
|
||||
|
||||
// TestDeterministicKeygen verifies that initTFHE produces identical keys
|
||||
// on repeated invocations (via the fixed seed), which is the core consensus fix.
|
||||
func TestDeterministicKeygen(t *testing.T) {
|
||||
// initTFHE uses sync.Once, so we can only verify the keys are non-nil
|
||||
// and that the public key serializes deterministically.
|
||||
err := initTFHE()
|
||||
require.NoError(t, err)
|
||||
|
||||
pk1 := tfheGetNetworkPublicKey()
|
||||
require.NotNil(t, pk1)
|
||||
|
||||
// Second call must return identical bytes (same singleton).
|
||||
pk2 := tfheGetNetworkPublicKey()
|
||||
require.Equal(t, pk1, pk2, "public key must be deterministic across calls")
|
||||
}
|
||||
|
||||
+7
-6
@@ -25,7 +25,7 @@ var (
|
||||
|
||||
_ contract.StatefulPrecompiledContract = &frostVerifyPrecompile{}
|
||||
|
||||
ErrInvalidInputLength = errors.New("invalid input length")
|
||||
ErrInvalidInputLength = contract.ErrInvalidInput
|
||||
ErrInvalidThreshold = errors.New("invalid threshold: t must be > 0 and <= n")
|
||||
ErrInvalidPublicKey = errors.New("invalid public key")
|
||||
ErrInvalidSignature = errors.New("invalid signature")
|
||||
@@ -87,8 +87,9 @@ func (p *frostVerifyPrecompile) Run(
|
||||
) ([]byte, uint64, error) {
|
||||
// Calculate required gas
|
||||
gasCost := p.RequiredGas(input)
|
||||
if suppliedGas < gasCost {
|
||||
return nil, 0, contract.ErrOutOfGas
|
||||
remainingGas, err := contract.DeductGas(suppliedGas, gasCost)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
// Input format:
|
||||
@@ -99,7 +100,7 @@ func (p *frostVerifyPrecompile) Run(
|
||||
// [72:136] = Schnorr signature (64 bytes: R || s)
|
||||
|
||||
if len(input) < MinInputSize {
|
||||
return nil, suppliedGas - gasCost, fmt.Errorf("%w: expected at least %d bytes, got %d",
|
||||
return nil, remainingGas, fmt.Errorf("%w: expected at least %d bytes, got %d",
|
||||
ErrInvalidInputLength, MinInputSize, len(input))
|
||||
}
|
||||
|
||||
@@ -109,7 +110,7 @@ func (p *frostVerifyPrecompile) Run(
|
||||
|
||||
// Validate threshold
|
||||
if threshold == 0 || threshold > totalSigners {
|
||||
return nil, suppliedGas - gasCost, ErrInvalidThreshold
|
||||
return nil, remainingGas, ErrInvalidThreshold
|
||||
}
|
||||
|
||||
// Parse public key, message hash, and signature
|
||||
@@ -138,7 +139,7 @@ func (p *frostVerifyPrecompile) Run(
|
||||
result[31] = 1
|
||||
}
|
||||
|
||||
return result, suppliedGas - gasCost, nil
|
||||
return result, remainingGas, nil
|
||||
}
|
||||
|
||||
// liftXCache caches decompressed public key points.
|
||||
|
||||
+7
-7
@@ -40,7 +40,7 @@ var (
|
||||
|
||||
_ contract.StatefulPrecompiledContract = &hpkePrecompile{}
|
||||
|
||||
ErrInvalidInput = errors.New("invalid HPKE input")
|
||||
ErrInvalidInput = contract.ErrInvalidInput
|
||||
ErrInvalidCipherSuite = errors.New("invalid cipher suite")
|
||||
)
|
||||
|
||||
@@ -180,18 +180,18 @@ func (p *hpkePrecompile) Run(
|
||||
readOnly bool,
|
||||
) ([]byte, uint64, error) {
|
||||
gasCost := p.RequiredGas(input)
|
||||
if suppliedGas < gasCost {
|
||||
return nil, 0, contract.ErrOutOfGas
|
||||
remainingGas, err := contract.DeductGas(suppliedGas, gasCost)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
if len(input) < 1 {
|
||||
return nil, suppliedGas - gasCost, ErrInvalidInput
|
||||
return nil, remainingGas, ErrInvalidInput
|
||||
}
|
||||
|
||||
op := input[0]
|
||||
|
||||
var result []byte
|
||||
var err error
|
||||
|
||||
switch op {
|
||||
case OpSingleShotSeal:
|
||||
@@ -201,10 +201,10 @@ func (p *hpkePrecompile) Run(
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return nil, suppliedGas - gasCost, err
|
||||
return nil, remainingGas, err
|
||||
}
|
||||
|
||||
return result, suppliedGas - gasCost, nil
|
||||
return result, remainingGas, nil
|
||||
}
|
||||
|
||||
func (p *hpkePrecompile) parseSuite(input []byte) (hpke.Suite, error) {
|
||||
|
||||
+8
-8
@@ -36,7 +36,7 @@ var (
|
||||
// Trusted setup context (initialized once)
|
||||
kzgContext *gokzg4844.Context
|
||||
|
||||
ErrInvalidInput = errors.New("invalid KZG4844 input")
|
||||
ErrInvalidInput = contract.ErrInvalidInput
|
||||
ErrInvalidBlob = errors.New("invalid blob data")
|
||||
ErrInvalidCommitment = errors.New("invalid commitment")
|
||||
ErrInvalidProof = errors.New("invalid proof")
|
||||
@@ -147,22 +147,22 @@ func (p *kzg4844Precompile) Run(
|
||||
readOnly bool,
|
||||
) ([]byte, uint64, error) {
|
||||
gasCost := p.RequiredGas(input)
|
||||
if suppliedGas < gasCost {
|
||||
return nil, 0, contract.ErrOutOfGas
|
||||
remainingGas, err := contract.DeductGas(suppliedGas, gasCost)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
if len(input) < 1 {
|
||||
return nil, suppliedGas - gasCost, ErrInvalidInput
|
||||
return nil, remainingGas, ErrInvalidInput
|
||||
}
|
||||
|
||||
if kzgContext == nil {
|
||||
return nil, suppliedGas - gasCost, ErrContextNotInit
|
||||
return nil, remainingGas, ErrContextNotInit
|
||||
}
|
||||
|
||||
op := input[0]
|
||||
|
||||
var result []byte
|
||||
var err error
|
||||
|
||||
switch op {
|
||||
case OpBlobToCommitment:
|
||||
@@ -182,10 +182,10 @@ func (p *kzg4844Precompile) Run(
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return nil, suppliedGas - gasCost, err
|
||||
return nil, remainingGas, err
|
||||
}
|
||||
|
||||
return result, suppliedGas - gasCost, nil
|
||||
return result, remainingGas, nil
|
||||
}
|
||||
|
||||
// blobToCommitment computes the KZG commitment for a blob
|
||||
|
||||
+4
-4
@@ -31,7 +31,7 @@ var (
|
||||
|
||||
_ contract.StatefulPrecompiledContract = Precompile
|
||||
|
||||
ErrInvalidInput = errors.New("invalid math input")
|
||||
ErrInvalidInput = contract.ErrInvalidInput
|
||||
// ErrOutOfGas aliases the canonical sentinel so callers can
|
||||
// errors.Is(err, contract.ErrOutOfGas) across every precompile.
|
||||
ErrOutOfGas = contract.ErrOutOfGas
|
||||
@@ -47,8 +47,8 @@ const (
|
||||
OpExp = 0x05
|
||||
OpPow = 0x06
|
||||
|
||||
BaseGas = 50
|
||||
WordGas = 5
|
||||
GasBase = 50
|
||||
GasWord = 5
|
||||
)
|
||||
|
||||
// q128 = 2^128 for Q128.128 fixed-point
|
||||
@@ -71,7 +71,7 @@ func (p *fixedPointMathPrecompile) Run(
|
||||
data := input[1:]
|
||||
|
||||
words := uint64(len(data)+31) / 32
|
||||
gasCost := BaseGas + words*WordGas
|
||||
gasCost := GasBase + words*GasWord
|
||||
gas, err := contract.DeductGas(suppliedGas, gasCost)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
|
||||
+14
-10
@@ -24,7 +24,7 @@ var (
|
||||
|
||||
_ contract.StatefulPrecompiledContract = &mldsaVerifyPrecompile{}
|
||||
|
||||
ErrInvalidInputLength = errors.New("invalid input length")
|
||||
ErrInvalidInputLength = contract.ErrInvalidInput
|
||||
ErrInvalidMode = errors.New("invalid ML-DSA mode")
|
||||
ErrUnsupportedMode = errors.New("unsupported ML-DSA mode")
|
||||
)
|
||||
@@ -166,13 +166,14 @@ func (p *mldsaVerifyPrecompile) Run(
|
||||
) ([]byte, uint64, error) {
|
||||
// Calculate required gas
|
||||
gasCost := p.RequiredGas(input)
|
||||
if suppliedGas < gasCost {
|
||||
return nil, 0, contract.ErrOutOfGas
|
||||
remainingGas, err := contract.DeductGas(suppliedGas, gasCost)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
// Minimum: mode byte
|
||||
if len(input) < ModeByte {
|
||||
return nil, suppliedGas - gasCost, fmt.Errorf("%w: need at least mode byte", ErrInvalidInputLength)
|
||||
return nil, remainingGas, fmt.Errorf("%w: need at least mode byte", ErrInvalidInputLength)
|
||||
}
|
||||
|
||||
// Dispatch batch verify
|
||||
@@ -184,7 +185,7 @@ func (p *mldsaVerifyPrecompile) Run(
|
||||
mode := input[0]
|
||||
pubKeySize, sigSize, _, mldsaMode, err := getModeParams(mode)
|
||||
if err != nil {
|
||||
return nil, suppliedGas - gasCost, fmt.Errorf("%w: 0x%02x", ErrUnsupportedMode, mode)
|
||||
return nil, remainingGas, fmt.Errorf("%w: 0x%02x", ErrUnsupportedMode, mode)
|
||||
}
|
||||
|
||||
// Calculate offsets
|
||||
@@ -198,7 +199,7 @@ func (p *mldsaVerifyPrecompile) Run(
|
||||
// Minimum input size for this mode
|
||||
minInputSize := sigEnd
|
||||
if len(input) < minInputSize {
|
||||
return nil, suppliedGas - gasCost, fmt.Errorf("%w: expected at least %d bytes for mode 0x%02x, got %d",
|
||||
return nil, remainingGas, fmt.Errorf("%w: expected at least %d bytes for mode 0x%02x, got %d",
|
||||
ErrInvalidInputLength, minInputSize, mode, len(input))
|
||||
}
|
||||
|
||||
@@ -213,7 +214,7 @@ func (p *mldsaVerifyPrecompile) Run(
|
||||
// Validate total input size
|
||||
expectedSize := uint64(sigEnd) + messageLen
|
||||
if uint64(len(input)) != expectedSize {
|
||||
return nil, suppliedGas - gasCost, fmt.Errorf("%w: expected %d bytes total, got %d",
|
||||
return nil, remainingGas, fmt.Errorf("%w: expected %d bytes total, got %d",
|
||||
ErrInvalidInputLength, expectedSize, len(input))
|
||||
}
|
||||
|
||||
@@ -233,7 +234,7 @@ func (p *mldsaVerifyPrecompile) Run(
|
||||
// CPU fallback: Parse public key and verify
|
||||
pub, err := mldsa.PublicKeyFromBytes(publicKey, mldsaMode)
|
||||
if err != nil {
|
||||
return nil, suppliedGas - gasCost, fmt.Errorf("invalid public key: %w", err)
|
||||
return nil, remainingGas, fmt.Errorf("invalid public key: %w", err)
|
||||
}
|
||||
valid = pub.Verify(message, signature, nil)
|
||||
}
|
||||
@@ -244,7 +245,7 @@ func (p *mldsaVerifyPrecompile) Run(
|
||||
result[31] = 1
|
||||
}
|
||||
|
||||
return result, suppliedGas - gasCost, nil
|
||||
return result, remainingGas, nil
|
||||
}
|
||||
|
||||
// runBatchVerify verifies N ML-DSA signatures in a single precompile call.
|
||||
@@ -262,7 +263,10 @@ func (p *mldsaVerifyPrecompile) Run(
|
||||
//
|
||||
// Output: count bytes, each 0x00 (invalid) or 0x01 (valid), left-padded to 32-byte alignment
|
||||
func (p *mldsaVerifyPrecompile) runBatchVerify(input []byte, suppliedGas, gasCost uint64) ([]byte, uint64, error) {
|
||||
remainingGas := suppliedGas - gasCost
|
||||
remainingGas, err := contract.DeductGas(suppliedGas, gasCost)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
// Minimum header: op(1) + mode(1) + count(2) = 4
|
||||
if len(input) < 4 {
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
|
||||
"github.com/luxfi/crypto/mldsa"
|
||||
"github.com/luxfi/geth/common"
|
||||
"github.com/luxfi/precompile/contract"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
@@ -183,7 +184,7 @@ func TestMLDSAVerify_InputTooShort(t *testing.T) {
|
||||
|
||||
require.Error(t, err)
|
||||
require.Nil(t, ret)
|
||||
require.Contains(t, err.Error(), "invalid input length")
|
||||
require.ErrorIs(t, err, contract.ErrInvalidInput)
|
||||
}
|
||||
|
||||
func TestMLDSAVerify_InvalidMode(t *testing.T) {
|
||||
|
||||
+7
-7
@@ -76,7 +76,7 @@ var (
|
||||
|
||||
_ contract.StatefulPrecompiledContract = &mlkemPrecompile{}
|
||||
|
||||
ErrInvalidInputLength = errors.New("invalid input length")
|
||||
ErrInvalidInputLength = contract.ErrInvalidInput
|
||||
ErrInvalidMode = errors.New("invalid ML-KEM mode")
|
||||
ErrUnsupportedMode = errors.New("unsupported ML-KEM mode")
|
||||
ErrUnsupportedOperation = errors.New("unsupported operation")
|
||||
@@ -188,19 +188,19 @@ func (p *mlkemPrecompile) Run(
|
||||
readOnly bool,
|
||||
) ([]byte, uint64, error) {
|
||||
gasCost := p.RequiredGas(input)
|
||||
if suppliedGas < gasCost {
|
||||
return nil, 0, contract.ErrOutOfGas
|
||||
remainingGas, err := contract.DeductGas(suppliedGas, gasCost)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
if len(input) < 2 {
|
||||
return nil, suppliedGas - gasCost, ErrInvalidInputLength
|
||||
return nil, remainingGas, ErrInvalidInputLength
|
||||
}
|
||||
|
||||
op := input[0]
|
||||
mode := input[1]
|
||||
|
||||
var result []byte
|
||||
var err error
|
||||
|
||||
switch op {
|
||||
case OpEncapsulate:
|
||||
@@ -210,10 +210,10 @@ func (p *mlkemPrecompile) Run(
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return nil, suppliedGas - gasCost, err
|
||||
return nil, remainingGas, err
|
||||
}
|
||||
|
||||
return result, suppliedGas - gasCost, nil
|
||||
return result, remainingGas, nil
|
||||
}
|
||||
|
||||
// encapsulate generates a shared secret and ciphertext from a public key.
|
||||
|
||||
+1
-1
@@ -32,7 +32,7 @@ var (
|
||||
|
||||
_ contract.StatefulPrecompiledContract = &pastaPrecompile{}
|
||||
|
||||
ErrInvalidInput = errors.New("invalid pasta input")
|
||||
ErrInvalidInput = contract.ErrInvalidInput
|
||||
ErrInvalidOp = errors.New("invalid pasta operation")
|
||||
ErrInvalidCurve = errors.New("invalid curve selector (0x01=Pallas, 0x02=Vesta)")
|
||||
ErrNotOnCurve = errors.New("point not on curve")
|
||||
|
||||
@@ -36,7 +36,7 @@ var (
|
||||
|
||||
_ contract.StatefulPrecompiledContract = &pedersenPrecompile{}
|
||||
|
||||
ErrInvalidInput = errors.New("invalid pedersen input")
|
||||
ErrInvalidInput = contract.ErrInvalidInput
|
||||
ErrInvalidOp = errors.New("invalid pedersen operation")
|
||||
ErrTooManyVals = errors.New("too many values for vector commitment (max 32)")
|
||||
)
|
||||
|
||||
@@ -32,7 +32,7 @@ var (
|
||||
|
||||
_ contract.StatefulPrecompiledContract = &poseidonPrecompile{}
|
||||
|
||||
ErrInvalidInput = errors.New("invalid poseidon input")
|
||||
ErrInvalidInput = contract.ErrInvalidInput
|
||||
ErrTooManyInputs = errors.New("too many inputs: max 16 field elements")
|
||||
ErrInvalidOp = errors.New("invalid operation selector")
|
||||
)
|
||||
|
||||
+23
-23
@@ -15,12 +15,12 @@ import (
|
||||
|
||||
const (
|
||||
// Gas costs (optimized for Verkle witnesses)
|
||||
VerkleVerifyGas = 3000 // Ultra-fast with PQ finality assumption
|
||||
BLSVerifyGas = 5000 // BLS aggregate verification
|
||||
BLSAggregateGas = 2000 // BLS signature aggregation
|
||||
CoronaVerifyGas = 8000 // Corona (ML-DSA) verification
|
||||
HybridVerifyGas = 10000 // BLS+Corona hybrid verification
|
||||
CompressedVerifyGas = 1000 // Compressed witness verification
|
||||
GasVerkleVerify = 3000 // Ultra-fast with PQ finality assumption
|
||||
GasBLSVerify = 5000 // BLS aggregate verification
|
||||
GasBLSAggregate = 2000 // BLS signature aggregation
|
||||
GasCoronaVerify = 8000 // Corona (ML-DSA) verification
|
||||
GasHybridVerify = 10000 // BLS+Corona hybrid verification
|
||||
GasCompressedVerify = 1000 // Compressed witness verification
|
||||
|
||||
// Precompile addresses
|
||||
VerkleVerifyAddress = "0x0300000000000000000000000000000000000020"
|
||||
@@ -36,7 +36,7 @@ var (
|
||||
_ contract.StatefulPrecompiledContract = &blsPrecompile{}
|
||||
_ contract.StatefulPrecompiledContract = &coronaPrecompile{}
|
||||
|
||||
ErrInvalidInput = errors.New("invalid input")
|
||||
ErrInvalidInput = contract.ErrInvalidInput
|
||||
ErrInvalidSignature = errors.New("invalid signature")
|
||||
ErrThresholdNotMet = errors.New("threshold not met")
|
||||
)
|
||||
@@ -50,14 +50,14 @@ func (v *verklePrecompile) Address() common.Address {
|
||||
|
||||
func (v *verklePrecompile) RequiredGas(input []byte) uint64 {
|
||||
// Ultra-low gas cost due to PQ finality assumption
|
||||
return VerkleVerifyGas
|
||||
return GasVerkleVerify
|
||||
}
|
||||
|
||||
func (v *verklePrecompile) Run(accessibleState contract.AccessibleState, caller common.Address, addr common.Address, input []byte, suppliedGas uint64, readOnly bool) (ret []byte, remainingGas uint64, err error) {
|
||||
if suppliedGas < VerkleVerifyGas {
|
||||
if suppliedGas < GasVerkleVerify {
|
||||
return nil, 0, contract.ErrOutOfGas
|
||||
}
|
||||
remainingGas = suppliedGas - VerkleVerifyGas
|
||||
remainingGas = suppliedGas - GasVerkleVerify
|
||||
|
||||
// Input format: [commitment(32)] [proof(32)] [threshold_met(1)]
|
||||
if len(input) < 65 {
|
||||
@@ -92,14 +92,14 @@ func (b *blsPrecompile) Address() common.Address {
|
||||
}
|
||||
|
||||
func (b *blsPrecompile) RequiredGas(input []byte) uint64 {
|
||||
return BLSVerifyGas
|
||||
return GasBLSVerify
|
||||
}
|
||||
|
||||
func (b *blsPrecompile) Run(accessibleState contract.AccessibleState, caller common.Address, addr common.Address, input []byte, suppliedGas uint64, readOnly bool) (ret []byte, remainingGas uint64, err error) {
|
||||
if suppliedGas < BLSVerifyGas {
|
||||
if suppliedGas < GasBLSVerify {
|
||||
return nil, 0, contract.ErrOutOfGas
|
||||
}
|
||||
remainingGas = suppliedGas - BLSVerifyGas
|
||||
remainingGas = suppliedGas - GasBLSVerify
|
||||
|
||||
// Input format: [pubkey(48)] [message(32)] [signature(96)]
|
||||
if len(input) < 176 {
|
||||
@@ -146,7 +146,7 @@ func (b *blsAggregatePrecompile) Address() common.Address {
|
||||
func (b *blsAggregatePrecompile) RequiredGas(input []byte) uint64 {
|
||||
// Gas scales with number of signatures
|
||||
numSigs := len(input) / 96
|
||||
return BLSAggregateGas * uint64(numSigs)
|
||||
return GasBLSAggregate * uint64(numSigs)
|
||||
}
|
||||
|
||||
func (b *blsAggregatePrecompile) Run(accessibleState contract.AccessibleState, caller common.Address, addr common.Address, input []byte, suppliedGas uint64, readOnly bool) (ret []byte, remainingGas uint64, err error) {
|
||||
@@ -197,14 +197,14 @@ func (r *coronaPrecompile) Address() common.Address {
|
||||
}
|
||||
|
||||
func (r *coronaPrecompile) RequiredGas(input []byte) uint64 {
|
||||
return CoronaVerifyGas
|
||||
return GasCoronaVerify
|
||||
}
|
||||
|
||||
func (r *coronaPrecompile) Run(accessibleState contract.AccessibleState, caller common.Address, addr common.Address, input []byte, suppliedGas uint64, readOnly bool) (ret []byte, remainingGas uint64, err error) {
|
||||
if suppliedGas < CoronaVerifyGas {
|
||||
if suppliedGas < GasCoronaVerify {
|
||||
return nil, 0, contract.ErrOutOfGas
|
||||
}
|
||||
remainingGas = suppliedGas - CoronaVerifyGas
|
||||
remainingGas = suppliedGas - GasCoronaVerify
|
||||
|
||||
// Input format: [mode(1)] [pubkey_len(2)] [pubkey] [msg_len(2)] [msg] [sig]
|
||||
if len(input) < 6 {
|
||||
@@ -257,14 +257,14 @@ func (h *hybridPrecompile) Address() common.Address {
|
||||
}
|
||||
|
||||
func (h *hybridPrecompile) RequiredGas(input []byte) uint64 {
|
||||
return HybridVerifyGas
|
||||
return GasHybridVerify
|
||||
}
|
||||
|
||||
func (h *hybridPrecompile) Run(accessibleState contract.AccessibleState, caller common.Address, addr common.Address, input []byte, suppliedGas uint64, readOnly bool) (ret []byte, remainingGas uint64, err error) {
|
||||
if suppliedGas < HybridVerifyGas {
|
||||
if suppliedGas < GasHybridVerify {
|
||||
return nil, 0, contract.ErrOutOfGas
|
||||
}
|
||||
remainingGas = suppliedGas - HybridVerifyGas
|
||||
remainingGas = suppliedGas - GasHybridVerify
|
||||
|
||||
// Input format: [bls_sig(96)] [corona_sig_len(2)] [corona_sig] [message(32)] [bls_pubkey(48)] [corona_pubkey]
|
||||
if len(input) < 178 {
|
||||
@@ -336,14 +336,14 @@ func (c *compressedPrecompile) Address() common.Address {
|
||||
}
|
||||
|
||||
func (c *compressedPrecompile) RequiredGas(input []byte) uint64 {
|
||||
return CompressedVerifyGas // Ultra-low gas
|
||||
return GasCompressedVerify // Ultra-low gas
|
||||
}
|
||||
|
||||
func (c *compressedPrecompile) Run(accessibleState contract.AccessibleState, caller common.Address, addr common.Address, input []byte, suppliedGas uint64, readOnly bool) (ret []byte, remainingGas uint64, err error) {
|
||||
if suppliedGas < CompressedVerifyGas {
|
||||
if suppliedGas < GasCompressedVerify {
|
||||
return nil, 0, contract.ErrOutOfGas
|
||||
}
|
||||
remainingGas = suppliedGas - CompressedVerifyGas
|
||||
remainingGas = suppliedGas - GasCompressedVerify
|
||||
|
||||
// Input format: [commitment(16)] [proof(16)] [metadata(8)] [validators(4)]
|
||||
if len(input) < 44 {
|
||||
|
||||
+44
-44
@@ -23,8 +23,8 @@ func TestVerklePrecompile_Address(t *testing.T) {
|
||||
|
||||
func TestVerklePrecompile_RequiredGas(t *testing.T) {
|
||||
v := &verklePrecompile{}
|
||||
require.Equal(t, uint64(VerkleVerifyGas), v.RequiredGas(nil))
|
||||
require.Equal(t, uint64(VerkleVerifyGas), v.RequiredGas(make([]byte, 100)))
|
||||
require.Equal(t, uint64(GasVerkleVerify), v.RequiredGas(nil))
|
||||
require.Equal(t, uint64(GasVerkleVerify), v.RequiredGas(make([]byte, 100)))
|
||||
}
|
||||
|
||||
func TestVerklePrecompile_ValidWitness(t *testing.T) {
|
||||
@@ -43,7 +43,7 @@ func TestVerklePrecompile_ValidWitness(t *testing.T) {
|
||||
input := append(commitment, proof...)
|
||||
input = append(input, thresholdMet)
|
||||
|
||||
ret, remainingGas, err := v.Run(nil, common.Address{}, v.Address(), input, VerkleVerifyGas, true)
|
||||
ret, remainingGas, err := v.Run(nil, common.Address{}, v.Address(), input, GasVerkleVerify, true)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, uint64(0), remainingGas)
|
||||
require.Equal(t, []byte{1}, ret)
|
||||
@@ -60,7 +60,7 @@ func TestVerklePrecompile_ThresholdNotMet(t *testing.T) {
|
||||
input := append(commitment, proof...)
|
||||
input = append(input, thresholdMet)
|
||||
|
||||
ret, remainingGas, err := v.Run(nil, common.Address{}, v.Address(), input, VerkleVerifyGas, true)
|
||||
ret, remainingGas, err := v.Run(nil, common.Address{}, v.Address(), input, GasVerkleVerify, true)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, uint64(0), remainingGas)
|
||||
require.Equal(t, []byte{0}, ret) // Returns false
|
||||
@@ -83,7 +83,7 @@ func TestVerklePrecompile_InvalidProof(t *testing.T) {
|
||||
input := append(commitment, proof...)
|
||||
input = append(input, thresholdMet)
|
||||
|
||||
ret, remainingGas, err := v.Run(nil, common.Address{}, v.Address(), input, VerkleVerifyGas, true)
|
||||
ret, remainingGas, err := v.Run(nil, common.Address{}, v.Address(), input, GasVerkleVerify, true)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, uint64(0), remainingGas)
|
||||
require.Equal(t, []byte{0}, ret) // Returns false due to mismatch
|
||||
@@ -95,7 +95,7 @@ func TestVerklePrecompile_InputTooShort(t *testing.T) {
|
||||
// Input less than 65 bytes
|
||||
input := make([]byte, 64)
|
||||
|
||||
ret, remainingGas, err := v.Run(nil, common.Address{}, v.Address(), input, VerkleVerifyGas, true)
|
||||
ret, remainingGas, err := v.Run(nil, common.Address{}, v.Address(), input, GasVerkleVerify, true)
|
||||
require.ErrorIs(t, err, ErrInvalidInput)
|
||||
require.Nil(t, ret)
|
||||
require.Equal(t, uint64(0), remainingGas)
|
||||
@@ -106,7 +106,7 @@ func TestVerklePrecompile_OutOfGas(t *testing.T) {
|
||||
|
||||
input := make([]byte, 65)
|
||||
|
||||
ret, remainingGas, err := v.Run(nil, common.Address{}, v.Address(), input, VerkleVerifyGas-1, true)
|
||||
ret, remainingGas, err := v.Run(nil, common.Address{}, v.Address(), input, GasVerkleVerify-1, true)
|
||||
require.ErrorIs(t, err, contract.ErrOutOfGas)
|
||||
require.Nil(t, ret)
|
||||
require.Equal(t, uint64(0), remainingGas)
|
||||
@@ -124,8 +124,8 @@ func TestBLSPrecompile_Address(t *testing.T) {
|
||||
|
||||
func TestBLSPrecompile_RequiredGas(t *testing.T) {
|
||||
b := &blsPrecompile{}
|
||||
require.Equal(t, uint64(BLSVerifyGas), b.RequiredGas(nil))
|
||||
require.Equal(t, uint64(BLSVerifyGas), b.RequiredGas(make([]byte, 200)))
|
||||
require.Equal(t, uint64(GasBLSVerify), b.RequiredGas(nil))
|
||||
require.Equal(t, uint64(GasBLSVerify), b.RequiredGas(make([]byte, 200)))
|
||||
}
|
||||
|
||||
func TestBLSPrecompile_InputTooShort(t *testing.T) {
|
||||
@@ -146,7 +146,7 @@ func TestBLSPrecompile_InputTooShort(t *testing.T) {
|
||||
for _, tc := range tests {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
input := make([]byte, tc.size)
|
||||
ret, remainingGas, err := b.Run(nil, common.Address{}, b.Address(), input, BLSVerifyGas, true)
|
||||
ret, remainingGas, err := b.Run(nil, common.Address{}, b.Address(), input, GasBLSVerify, true)
|
||||
require.ErrorIs(t, err, ErrInvalidInput)
|
||||
require.Nil(t, ret)
|
||||
require.Equal(t, uint64(0), remainingGas)
|
||||
@@ -160,7 +160,7 @@ func TestBLSPrecompile_InvalidPubKey(t *testing.T) {
|
||||
// Create invalid pubkey (all zeros is not a valid BLS pubkey)
|
||||
input := make([]byte, 176)
|
||||
|
||||
ret, remainingGas, err := b.Run(nil, common.Address{}, b.Address(), input, BLSVerifyGas, true)
|
||||
ret, remainingGas, err := b.Run(nil, common.Address{}, b.Address(), input, GasBLSVerify, true)
|
||||
require.NoError(t, err) // No error, just returns 0
|
||||
require.Equal(t, []byte{0}, ret)
|
||||
require.Equal(t, uint64(0), remainingGas)
|
||||
@@ -171,7 +171,7 @@ func TestBLSPrecompile_OutOfGas(t *testing.T) {
|
||||
|
||||
input := make([]byte, 176)
|
||||
|
||||
ret, remainingGas, err := b.Run(nil, common.Address{}, b.Address(), input, BLSVerifyGas-1, true)
|
||||
ret, remainingGas, err := b.Run(nil, common.Address{}, b.Address(), input, GasBLSVerify-1, true)
|
||||
require.ErrorIs(t, err, contract.ErrOutOfGas)
|
||||
require.Nil(t, ret)
|
||||
require.Equal(t, uint64(0), remainingGas)
|
||||
@@ -196,9 +196,9 @@ func TestBLSAggregatePrecompile_RequiredGas(t *testing.T) {
|
||||
expected uint64
|
||||
}{
|
||||
{"empty", 0, 0},
|
||||
{"one_sig", 1, BLSAggregateGas},
|
||||
{"two_sigs", 2, 2 * BLSAggregateGas},
|
||||
{"ten_sigs", 10, 10 * BLSAggregateGas},
|
||||
{"one_sig", 1, GasBLSAggregate},
|
||||
{"two_sigs", 2, 2 * GasBLSAggregate},
|
||||
{"ten_sigs", 10, 10 * GasBLSAggregate},
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
@@ -272,8 +272,8 @@ func TestCoronaPrecompile_Address(t *testing.T) {
|
||||
|
||||
func TestCoronaPrecompile_RequiredGas(t *testing.T) {
|
||||
r := &coronaPrecompile{}
|
||||
require.Equal(t, uint64(CoronaVerifyGas), r.RequiredGas(nil))
|
||||
require.Equal(t, uint64(CoronaVerifyGas), r.RequiredGas(make([]byte, 5000)))
|
||||
require.Equal(t, uint64(GasCoronaVerify), r.RequiredGas(nil))
|
||||
require.Equal(t, uint64(GasCoronaVerify), r.RequiredGas(make([]byte, 5000)))
|
||||
}
|
||||
|
||||
func TestCoronaPrecompile_InputTooShort(t *testing.T) {
|
||||
@@ -293,7 +293,7 @@ func TestCoronaPrecompile_InputTooShort(t *testing.T) {
|
||||
for _, tc := range tests {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
input := make([]byte, tc.size)
|
||||
ret, _, err := r.Run(nil, common.Address{}, r.Address(), input, CoronaVerifyGas, true)
|
||||
ret, _, err := r.Run(nil, common.Address{}, r.Address(), input, GasCoronaVerify, true)
|
||||
require.ErrorIs(t, err, ErrInvalidInput)
|
||||
require.Nil(t, ret)
|
||||
})
|
||||
@@ -310,7 +310,7 @@ func TestCoronaPrecompile_TruncatedPubKey(t *testing.T) {
|
||||
input[2] = 0x20 // pubkey_len low byte = 32
|
||||
// Only 7 bytes of pubkey follow (input[3:10])
|
||||
|
||||
ret, _, err := r.Run(nil, common.Address{}, r.Address(), input, CoronaVerifyGas, true)
|
||||
ret, _, err := r.Run(nil, common.Address{}, r.Address(), input, GasCoronaVerify, true)
|
||||
require.ErrorIs(t, err, ErrInvalidInput)
|
||||
require.Nil(t, ret)
|
||||
}
|
||||
@@ -329,7 +329,7 @@ func TestCoronaPrecompile_TruncatedMessage(t *testing.T) {
|
||||
input[3+pubKeyLen+1] = 0x10 // msg_len low = 16
|
||||
// No message bytes follow
|
||||
|
||||
ret, _, err := r.Run(nil, common.Address{}, r.Address(), input, CoronaVerifyGas, true)
|
||||
ret, _, err := r.Run(nil, common.Address{}, r.Address(), input, GasCoronaVerify, true)
|
||||
require.ErrorIs(t, err, ErrInvalidInput)
|
||||
require.Nil(t, ret)
|
||||
}
|
||||
@@ -350,7 +350,7 @@ func TestCoronaPrecompile_InvalidPubKey(t *testing.T) {
|
||||
input[3+pubKeyLen+1] = byte(msgLen & 0xFF)
|
||||
// Leave everything zeros - invalid pubkey
|
||||
|
||||
ret, _, err := r.Run(nil, common.Address{}, r.Address(), input, CoronaVerifyGas, true)
|
||||
ret, _, err := r.Run(nil, common.Address{}, r.Address(), input, GasCoronaVerify, true)
|
||||
require.NoError(t, err) // No error, returns 0
|
||||
require.Equal(t, []byte{0}, ret)
|
||||
}
|
||||
@@ -360,7 +360,7 @@ func TestCoronaPrecompile_OutOfGas(t *testing.T) {
|
||||
|
||||
input := make([]byte, 100)
|
||||
|
||||
ret, remainingGas, err := r.Run(nil, common.Address{}, r.Address(), input, CoronaVerifyGas-1, true)
|
||||
ret, remainingGas, err := r.Run(nil, common.Address{}, r.Address(), input, GasCoronaVerify-1, true)
|
||||
require.ErrorIs(t, err, contract.ErrOutOfGas)
|
||||
require.Nil(t, ret)
|
||||
require.Equal(t, uint64(0), remainingGas)
|
||||
@@ -378,8 +378,8 @@ func TestHybridPrecompile_Address(t *testing.T) {
|
||||
|
||||
func TestHybridPrecompile_RequiredGas(t *testing.T) {
|
||||
h := &hybridPrecompile{}
|
||||
require.Equal(t, uint64(HybridVerifyGas), h.RequiredGas(nil))
|
||||
require.Equal(t, uint64(HybridVerifyGas), h.RequiredGas(make([]byte, 1000)))
|
||||
require.Equal(t, uint64(GasHybridVerify), h.RequiredGas(nil))
|
||||
require.Equal(t, uint64(GasHybridVerify), h.RequiredGas(make([]byte, 1000)))
|
||||
}
|
||||
|
||||
func TestHybridPrecompile_InputTooShort(t *testing.T) {
|
||||
@@ -400,7 +400,7 @@ func TestHybridPrecompile_InputTooShort(t *testing.T) {
|
||||
for _, tc := range tests {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
input := make([]byte, tc.size)
|
||||
ret, _, err := h.Run(nil, common.Address{}, h.Address(), input, HybridVerifyGas, true)
|
||||
ret, _, err := h.Run(nil, common.Address{}, h.Address(), input, GasHybridVerify, true)
|
||||
require.ErrorIs(t, err, ErrInvalidInput)
|
||||
require.Nil(t, ret)
|
||||
})
|
||||
@@ -416,7 +416,7 @@ func TestHybridPrecompile_TruncatedCoronaSig(t *testing.T) {
|
||||
input[96] = 0x10 // corona_sig_len high = 4096 (way larger than remaining)
|
||||
input[97] = 0x00 // corona_sig_len low
|
||||
|
||||
ret, _, err := h.Run(nil, common.Address{}, h.Address(), input, HybridVerifyGas, true)
|
||||
ret, _, err := h.Run(nil, common.Address{}, h.Address(), input, GasHybridVerify, true)
|
||||
require.ErrorIs(t, err, ErrInvalidInput)
|
||||
require.Nil(t, ret)
|
||||
}
|
||||
@@ -433,7 +433,7 @@ func TestHybridPrecompile_InvalidBLSPubKey(t *testing.T) {
|
||||
input[97] = byte(coronaSigLen & 0xFF)
|
||||
// All zeros = invalid keys
|
||||
|
||||
ret, _, err := h.Run(nil, common.Address{}, h.Address(), input, HybridVerifyGas, true)
|
||||
ret, _, err := h.Run(nil, common.Address{}, h.Address(), input, GasHybridVerify, true)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, []byte{0}, ret) // Returns false
|
||||
}
|
||||
@@ -445,7 +445,7 @@ func TestHybridPrecompile_OutOfGas(t *testing.T) {
|
||||
input[96] = 0x00
|
||||
input[97] = 0x10 // corona_sig_len = 16
|
||||
|
||||
ret, remainingGas, err := h.Run(nil, common.Address{}, h.Address(), input, HybridVerifyGas-1, true)
|
||||
ret, remainingGas, err := h.Run(nil, common.Address{}, h.Address(), input, GasHybridVerify-1, true)
|
||||
require.ErrorIs(t, err, contract.ErrOutOfGas)
|
||||
require.Nil(t, ret)
|
||||
require.Equal(t, uint64(0), remainingGas)
|
||||
@@ -463,8 +463,8 @@ func TestCompressedPrecompile_Address(t *testing.T) {
|
||||
|
||||
func TestCompressedPrecompile_RequiredGas(t *testing.T) {
|
||||
c := &compressedPrecompile{}
|
||||
require.Equal(t, uint64(CompressedVerifyGas), c.RequiredGas(nil))
|
||||
require.Equal(t, uint64(CompressedVerifyGas), c.RequiredGas(make([]byte, 100)))
|
||||
require.Equal(t, uint64(GasCompressedVerify), c.RequiredGas(nil))
|
||||
require.Equal(t, uint64(GasCompressedVerify), c.RequiredGas(make([]byte, 100)))
|
||||
}
|
||||
|
||||
func TestCompressedPrecompile_ThresholdMet(t *testing.T) {
|
||||
@@ -480,7 +480,7 @@ func TestCompressedPrecompile_ThresholdMet(t *testing.T) {
|
||||
input[42] = 0xFF
|
||||
input[43] = 0xFF
|
||||
|
||||
ret, remainingGas, err := c.Run(nil, common.Address{}, c.Address(), input, CompressedVerifyGas, true)
|
||||
ret, remainingGas, err := c.Run(nil, common.Address{}, c.Address(), input, GasCompressedVerify, true)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, uint64(0), remainingGas)
|
||||
require.Equal(t, []byte{1}, ret) // Threshold met
|
||||
@@ -499,7 +499,7 @@ func TestCompressedPrecompile_ThresholdNotMet(t *testing.T) {
|
||||
input[42] = 0x00
|
||||
input[43] = 0x00
|
||||
|
||||
ret, remainingGas, err := c.Run(nil, common.Address{}, c.Address(), input, CompressedVerifyGas, true)
|
||||
ret, remainingGas, err := c.Run(nil, common.Address{}, c.Address(), input, GasCompressedVerify, true)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, uint64(0), remainingGas)
|
||||
require.Equal(t, []byte{0}, ret) // Threshold not met
|
||||
@@ -517,7 +517,7 @@ func TestCompressedPrecompile_ExactThreshold(t *testing.T) {
|
||||
input[42] = 0x3F
|
||||
input[43] = 0x00
|
||||
|
||||
ret, remainingGas, err := c.Run(nil, common.Address{}, c.Address(), input, CompressedVerifyGas, true)
|
||||
ret, remainingGas, err := c.Run(nil, common.Address{}, c.Address(), input, GasCompressedVerify, true)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, uint64(0), remainingGas)
|
||||
require.Equal(t, []byte{1}, ret) // Exactly at threshold
|
||||
@@ -535,7 +535,7 @@ func TestCompressedPrecompile_JustBelowThreshold(t *testing.T) {
|
||||
input[42] = 0x1F
|
||||
input[43] = 0x00
|
||||
|
||||
ret, remainingGas, err := c.Run(nil, common.Address{}, c.Address(), input, CompressedVerifyGas, true)
|
||||
ret, remainingGas, err := c.Run(nil, common.Address{}, c.Address(), input, GasCompressedVerify, true)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, uint64(0), remainingGas)
|
||||
require.Equal(t, []byte{0}, ret) // Below threshold
|
||||
@@ -552,7 +552,7 @@ func TestCompressedPrecompile_AllValidators(t *testing.T) {
|
||||
input[42] = 0xFF
|
||||
input[43] = 0xFF
|
||||
|
||||
ret, remainingGas, err := c.Run(nil, common.Address{}, c.Address(), input, CompressedVerifyGas, true)
|
||||
ret, remainingGas, err := c.Run(nil, common.Address{}, c.Address(), input, GasCompressedVerify, true)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, uint64(0), remainingGas)
|
||||
require.Equal(t, []byte{1}, ret)
|
||||
@@ -563,7 +563,7 @@ func TestCompressedPrecompile_NoValidators(t *testing.T) {
|
||||
|
||||
input := make([]byte, 44) // All zeros
|
||||
|
||||
ret, remainingGas, err := c.Run(nil, common.Address{}, c.Address(), input, CompressedVerifyGas, true)
|
||||
ret, remainingGas, err := c.Run(nil, common.Address{}, c.Address(), input, GasCompressedVerify, true)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, uint64(0), remainingGas)
|
||||
require.Equal(t, []byte{0}, ret)
|
||||
@@ -586,7 +586,7 @@ func TestCompressedPrecompile_InputTooShort(t *testing.T) {
|
||||
for _, tc := range tests {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
input := make([]byte, tc.size)
|
||||
ret, _, err := c.Run(nil, common.Address{}, c.Address(), input, CompressedVerifyGas, true)
|
||||
ret, _, err := c.Run(nil, common.Address{}, c.Address(), input, GasCompressedVerify, true)
|
||||
require.ErrorIs(t, err, ErrInvalidInput)
|
||||
require.Nil(t, ret)
|
||||
})
|
||||
@@ -598,7 +598,7 @@ func TestCompressedPrecompile_OutOfGas(t *testing.T) {
|
||||
|
||||
input := make([]byte, 44)
|
||||
|
||||
ret, remainingGas, err := c.Run(nil, common.Address{}, c.Address(), input, CompressedVerifyGas-1, true)
|
||||
ret, remainingGas, err := c.Run(nil, common.Address{}, c.Address(), input, GasCompressedVerify-1, true)
|
||||
require.ErrorIs(t, err, contract.ErrOutOfGas)
|
||||
require.Nil(t, ret)
|
||||
require.Equal(t, uint64(0), remainingGas)
|
||||
@@ -700,7 +700,7 @@ func TestGasConsumption_Verkle(t *testing.T) {
|
||||
input := make([]byte, 65)
|
||||
input[64] = 1 // threshold met
|
||||
|
||||
suppliedGas := uint64(VerkleVerifyGas + 1000)
|
||||
suppliedGas := uint64(GasVerkleVerify + 1000)
|
||||
_, remainingGas, err := v.Run(nil, common.Address{}, v.Address(), input, suppliedGas, true)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, uint64(1000), remainingGas)
|
||||
@@ -710,7 +710,7 @@ func TestGasConsumption_BLS(t *testing.T) {
|
||||
b := &blsPrecompile{}
|
||||
input := make([]byte, 176)
|
||||
|
||||
suppliedGas := uint64(BLSVerifyGas + 500)
|
||||
suppliedGas := uint64(GasBLSVerify + 500)
|
||||
_, remainingGas, err := b.Run(nil, common.Address{}, b.Address(), input, suppliedGas, true)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, uint64(500), remainingGas)
|
||||
@@ -720,7 +720,7 @@ func TestGasConsumption_Compressed(t *testing.T) {
|
||||
c := &compressedPrecompile{}
|
||||
input := make([]byte, 44)
|
||||
|
||||
suppliedGas := uint64(CompressedVerifyGas + 200)
|
||||
suppliedGas := uint64(GasCompressedVerify + 200)
|
||||
_, remainingGas, err := c.Run(nil, common.Address{}, c.Address(), input, suppliedGas, true)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, uint64(200), remainingGas)
|
||||
@@ -741,7 +741,7 @@ func BenchmarkVerkleVerify(b *testing.B) {
|
||||
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
_, _, _ = v.Run(nil, common.Address{}, v.Address(), input, VerkleVerifyGas, true)
|
||||
_, _, _ = v.Run(nil, common.Address{}, v.Address(), input, GasVerkleVerify, true)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -755,11 +755,11 @@ func BenchmarkCompressedVerify(b *testing.B) {
|
||||
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
_, _, _ = c.Run(nil, common.Address{}, c.Address(), input, CompressedVerifyGas, true)
|
||||
_, _, _ = c.Run(nil, common.Address{}, c.Address(), input, GasCompressedVerify, true)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkBLSAggregateGas(b *testing.B) {
|
||||
func BenchmarkGasBLSAggregate(b *testing.B) {
|
||||
ba := &blsAggregatePrecompile{}
|
||||
|
||||
for _, numSigs := range []int{1, 10, 100} {
|
||||
|
||||
+10
-10
@@ -20,7 +20,7 @@ func TestDeep_Verkle_ExactGasConsumption(t *testing.T) {
|
||||
input[64] = 1
|
||||
|
||||
for _, extra := range []uint64{0, 1, 100, 10000} {
|
||||
_, remaining, err := v.Run(nil, common.Address{}, v.Address(), input, VerkleVerifyGas+extra, true)
|
||||
_, remaining, err := v.Run(nil, common.Address{}, v.Address(), input, GasVerkleVerify+extra, true)
|
||||
require.NoError(t, err, "extra=%d", extra)
|
||||
require.Equal(t, extra, remaining, "extra=%d", extra)
|
||||
}
|
||||
@@ -45,7 +45,7 @@ func TestDeep_Verkle_Concurrent(t *testing.T) {
|
||||
var wg sync.WaitGroup
|
||||
for range 50 {
|
||||
wg.Go(func() {
|
||||
ret, _, err := v.Run(nil, common.Address{}, v.Address(), input, VerkleVerifyGas+100, true)
|
||||
ret, _, err := v.Run(nil, common.Address{}, v.Address(), input, GasVerkleVerify+100, true)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, []byte{1}, ret)
|
||||
})
|
||||
@@ -65,14 +65,14 @@ func TestDeep_BLS_GasZero(t *testing.T) {
|
||||
func TestDeep_BLS_ExactGas(t *testing.T) {
|
||||
b := &blsPrecompile{}
|
||||
input := make([]byte, 176)
|
||||
_, remaining, err := b.Run(nil, common.Address{}, b.Address(), input, BLSVerifyGas, true)
|
||||
_, remaining, err := b.Run(nil, common.Address{}, b.Address(), input, GasBLSVerify, true)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, uint64(0), remaining)
|
||||
}
|
||||
|
||||
func TestDeep_BLS_EmptyInput(t *testing.T) {
|
||||
b := &blsPrecompile{}
|
||||
_, _, err := b.Run(nil, common.Address{}, b.Address(), nil, BLSVerifyGas, true)
|
||||
_, _, err := b.Run(nil, common.Address{}, b.Address(), nil, GasBLSVerify, true)
|
||||
require.Error(t, err)
|
||||
}
|
||||
|
||||
@@ -83,7 +83,7 @@ func TestDeep_BLS_Concurrent(t *testing.T) {
|
||||
var wg sync.WaitGroup
|
||||
for range 50 {
|
||||
wg.Go(func() {
|
||||
_, _, err := b.Run(nil, common.Address{}, b.Address(), input, BLSVerifyGas+100, true)
|
||||
_, _, err := b.Run(nil, common.Address{}, b.Address(), input, GasBLSVerify+100, true)
|
||||
require.NoError(t, err)
|
||||
})
|
||||
}
|
||||
@@ -116,7 +116,7 @@ func TestDeep_Corona_GasZero(t *testing.T) {
|
||||
|
||||
func TestDeep_Corona_EmptyInput(t *testing.T) {
|
||||
r := &coronaPrecompile{}
|
||||
_, _, err := r.Run(nil, common.Address{}, r.Address(), nil, CoronaVerifyGas, true)
|
||||
_, _, err := r.Run(nil, common.Address{}, r.Address(), nil, GasCoronaVerify, true)
|
||||
require.Error(t, err)
|
||||
}
|
||||
|
||||
@@ -131,7 +131,7 @@ func TestDeep_Hybrid_GasZero(t *testing.T) {
|
||||
|
||||
func TestDeep_Hybrid_EmptyInput(t *testing.T) {
|
||||
h := &hybridPrecompile{}
|
||||
_, _, err := h.Run(nil, common.Address{}, h.Address(), nil, HybridVerifyGas, true)
|
||||
_, _, err := h.Run(nil, common.Address{}, h.Address(), nil, GasHybridVerify, true)
|
||||
require.Error(t, err)
|
||||
}
|
||||
|
||||
@@ -146,7 +146,7 @@ func TestDeep_Compressed_GasZero(t *testing.T) {
|
||||
|
||||
func TestDeep_Compressed_EmptyInput(t *testing.T) {
|
||||
c := &compressedPrecompile{}
|
||||
_, _, err := c.Run(nil, common.Address{}, c.Address(), nil, CompressedVerifyGas, true)
|
||||
_, _, err := c.Run(nil, common.Address{}, c.Address(), nil, GasCompressedVerify, true)
|
||||
require.Error(t, err)
|
||||
}
|
||||
|
||||
@@ -164,7 +164,7 @@ func FuzzVerkle(f *testing.F) {
|
||||
t.Errorf("panic: %v", r)
|
||||
}
|
||||
}()
|
||||
v.Run(nil, common.Address{}, v.Address(), input, VerkleVerifyGas+100000, true)
|
||||
v.Run(nil, common.Address{}, v.Address(), input, GasVerkleVerify+100000, true)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -179,6 +179,6 @@ func FuzzCompressed(f *testing.F) {
|
||||
t.Errorf("panic: %v", r)
|
||||
}
|
||||
}()
|
||||
c.Run(nil, common.Address{}, c.Address(), input, CompressedVerifyGas+100000, true)
|
||||
c.Run(nil, common.Address{}, c.Address(), input, GasCompressedVerify+100000, true)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -13,9 +13,12 @@ import (
|
||||
// PRECOMPILE ADDRESS SCHEME - Aligned with LP Numbering (LP-0099)
|
||||
// ============================================================================
|
||||
//
|
||||
// All Lux-native precompiles use trailing-significant 20-byte addresses:
|
||||
// LP-numbered precompiles (Pages 2-9) use trailing-significant 20-byte addresses:
|
||||
// Format: 0x0000000000000000000000000000000000PCII
|
||||
//
|
||||
// Legacy module ranges (math 0x0400..0050, stableswap 0x0400..0060) use
|
||||
// leading-significant addresses and are NOT part of the PCII scheme.
|
||||
//
|
||||
// The address ends with the 16-bit LP number (PCII) for easy identification.
|
||||
// The selector encodes:
|
||||
// 0x 0000...0000 P C II
|
||||
@@ -314,7 +317,7 @@ const (
|
||||
|
||||
// PrecompileAddress calculates address from (P, C, II) nibbles
|
||||
// P = Family page (aligned with LP-Pxxx), C = Chain slot, II = Item
|
||||
// Returns trailing-significant format: 0x0000000000000000000000000000000000PCII
|
||||
// Returns PCII address: 0x0000000000000000000000000000000000PCII
|
||||
// The address ends with the LP number (e.g., 9200 for LP-9200 PoolManager)
|
||||
func PrecompileAddress(p, c, ii uint8) common.Address {
|
||||
if p > 15 || c > 15 {
|
||||
|
||||
+7
-7
@@ -43,7 +43,7 @@ var (
|
||||
|
||||
_ contract.StatefulPrecompiledContract = &ringSignaturePrecompile{}
|
||||
|
||||
ErrInvalidInput = errors.New("invalid ring signature input")
|
||||
ErrInvalidInput = contract.ErrInvalidInput
|
||||
ErrInvalidScheme = errors.New("invalid signature scheme")
|
||||
ErrInvalidRingSize = errors.New("ring size must be >= 2")
|
||||
ErrInvalidSignature = errors.New("invalid ring signature")
|
||||
@@ -125,19 +125,19 @@ func (p *ringSignaturePrecompile) Run(
|
||||
readOnly bool,
|
||||
) ([]byte, uint64, error) {
|
||||
gasCost := p.RequiredGas(input)
|
||||
if suppliedGas < gasCost {
|
||||
return nil, 0, contract.ErrOutOfGas
|
||||
remainingGas, err := contract.DeductGas(suppliedGas, gasCost)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
if len(input) < 3 {
|
||||
return nil, suppliedGas - gasCost, ErrInvalidInput
|
||||
return nil, remainingGas, ErrInvalidInput
|
||||
}
|
||||
|
||||
op := input[0]
|
||||
scheme := input[1]
|
||||
|
||||
var result []byte
|
||||
var err error
|
||||
|
||||
switch op {
|
||||
case OpVerify:
|
||||
@@ -147,10 +147,10 @@ func (p *ringSignaturePrecompile) Run(
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return nil, suppliedGas - gasCost, err
|
||||
return nil, remainingGas, err
|
||||
}
|
||||
|
||||
return result, suppliedGas - gasCost, nil
|
||||
return result, remainingGas, nil
|
||||
}
|
||||
|
||||
// LSAGSignature represents an LSAG ring signature
|
||||
|
||||
@@ -6,11 +6,11 @@ package secp256r1
|
||||
import (
|
||||
"crypto/ecdsa"
|
||||
"crypto/elliptic"
|
||||
"errors"
|
||||
"math/big"
|
||||
|
||||
"github.com/luxfi/accel"
|
||||
"github.com/luxfi/geth/common"
|
||||
"github.com/luxfi/precompile/contract"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -18,9 +18,9 @@ const (
|
||||
// Matches RIP-7212/EIP-7212 for cross-ecosystem compatibility
|
||||
P256VerifyAddress = "0x0000000000000000000000000000000000000100"
|
||||
|
||||
// P256VerifyGas is the gas cost for signature verification
|
||||
// GasP256Verify is the gas cost for signature verification
|
||||
// Based on EIP-7212 benchmarking - 100x cheaper than Solidity
|
||||
P256VerifyGas = 3450
|
||||
GasP256Verify = 3450
|
||||
|
||||
// InputLength is the required input length (160 bytes)
|
||||
// 32 (hash) + 32 (r) + 32 (s) + 32 (x) + 32 (y)
|
||||
@@ -35,7 +35,7 @@ var (
|
||||
successResult = common.LeftPadBytes([]byte{1}, 32)
|
||||
|
||||
// Errors
|
||||
ErrInvalidInputLength = errors.New("secp256r1: invalid input length")
|
||||
ErrInvalidInputLength = contract.ErrInvalidInput
|
||||
)
|
||||
|
||||
// Contract implements the secp256r1 signature verification precompile
|
||||
@@ -48,7 +48,7 @@ func (c *Contract) Address() common.Address {
|
||||
|
||||
// RequiredGas returns the gas required to execute the precompile
|
||||
func (c *Contract) RequiredGas(input []byte) uint64 {
|
||||
return P256VerifyGas
|
||||
return GasP256Verify
|
||||
}
|
||||
|
||||
// Run executes the secp256r1 signature verification
|
||||
|
||||
@@ -22,7 +22,7 @@ func TestContract_Address(t *testing.T) {
|
||||
|
||||
func TestContract_RequiredGas(t *testing.T) {
|
||||
c := &Contract{}
|
||||
require.Equal(t, uint64(P256VerifyGas), c.RequiredGas(make([]byte, 160)))
|
||||
require.Equal(t, uint64(GasP256Verify), c.RequiredGas(make([]byte, 160)))
|
||||
}
|
||||
|
||||
func TestContract_Name(t *testing.T) {
|
||||
|
||||
@@ -238,9 +238,9 @@ func TestRun_REqualsN(t *testing.T) {
|
||||
|
||||
func TestRequiredGas(t *testing.T) {
|
||||
c := &Contract{}
|
||||
require.Equal(t, uint64(P256VerifyGas), c.RequiredGas(nil))
|
||||
require.Equal(t, uint64(P256VerifyGas), c.RequiredGas(make([]byte, 0)))
|
||||
require.Equal(t, uint64(P256VerifyGas), c.RequiredGas(make([]byte, 1000)))
|
||||
require.Equal(t, uint64(GasP256Verify), c.RequiredGas(nil))
|
||||
require.Equal(t, uint64(GasP256Verify), c.RequiredGas(make([]byte, 0)))
|
||||
require.Equal(t, uint64(GasP256Verify), c.RequiredGas(make([]byte, 1000)))
|
||||
}
|
||||
|
||||
func TestAddress(t *testing.T) {
|
||||
|
||||
+11
-10
@@ -23,7 +23,7 @@ var (
|
||||
|
||||
_ contract.StatefulPrecompiledContract = &slhdsaVerifyPrecompile{}
|
||||
|
||||
ErrInvalidInputLength = errors.New("invalid input length")
|
||||
ErrInvalidInputLength = contract.ErrInvalidInput
|
||||
ErrInvalidMode = errors.New("invalid SLH-DSA mode")
|
||||
ErrUnsupportedMode = errors.New("unsupported SLH-DSA mode")
|
||||
)
|
||||
@@ -188,27 +188,28 @@ func (p *slhdsaVerifyPrecompile) Run(
|
||||
) ([]byte, uint64, error) {
|
||||
// Calculate required gas
|
||||
gasCost := p.RequiredGas(input)
|
||||
if suppliedGas < gasCost {
|
||||
return nil, 0, contract.ErrOutOfGas
|
||||
remainingGas, err := contract.DeductGas(suppliedGas, gasCost)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
// Minimum: mode byte + pubkey length
|
||||
minHeader := ModeByte + PubKeyLenSize
|
||||
if len(input) < minHeader {
|
||||
return nil, suppliedGas - gasCost, fmt.Errorf("%w: need at least %d bytes", ErrInvalidInputLength, minHeader)
|
||||
return nil, remainingGas, fmt.Errorf("%w: need at least %d bytes", ErrInvalidInputLength, minHeader)
|
||||
}
|
||||
|
||||
// Parse mode
|
||||
mode := input[0]
|
||||
pubKeySize, sigSize, _, slhdsaMode, err := getModeParams(mode)
|
||||
if err != nil {
|
||||
return nil, suppliedGas - gasCost, fmt.Errorf("%w: 0x%02x", ErrUnsupportedMode, mode)
|
||||
return nil, remainingGas, fmt.Errorf("%w: 0x%02x", ErrUnsupportedMode, mode)
|
||||
}
|
||||
|
||||
// Parse public key length
|
||||
pubKeyLen := int(binary.BigEndian.Uint16(input[ModeByte : ModeByte+PubKeyLenSize]))
|
||||
if pubKeyLen != pubKeySize {
|
||||
return nil, suppliedGas - gasCost, fmt.Errorf("%w: expected pubkey size %d for mode 0x%02x, got %d",
|
||||
return nil, remainingGas, fmt.Errorf("%w: expected pubkey size %d for mode 0x%02x, got %d",
|
||||
ErrInvalidInputLength, pubKeySize, mode, pubKeyLen)
|
||||
}
|
||||
|
||||
@@ -220,7 +221,7 @@ func (p *slhdsaVerifyPrecompile) Run(
|
||||
|
||||
// Check we have enough input
|
||||
if len(input) < msgLenEnd {
|
||||
return nil, suppliedGas - gasCost, fmt.Errorf("%w: input too short for message length", ErrInvalidInputLength)
|
||||
return nil, remainingGas, fmt.Errorf("%w: input too short for message length", ErrInvalidInputLength)
|
||||
}
|
||||
|
||||
// Parse message length
|
||||
@@ -232,7 +233,7 @@ func (p *slhdsaVerifyPrecompile) Run(
|
||||
|
||||
// Validate total input size
|
||||
if len(input) < sigEnd {
|
||||
return nil, suppliedGas - gasCost, fmt.Errorf("%w: expected at least %d bytes, got %d",
|
||||
return nil, remainingGas, fmt.Errorf("%w: expected at least %d bytes, got %d",
|
||||
ErrInvalidInputLength, sigEnd, len(input))
|
||||
}
|
||||
|
||||
@@ -249,7 +250,7 @@ func (p *slhdsaVerifyPrecompile) Run(
|
||||
// CPU fallback: parse public key and verify
|
||||
pub, err := slhdsa.PublicKeyFromBytes(publicKey, slhdsaMode)
|
||||
if err != nil {
|
||||
return nil, suppliedGas - gasCost, fmt.Errorf("invalid public key: %w", err)
|
||||
return nil, remainingGas, fmt.Errorf("invalid public key: %w", err)
|
||||
}
|
||||
valid = pub.Verify(message, signature, nil)
|
||||
}
|
||||
@@ -260,7 +261,7 @@ func (p *slhdsaVerifyPrecompile) Run(
|
||||
result[31] = 1
|
||||
}
|
||||
|
||||
return result, suppliedGas - gasCost, nil
|
||||
return result, remainingGas, nil
|
||||
}
|
||||
|
||||
// verifySLHDSAGPU attempts GPU-accelerated SLH-DSA signature verification.
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
|
||||
"github.com/luxfi/crypto/slhdsa"
|
||||
"github.com/luxfi/geth/common"
|
||||
"github.com/luxfi/precompile/contract"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
@@ -134,7 +135,7 @@ func TestSLHDSAVerify_InputTooShort(t *testing.T) {
|
||||
)
|
||||
|
||||
require.Error(t, err)
|
||||
require.Contains(t, err.Error(), "invalid input length")
|
||||
require.ErrorIs(t, err, contract.ErrInvalidInput)
|
||||
}
|
||||
|
||||
// TestSLHDSAVerify_InvalidMode tests rejection of invalid mode byte
|
||||
|
||||
+3
-3
@@ -92,10 +92,10 @@ func (p *sr25519VerifyPrecompile) Run(
|
||||
readOnly bool,
|
||||
) ([]byte, uint64, error) {
|
||||
gasCost := p.RequiredGas(input)
|
||||
if suppliedGas < gasCost {
|
||||
return nil, 0, contract.ErrOutOfGas
|
||||
remainingGas, err := contract.DeductGas(suppliedGas, gasCost)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
remainingGas := suppliedGas - gasCost
|
||||
|
||||
if len(input) < MinInputSize {
|
||||
return failResult, remainingGas, fmt.Errorf(
|
||||
|
||||
@@ -34,7 +34,7 @@ var (
|
||||
|
||||
_ contract.StatefulPrecompiledContract = Precompile
|
||||
|
||||
ErrInvalidInput = errors.New("invalid stableswap input")
|
||||
ErrInvalidInput = contract.ErrInvalidInput
|
||||
ErrInvalidIndex = errors.New("invalid token index")
|
||||
ErrZeroLiquidity = errors.New("zero liquidity")
|
||||
ErrConvergence = errors.New("newton method did not converge")
|
||||
|
||||
+4
-4
@@ -50,7 +50,7 @@ const (
|
||||
// ─── Errors ───────────────────────────────────────────────────────────────────
|
||||
|
||||
var (
|
||||
ErrInvalidInput = errors.New("vrf: invalid input")
|
||||
ErrInvalidInput = contract.ErrInvalidInput
|
||||
ErrInvalidOperation = errors.New("vrf: invalid operation selector")
|
||||
)
|
||||
|
||||
@@ -105,10 +105,10 @@ func (p *vrfPrecompile) Run(
|
||||
readOnly bool,
|
||||
) ([]byte, uint64, error) {
|
||||
gasCost := p.RequiredGas(input)
|
||||
if suppliedGas < gasCost {
|
||||
return nil, 0, contract.ErrOutOfGas
|
||||
remainingGas, err := contract.DeductGas(suppliedGas, gasCost)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
remainingGas := suppliedGas - gasCost
|
||||
|
||||
if len(input) < 1 {
|
||||
return nil, remainingGas, nil
|
||||
|
||||
+1
-1
@@ -40,7 +40,7 @@ var (
|
||||
|
||||
_ contract.StatefulPrecompiledContract = &x25519Precompile{}
|
||||
|
||||
ErrInvalidInput = errors.New("invalid x25519 input")
|
||||
ErrInvalidInput = contract.ErrInvalidInput
|
||||
ErrInvalidOp = errors.New("invalid x25519 operation")
|
||||
ErrDHFailed = errors.New("x25519 scalar multiplication failed")
|
||||
)
|
||||
|
||||
+1
-1
@@ -45,7 +45,7 @@ var (
|
||||
|
||||
_ contract.StatefulPrecompiledContract = &xwingPrecompile{}
|
||||
|
||||
ErrInvalidInput = errors.New("invalid xwing input")
|
||||
ErrInvalidInput = contract.ErrInvalidInput
|
||||
ErrInvalidOp = errors.New("invalid xwing operation")
|
||||
)
|
||||
|
||||
|
||||
+4
-4
@@ -17,7 +17,7 @@ import (
|
||||
|
||||
var (
|
||||
// Note: ErrInvalidPublicInputs is defined in types.go
|
||||
ErrInvalidInput = errors.New("invalid zk input")
|
||||
ErrInvalidInput = contract.ErrInvalidInput
|
||||
ErrInvalidOperation = errors.New("invalid operation selector")
|
||||
ErrInvalidProofLength = errors.New("invalid proof length")
|
||||
ErrVerificationFailed = errors.New("proof verification failed")
|
||||
@@ -142,10 +142,10 @@ func (p *zkVerifyPrecompile) Run(
|
||||
) (ret []byte, remainingGas uint64, err error) {
|
||||
// Calculate required gas
|
||||
requiredGas := p.RequiredGas(input)
|
||||
if suppliedGas < requiredGas {
|
||||
return nil, 0, contract.ErrOutOfGas
|
||||
remainingGas, err = contract.DeductGas(suppliedGas, requiredGas)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
remainingGas = suppliedGas - requiredGas
|
||||
|
||||
if len(input) < 1 {
|
||||
return nil, remainingGas, ErrInvalidInput
|
||||
|
||||
+2
-1
@@ -11,6 +11,7 @@ import (
|
||||
"github.com/consensys/gnark-crypto/ecc/bn254/fr"
|
||||
"github.com/consensys/gnark-crypto/ecc/bn254/fr/poseidon2"
|
||||
"github.com/luxfi/geth/common"
|
||||
"github.com/luxfi/precompile/contract"
|
||||
)
|
||||
|
||||
// poseidon2Hasher is the underlying gnark-crypto hasher
|
||||
@@ -31,7 +32,7 @@ var (
|
||||
)
|
||||
|
||||
var (
|
||||
ErrInvalidInputLength = errors.New("invalid input length: must be multiple of 32 bytes")
|
||||
ErrInvalidInputLength = contract.ErrInvalidInput
|
||||
ErrTooManyInputs = errors.New("too many inputs: maximum 16 field elements")
|
||||
ErrInvalidFieldElement = errors.New("invalid field element: exceeds BN254 scalar field")
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user