mirror of
https://github.com/luxfi/precompile.git
synced 2026-09-24 00:26:21 +00:00
212 lines
5.7 KiB
Go
212 lines
5.7 KiB
Go
// Copyright (C) 2026, Lux Industries Inc. All rights reserved.
|
|
// See the file LICENSE for licensing terms.
|
|
|
|
package math
|
|
|
|
import (
|
|
"math/big"
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
func TestAddress(t *testing.T) {
|
|
expected := "0x0400000000000000000000000000000000000050"
|
|
require.Equal(t, expected, ContractAddress.Hex())
|
|
}
|
|
|
|
func TestMulDiv(t *testing.T) {
|
|
p := Precompile
|
|
tests := []struct {
|
|
name string
|
|
a, b, d *big.Int
|
|
want *big.Int
|
|
wantErr error
|
|
}{
|
|
{
|
|
name: "basic",
|
|
a: big.NewInt(10),
|
|
b: big.NewInt(20),
|
|
d: big.NewInt(5),
|
|
want: big.NewInt(40),
|
|
},
|
|
{
|
|
name: "large product no overflow",
|
|
a: new(big.Int).Exp(big.NewInt(2), big.NewInt(128), nil),
|
|
b: new(big.Int).Exp(big.NewInt(2), big.NewInt(128), nil),
|
|
d: new(big.Int).Exp(big.NewInt(2), big.NewInt(128), nil),
|
|
want: new(big.Int).Exp(big.NewInt(2), big.NewInt(128), nil),
|
|
},
|
|
{
|
|
name: "div by zero",
|
|
a: big.NewInt(1),
|
|
b: big.NewInt(1),
|
|
d: big.NewInt(0),
|
|
wantErr: ErrDivByZero,
|
|
},
|
|
{
|
|
name: "zero numerator",
|
|
a: big.NewInt(0),
|
|
b: big.NewInt(100),
|
|
d: big.NewInt(7),
|
|
want: big.NewInt(0),
|
|
},
|
|
}
|
|
|
|
for _, tc := range tests {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
input := make([]byte, 1+96)
|
|
input[0] = OpMulDiv
|
|
copy(input[1:33], padTo32(tc.a.Bytes()))
|
|
copy(input[33:65], padTo32(tc.b.Bytes()))
|
|
copy(input[65:97], padTo32(tc.d.Bytes()))
|
|
|
|
result, gas, err := p.Run(nil, addr0, ContractAddress, input, 100000, false)
|
|
if tc.wantErr != nil {
|
|
require.ErrorIs(t, err, tc.wantErr)
|
|
return
|
|
}
|
|
require.NoError(t, err)
|
|
require.True(t, gas < 100000, "gas should be consumed")
|
|
got := new(big.Int).SetBytes(result)
|
|
require.Equal(t, 0, tc.want.Cmp(got), "expected %s, got %s", tc.want, got)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestMulDivRoundUp(t *testing.T) {
|
|
p := Precompile
|
|
// 10 * 3 / 7 = 4.28... -> ceil = 5
|
|
input := make([]byte, 1+96)
|
|
input[0] = OpMulDivRoundUp
|
|
copy(input[1:33], padTo32(big.NewInt(10).Bytes()))
|
|
copy(input[33:65], padTo32(big.NewInt(3).Bytes()))
|
|
copy(input[65:97], padTo32(big.NewInt(7).Bytes()))
|
|
|
|
result, _, err := p.Run(nil, addr0, ContractAddress, input, 100000, false)
|
|
require.NoError(t, err)
|
|
got := new(big.Int).SetBytes(result)
|
|
require.Equal(t, big.NewInt(5), got)
|
|
}
|
|
|
|
func TestSqrt(t *testing.T) {
|
|
p := Precompile
|
|
tests := []struct {
|
|
name string
|
|
x *big.Int
|
|
want *big.Int
|
|
}{
|
|
{"zero", big.NewInt(0), big.NewInt(0)},
|
|
{"one", big.NewInt(1), big.NewInt(1)},
|
|
{"four", big.NewInt(4), big.NewInt(2)},
|
|
{"non-perfect", big.NewInt(10), big.NewInt(3)},
|
|
{"large", new(big.Int).Exp(big.NewInt(10), big.NewInt(40), nil), new(big.Int).Exp(big.NewInt(10), big.NewInt(20), nil)},
|
|
}
|
|
|
|
for _, tc := range tests {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
input := make([]byte, 1+32)
|
|
input[0] = OpSqrt
|
|
copy(input[1:33], padTo32(tc.x.Bytes()))
|
|
|
|
result, _, err := p.Run(nil, addr0, ContractAddress, input, 100000, false)
|
|
require.NoError(t, err)
|
|
got := new(big.Int).SetBytes(result)
|
|
require.Equal(t, 0, tc.want.Cmp(got), "sqrt(%s): expected %s, got %s", tc.x, tc.want, got)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestLog2(t *testing.T) {
|
|
p := Precompile
|
|
tests := []struct {
|
|
name string
|
|
x *big.Int
|
|
want int64
|
|
}{
|
|
{"one", big.NewInt(1), 0},
|
|
{"two", big.NewInt(2), 1},
|
|
{"three", big.NewInt(3), 1},
|
|
{"256", big.NewInt(256), 8},
|
|
{"max_u64", new(big.Int).SetUint64(^uint64(0)), 63},
|
|
}
|
|
|
|
for _, tc := range tests {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
input := make([]byte, 1+32)
|
|
input[0] = OpLog2
|
|
copy(input[1:33], padTo32(tc.x.Bytes()))
|
|
|
|
result, _, err := p.Run(nil, addr0, ContractAddress, input, 100000, false)
|
|
require.NoError(t, err)
|
|
got := new(big.Int).SetBytes(result)
|
|
require.Equal(t, 0, big.NewInt(tc.want).Cmp(got), "expected %d, got %s", tc.want, got)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestLog2Zero(t *testing.T) {
|
|
input := make([]byte, 1+32)
|
|
input[0] = OpLog2
|
|
// x = 0 (all zeros)
|
|
|
|
result, _, err := Precompile.Run(nil, addr0, ContractAddress, input, 100000, false)
|
|
require.NoError(t, err)
|
|
got := new(big.Int).SetBytes(result)
|
|
require.Equal(t, 0, big.NewInt(0).Cmp(got), "log2(0) should return 0")
|
|
}
|
|
|
|
func TestPow(t *testing.T) {
|
|
p := Precompile
|
|
// 2^10 = 1024
|
|
input := make([]byte, 1+64)
|
|
input[0] = OpPow
|
|
copy(input[1:33], padTo32(big.NewInt(2).Bytes()))
|
|
copy(input[33:65], padTo32(big.NewInt(10).Bytes()))
|
|
|
|
result, _, err := p.Run(nil, addr0, ContractAddress, input, 100000, false)
|
|
require.NoError(t, err)
|
|
got := new(big.Int).SetBytes(result)
|
|
require.Equal(t, big.NewInt(1024), got)
|
|
}
|
|
|
|
func TestExp(t *testing.T) {
|
|
// e^0 in Q128.128 = 2^128 (= 1.0)
|
|
input := make([]byte, 1+32)
|
|
input[0] = OpExp
|
|
// x = 0
|
|
|
|
result, _, err := Precompile.Run(nil, addr0, ContractAddress, input, 100000, false)
|
|
require.NoError(t, err)
|
|
got := new(big.Int).SetBytes(result)
|
|
// e^0 = 1.0 in Q128.128 = 2^128
|
|
require.Equal(t, 0, q128.Cmp(got), "e^0 should be 1.0 in Q128.128")
|
|
}
|
|
|
|
func TestGasInsufficient(t *testing.T) {
|
|
input := []byte{OpMulDiv}
|
|
input = append(input, make([]byte, 96)...)
|
|
_, _, err := Precompile.Run(nil, addr0, ContractAddress, input, 1, false)
|
|
require.Error(t, err)
|
|
}
|
|
|
|
func TestInvalidInput(t *testing.T) {
|
|
// Empty input
|
|
_, _, err := Precompile.Run(nil, addr0, ContractAddress, nil, 100000, false)
|
|
require.ErrorIs(t, err, ErrInvalidInput)
|
|
|
|
// Unknown op
|
|
_, _, err = Precompile.Run(nil, addr0, ContractAddress, []byte{0xFF}, 100000, false)
|
|
require.ErrorIs(t, err, ErrUnknownOp)
|
|
|
|
// MulDiv with too little data
|
|
_, _, err = Precompile.Run(nil, addr0, ContractAddress, []byte{OpMulDiv, 0x01}, 100000, false)
|
|
require.ErrorIs(t, err, ErrInvalidInput)
|
|
|
|
// Sqrt with too little data
|
|
_, _, err = Precompile.Run(nil, addr0, ContractAddress, []byte{OpSqrt}, 100000, false)
|
|
require.ErrorIs(t, err, ErrInvalidInput)
|
|
}
|
|
|
|
var addr0 = ContractAddress
|