Files
precompile/vrf/module_test.go
T
zeekay 0ba85e6aaf vrf: an algorithm from one RFC 9381 suite, labelled with another's byte
The package documented ECVRF-EDWARDS25519-SHA512-ELL2 and sent suite byte 0x04,
while the code ran try-and-increment — which is TAI, suite 0x03. The suite byte
is hashed into both the encode-to-curve and challenge digests, so the
combination matched no conforming prover and could not verify a proof from one.
Aligned on TAI throughout, with the RFC's front/back domain separators.

Verification also now runs with validate_key = TRUE (Section 5.4.5). The public
key arrives in calldata, so it is attacker-chosen, and that check is what buys
the collision resistance and unpredictability-under-malicious-key-generation an
on-chain lottery is paying for (Sections 7.1.1, 7.1.3). Point decoding refuses
non-canonical encodings.

Gas follows the work: encode_to_curve hashes the whole alpha string once per
counter, so the fee carries a per-word term, and a call that does no work is
charged rather than free.
2026-08-28 15:39:15 -07:00

94 lines
2.7 KiB
Go

// Copyright (C) 2025, Lux Industries, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package vrf
import (
"encoding/json"
"testing"
"github.com/luxfi/precompile/modules"
"github.com/luxfi/precompile/precompileconfig"
"github.com/stretchr/testify/require"
)
func TestModule_RegisteredAtTheAdvertisedAddress(t *testing.T) {
m, ok := modules.GetPrecompileModuleByAddress(ContractAddress)
require.True(t, ok, "vrf is not registered at %s", ContractAddress)
require.Equal(t, ConfigKey, m.ConfigKey)
require.Equal(t, VRFPrecompile, m.Contract)
byKey, ok := modules.GetPrecompileModule(ConfigKey)
require.True(t, ok)
require.Equal(t, ContractAddress, byKey.Address)
}
// TestModule_RegisteringTwiceIsRejected pins the guard that stops a second
// module claiming this address or config key.
func TestModule_RegisteringTwiceIsRejected(t *testing.T) {
require.Error(t, modules.RegisterModule(Module))
}
func TestConfig_MakeConfigMatchesKey(t *testing.T) {
cfg := Module.Configurator.MakeConfig()
require.IsType(t, &Config{}, cfg)
require.Equal(t, ConfigKey, cfg.Key())
}
func TestConfig_Timestamp(t *testing.T) {
var cfg Config
require.Nil(t, cfg.Timestamp())
at := uint64(1700000000)
cfg.Upgrade.BlockTimestamp = &at
require.NotNil(t, cfg.Timestamp())
require.Equal(t, at, *cfg.Timestamp())
}
func TestConfig_IsDisabled(t *testing.T) {
var cfg Config
require.False(t, cfg.IsDisabled())
cfg.Upgrade.Disable = true
require.True(t, cfg.IsDisabled())
}
func TestConfig_Equal(t *testing.T) {
at, other := uint64(10), uint64(11)
a := &Config{Upgrade: precompileconfig.Upgrade{BlockTimestamp: &at}}
same := &Config{Upgrade: precompileconfig.Upgrade{BlockTimestamp: &at}}
later := &Config{Upgrade: precompileconfig.Upgrade{BlockTimestamp: &other}}
disabled := &Config{Upgrade: precompileconfig.Upgrade{BlockTimestamp: &at, Disable: true}}
require.True(t, a.Equal(same))
require.False(t, a.Equal(later))
require.False(t, a.Equal(disabled))
require.False(t, a.Equal(nil))
require.False(t, a.Equal(otherConfig{}))
}
// otherConfig is a Config of a different concrete type, which Equal must
// refuse rather than treat as a match.
type otherConfig struct{ precompileconfig.Config }
func (otherConfig) Key() string { return "not-vrf" }
func TestConfig_VerifyAndConfigureAreTotal(t *testing.T) {
cfg := &Config{}
require.NoError(t, cfg.Verify(nil))
require.NoError(t, Module.Configurator.Configure(nil, cfg, nil, nil))
}
func TestConfig_JSONRoundTrip(t *testing.T) {
at := uint64(42)
in := &Config{Upgrade: precompileconfig.Upgrade{BlockTimestamp: &at}}
raw, err := json.Marshal(in)
require.NoError(t, err)
out := Module.Configurator.MakeConfig()
require.NoError(t, json.Unmarshal(raw, out))
require.True(t, in.Equal(out))
}