mirror of
https://github.com/luxfi/evmgpu.git
synced 2026-10-30 16:57:11 +00:00
Two separate faults, both in the code generator's own coverage. The precompile module template never emitted MakeGenesisConfig, which contract.Configurator has required for some time. Every precompile generated by precompilebind therefore failed to compile against the interface it is generated to satisfy. The template now emits it, matching the config template's NewConfig signature in both the plain and allow-list shapes. The test's stand-in address also predated the LP-aligned reserved range, so registration panicked with "not in a reserved range" the moment the generated code did compile; it now sits at the top of 0x10000-0x1ffff, clear of every precompile this repo registers. The binding test built its generated module against github.com/luxfi/evmgpu resolved from the proxy, so it measured the last published release rather than the working tree -- it fails when the release has drifted and, worse, passes when the tree is broken. Pointing it at the tree alone was not enough: a from-scratch tidy re-resolves every version, because the go command ignores exclude and replace directives in a module that is not the main one. That pulled warp v1.24.1, which this repository does not build with and where warp.UnsignedMessage no longer exists. The test now copies go.mod and go.sum across and renames the module, so the generated code builds against the same build list the repository uses. accounts/abi/bind and accounts/abi/bind/precompilebind now pass. Co-authored-by: Hanzo Dev <[email protected]>
719 lines
27 KiB
Go
719 lines
27 KiB
Go
// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
|
|
// See the file LICENSE for licensing terms.
|
|
//
|
|
// This file is a derived work, based on the go-ethereum library whose original
|
|
// notices appear below.
|
|
//
|
|
// It is distributed under a license compatible with the licensing terms of the
|
|
// original code from which it is derived.
|
|
//
|
|
// Much love to the original authors for their work.
|
|
// **********
|
|
// Copyright 2016 The go-ethereum Authors
|
|
// This file is part of the go-ethereum library.
|
|
//
|
|
// The go-ethereum library is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU Lesser General Public License as published by
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
// (at your option) any later version.
|
|
//
|
|
// The go-ethereum library is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU Lesser General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU Lesser General Public License
|
|
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
package precompilebind
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"os/exec"
|
|
"path/filepath"
|
|
"runtime"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/luxfi/evmgpu/accounts/abi/bind"
|
|
"github.com/luxfi/geth/common"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
var bindTests = []struct {
|
|
name string
|
|
contract string
|
|
abi string
|
|
imports string
|
|
tester string
|
|
errMsg string
|
|
expectAllowlist bool
|
|
}{
|
|
{
|
|
"AnonOutputChecker",
|
|
"",
|
|
`
|
|
[
|
|
{"type":"function","name":"anonOutput","constant":true,"inputs":[],"outputs":[{"name":"","type":"string"}]}
|
|
]
|
|
`,
|
|
"",
|
|
"",
|
|
"ABI outputs for anonOutput require a name to generate the precompile binding, re-generate the ABI from a Solidity source file with all named outputs",
|
|
false,
|
|
},
|
|
{
|
|
"AnonOutputsChecker",
|
|
"",
|
|
`
|
|
[
|
|
{"type":"function","name":"anonOutputs","constant":true,"inputs":[],"outputs":[{"name":"","type":"string"},{"name":"","type":"string"}]}
|
|
]
|
|
`,
|
|
"",
|
|
"",
|
|
"ABI outputs for anonOutputs require a name to generate the precompile binding, re-generate the ABI from a Solidity source file with all named outputs",
|
|
false,
|
|
},
|
|
{
|
|
"MixedOutputsChecker",
|
|
"",
|
|
`
|
|
[
|
|
{"type":"function","name":"mixedOutputs","constant":true,"inputs":[],"outputs":[{"name":"","type":"string"},{"name":"str","type":"string"}]}
|
|
]
|
|
`,
|
|
"",
|
|
"",
|
|
"ABI outputs for mixedOutputs require a name to generate the precompile binding, re-generate the ABI from a Solidity source file with all named outputs",
|
|
false,
|
|
},
|
|
// Test that module is generated correctly
|
|
{
|
|
`EmptyContract`,
|
|
`contract EmptyContract {}`,
|
|
"[]",
|
|
"",
|
|
"",
|
|
"no ABI methods found",
|
|
false,
|
|
},
|
|
// Test that named and anonymous inputs are handled correctly
|
|
{
|
|
`InputChecker`, ``,
|
|
`
|
|
[
|
|
{"type":"function","name":"noInput","constant":true,"inputs":[],"outputs":[]},
|
|
{"type":"function","name":"namedInput","constant":true,"inputs":[{"name":"str","type":"string"}],"outputs":[]},
|
|
{"type":"function","name":"namedInputs","constant":true,"inputs":[{"name":"str1","type":"string"},{"name":"str2","type":"string"}],"outputs":[]}
|
|
]
|
|
`,
|
|
`
|
|
"github.com/stretchr/testify/require"
|
|
`,
|
|
`
|
|
testInput := "test"
|
|
packedInput, err := PackNamedInput(testInput)
|
|
require.NoError(t, err)
|
|
// remove the first 4 bytes of the packed input
|
|
packedInput = packedInput[4:]
|
|
unpackedInput, err := UnpackNamedInputInput(packedInput)
|
|
require.NoError(t, err)
|
|
require.Equal(t, testInput, unpackedInput)
|
|
|
|
testInputStruct := NamedInputsInput{
|
|
Str1: "test1",
|
|
Str2: "test2",
|
|
}
|
|
packedInputStruct, err := PackNamedInputs(testInputStruct)
|
|
require.NoError(t, err)
|
|
// remove the first 4 bytes of the packed input
|
|
packedInputStruct = packedInputStruct[4:]
|
|
unpackedInputStruct, err := UnpackNamedInputsInput(packedInputStruct)
|
|
require.NoError(t, err)
|
|
require.Equal(t, unpackedInputStruct, testInputStruct)
|
|
`,
|
|
"",
|
|
false,
|
|
},
|
|
// Test that named and anonymous outputs are handled correctly
|
|
{
|
|
`OutputChecker`, ``,
|
|
`
|
|
[
|
|
{"type":"function","name":"noOutput","constant":true,"inputs":[],"outputs":[]},
|
|
{"type":"function","name":"namedOutput","constant":true,"inputs":[],"outputs":[{"name":"str","type":"string"}]},
|
|
{"type":"function","name":"namedOutputs","constant":true,"inputs":[],"outputs":[{"name":"str1","type":"string"},{"name":"str2","type":"string"}]}
|
|
]
|
|
`,
|
|
`
|
|
"github.com/stretchr/testify/require"
|
|
`,
|
|
`
|
|
testOutput := "test"
|
|
packedOutput, err := PackNamedOutputOutput(testOutput)
|
|
require.NoError(t, err)
|
|
unpackedOutput, err := UnpackNamedOutputOutput(packedOutput)
|
|
require.NoError(t, err)
|
|
require.Equal(t, testOutput, unpackedOutput)
|
|
|
|
testNamedOutputs := NamedOutputsOutput{
|
|
Str1: "test1",
|
|
Str2: "test2",
|
|
}
|
|
packedNamedOutputs, err := PackNamedOutputsOutput(testNamedOutputs)
|
|
require.NoError(t, err)
|
|
unpackedNamedOutputs, err := UnpackNamedOutputsOutput(packedNamedOutputs)
|
|
require.NoError(t, err)
|
|
require.Equal(t, testNamedOutputs, unpackedNamedOutputs)
|
|
`,
|
|
"",
|
|
false,
|
|
},
|
|
{
|
|
`Tupler`,
|
|
`
|
|
interface Tupler {
|
|
function tuple() constant returns (string a, int b, bytes32 c);
|
|
}
|
|
`,
|
|
`[{"constant":true,"inputs":[],"name":"tuple","outputs":[{"name":"a","type":"string"},{"name":"b","type":"int256"},{"name":"c","type":"bytes32"}],"type":"function"}]`,
|
|
`
|
|
"math/big"
|
|
"github.com/stretchr/testify/require"
|
|
`,
|
|
`
|
|
testOutput := TupleOutput{"Hi", big.NewInt(123), [32]byte{1, 2, 3}}
|
|
packedOutput, err := PackTupleOutput(testOutput)
|
|
require.NoError(t, err)
|
|
unpackedOutput, err := UnpackTupleOutput(packedOutput)
|
|
require.NoError(t, err)
|
|
require.Equal(t, testOutput, unpackedOutput)
|
|
`,
|
|
"",
|
|
false,
|
|
},
|
|
{
|
|
`Slicer`,
|
|
`
|
|
interface Slicer {
|
|
function echoAddresses(address[] input) constant returns (address[] output);
|
|
function echoInts(int[] input) constant returns (int[] output);
|
|
function echoFancyInts(uint8[23] input) constant returns (uint8[23] output);
|
|
function echoBools(bool[] input) constant returns (bool[] output);
|
|
}
|
|
`,
|
|
`[{"constant":true,"inputs":[{"name":"input","type":"address[]"}],"name":"echoAddresses","outputs":[{"name":"output","type":"address[]"}],"type":"function"},{"constant":true,"inputs":[{"name":"input","type":"uint8[23]"}],"name":"echoFancyInts","outputs":[{"name":"output","type":"uint8[23]"}],"type":"function"},{"constant":true,"inputs":[{"name":"input","type":"int256[]"}],"name":"echoInts","outputs":[{"name":"output","type":"int256[]"}],"type":"function"},{"constant":true,"inputs":[{"name":"input","type":"bool[]"}],"name":"echoBools","outputs":[{"name":"output","type":"bool[]"}],"type":"function"}]`,
|
|
`
|
|
"math/big"
|
|
"github.com/stretchr/testify/require"
|
|
"github.com/luxfi/geth/common"
|
|
`,
|
|
`
|
|
testArgs := []common.Address{common.HexToAddress("1"), common.HexToAddress("2"), common.HexToAddress("3")}
|
|
packedOutput, err := PackEchoAddressesOutput(testArgs)
|
|
require.NoError(t, err)
|
|
unpackedOutput, err := UnpackEchoAddressesOutput(packedOutput)
|
|
require.NoError(t, err)
|
|
require.Equal(t, testArgs, unpackedOutput)
|
|
packedInput, err := PackEchoAddresses(testArgs)
|
|
// remove the first 4 bytes of the packed input
|
|
packedInput = packedInput[4:]
|
|
require.NoError(t, err)
|
|
unpackedInput, err := UnpackEchoAddressesInput(packedInput)
|
|
require.NoError(t, err)
|
|
require.Equal(t, testArgs, unpackedInput)
|
|
|
|
testArgs2 := []*big.Int{common.Big1, common.Big2, common.Big3}
|
|
packedOutput2, err := PackEchoIntsOutput(testArgs2)
|
|
require.NoError(t, err)
|
|
unpackedOutput2, err := UnpackEchoIntsOutput(packedOutput2)
|
|
require.NoError(t, err)
|
|
require.Equal(t, testArgs2, unpackedOutput2)
|
|
packedInput2, err := PackEchoInts(testArgs2)
|
|
// remove the first 4 bytes of the packed input
|
|
packedInput2 = packedInput2[4:]
|
|
require.NoError(t, err)
|
|
unpackedInput2, err := UnpackEchoIntsInput(packedInput2)
|
|
require.NoError(t, err)
|
|
require.Equal(t, testArgs2, unpackedInput2)
|
|
|
|
testArgs3 := [23]uint8{1, 2, 3}
|
|
packedOutput3, err := PackEchoFancyIntsOutput(testArgs3)
|
|
require.NoError(t, err)
|
|
unpackedOutput3, err := UnpackEchoFancyIntsOutput(packedOutput3)
|
|
require.NoError(t, err)
|
|
require.Equal(t, testArgs3, unpackedOutput3)
|
|
packedInput3, err := PackEchoFancyInts(testArgs3)
|
|
// remove the first 4 bytes of the packed input
|
|
packedInput3 = packedInput3[4:]
|
|
require.NoError(t, err)
|
|
unpackedInput3, err := UnpackEchoFancyIntsInput(packedInput3)
|
|
require.NoError(t, err)
|
|
require.Equal(t, testArgs3, unpackedInput3)
|
|
|
|
testArgs4 := []bool{true, false, true}
|
|
packedOutput4, err := PackEchoBoolsOutput(testArgs4)
|
|
require.NoError(t, err)
|
|
unpackedOutput4, err := UnpackEchoBoolsOutput(packedOutput4)
|
|
require.NoError(t, err)
|
|
require.Equal(t, testArgs4, unpackedOutput4)
|
|
packedInput4, err := PackEchoBools(testArgs4)
|
|
// remove the first 4 bytes of the packed input
|
|
packedInput4 = packedInput4[4:]
|
|
require.NoError(t, err)
|
|
unpackedInput4, err := UnpackEchoBoolsInput(packedInput4)
|
|
require.NoError(t, err)
|
|
require.Equal(t, testArgs4, unpackedInput4)
|
|
`,
|
|
"",
|
|
false,
|
|
},
|
|
{
|
|
`Fallback`,
|
|
`
|
|
interface Fallback {
|
|
fallback() external payable;
|
|
|
|
receive() external payable;
|
|
function testFunction(uint t) external;
|
|
}
|
|
`,
|
|
`[{"stateMutability":"payable","type":"fallback"},{"inputs":[{"internalType":"uint256","name":"t","type":"uint256"}],"name":"testFunction","outputs":[],"stateMutability":"nonpayable","type":"function"},{"stateMutability":"payable","type":"receive"}]`,
|
|
`
|
|
"github.com/stretchr/testify/require"
|
|
"math/big"
|
|
`,
|
|
`
|
|
packedInput, err := PackTestFunction(big.NewInt(5))
|
|
require.NoError(t, err)
|
|
// remove the first 4 bytes of the packed input
|
|
packedInput = packedInput[4:]
|
|
unpackedInput, err := UnpackTestFunctionInput(packedInput)
|
|
require.NoError(t, err)
|
|
require.Equal(t, big.NewInt(5), unpackedInput)
|
|
`,
|
|
"",
|
|
false,
|
|
},
|
|
{
|
|
`Structs`,
|
|
`
|
|
interface Struct {
|
|
struct A {
|
|
bytes32 B;
|
|
}
|
|
function F() external view returns (A[] memory a, uint256[] memory c, bool[] memory d);
|
|
function G() external view returns (A[] memory a);
|
|
}
|
|
`,
|
|
`[{"inputs":[],"name":"F","outputs":[{"components":[{"internalType":"bytes32","name":"B","type":"bytes32"}],"internalType":"struct Structs.A[]","name":"a","type":"tuple[]"},{"internalType":"uint256[]","name":"c","type":"uint256[]"},{"internalType":"bool[]","name":"d","type":"bool[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"G","outputs":[{"components":[{"internalType":"bytes32","name":"B","type":"bytes32"}],"internalType":"struct Structs.A[]","name":"a","type":"tuple[]"}],"stateMutability":"view","type":"function"}]`,
|
|
`
|
|
"github.com/stretchr/testify/require"
|
|
"math/big"
|
|
`,
|
|
`
|
|
testOutput := FOutput{
|
|
A: []StructsA{
|
|
{
|
|
B: [32]byte{1},
|
|
},
|
|
},
|
|
C: []*big.Int{big.NewInt(2)},
|
|
D: []bool{true,false},
|
|
}
|
|
packedOutput, err := PackFOutput(testOutput)
|
|
require.NoError(t, err)
|
|
unpackedInput, err := UnpackFOutput(packedOutput)
|
|
require.NoError(t, err)
|
|
require.Equal(t, testOutput, unpackedInput)
|
|
`,
|
|
"",
|
|
false,
|
|
},
|
|
{
|
|
`Underscorer`,
|
|
`
|
|
interface Underscorer {
|
|
function UnderscoredOutput() external returns (int _int, string _string);
|
|
}
|
|
`,
|
|
`[{"inputs":[],"name":"UnderscoredOutput","outputs":[{"internalType":"int256","name":"_int","type":"int256"},{"internalType":"string","name":"_string","type":"string"}],"stateMutability":"nonpayable","type":"function"}]`,
|
|
`
|
|
"github.com/stretchr/testify/require"
|
|
"math/big"
|
|
`,
|
|
`
|
|
testOutput := UnderscoredOutputOutput{
|
|
Int: big.NewInt(5),
|
|
String: "hello",
|
|
}
|
|
packedOutput, err := PackUnderscoredOutputOutput(testOutput)
|
|
require.NoError(t, err)
|
|
unpackedInput, err := UnpackUnderscoredOutputOutput(packedOutput)
|
|
require.NoError(t, err)
|
|
require.Equal(t, testOutput, unpackedInput)
|
|
`,
|
|
"",
|
|
false,
|
|
},
|
|
{
|
|
`OutputCollision`,
|
|
`
|
|
interface Collision {
|
|
function LowerLowerCollision() external returns (int _res, int res, int res_);
|
|
`,
|
|
`[{"inputs":[],"name":"LowerLowerCollision","outputs":[{"internalType":"int256","name":"_res","type":"int256"},{"internalType":"int256","name":"res","type":"int256"},{"internalType":"int256","name":"res_","type":"int256"}],"stateMutability":"nonpayable","type":"function"}]`,
|
|
"",
|
|
"",
|
|
"normalized output name is duplicated",
|
|
false,
|
|
},
|
|
|
|
{
|
|
`InputCollision`,
|
|
`
|
|
interface Collision {
|
|
function LowerUpperCollision(int _res, int Res) external;
|
|
}
|
|
`,
|
|
`[{"inputs":[{"internalType":"int256","name":"_res","type":"int256"},{"internalType":"int256","name":"Res","type":"int256"}],"name":"LowerUpperCollision","outputs":[],"stateMutability":"nonpayable","type":"function"}]`, "",
|
|
"",
|
|
"normalized input name is duplicated",
|
|
false,
|
|
},
|
|
{
|
|
`DeeplyNestedArray`,
|
|
`
|
|
interface DeeplyNestedArray {
|
|
function storeDeepUintArray(uint64[3][4][5] arr) external public;
|
|
function retrieveDeepArray() public external view returns (uint64[3][4][5] arr);
|
|
}
|
|
`,
|
|
`[{"inputs":[],"name":"retrieveDeepArray","outputs":[{"internalType":"uint64[3][4][5]","name":"arr","type":"uint64[3][4][5]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint64[3][4][5]","name":"arr","type":"uint64[3][4][5]"}],"name":"storeDeepUintArray","outputs":[],"stateMutability":"nonpayable","type":"function"}]`, `
|
|
"github.com/stretchr/testify/require"
|
|
`,
|
|
`
|
|
testArr := [5][4][3]uint64{
|
|
{
|
|
{1, 2, 3},
|
|
{4, 5, 6},
|
|
{7, 8, 9},
|
|
{10, 11, 12},
|
|
},
|
|
{
|
|
{13, 14, 15},
|
|
{16, 17, 18},
|
|
{19, 20, 21},
|
|
{22, 23, 24},
|
|
},
|
|
}
|
|
packedInput, err := PackStoreDeepUintArray(testArr)
|
|
require.NoError(t, err)
|
|
// remove the first 4 bytes of the packed input
|
|
packedInput = packedInput[4:]
|
|
unpackedInput, err := UnpackStoreDeepUintArrayInput(packedInput)
|
|
require.NoError(t, err)
|
|
require.Equal(t, testArr, unpackedInput)
|
|
|
|
packedOutput, err := PackRetrieveDeepArrayOutput(testArr)
|
|
require.NoError(t, err)
|
|
unpackedOutput, err := UnpackRetrieveDeepArrayOutput(packedOutput)
|
|
require.NoError(t, err)
|
|
require.Equal(t, testArr, unpackedOutput)
|
|
`,
|
|
"",
|
|
false,
|
|
},
|
|
{
|
|
"RangeKeyword",
|
|
`
|
|
interface keywordcontract {
|
|
function functionWithKeywordParameter(uint8 func, uint8 range) external pure;
|
|
}
|
|
`,
|
|
`[{"inputs":[{"internalType":"uint8","name":"func","type":"uint8"},{"internalType":"uint8","name":"range","type":"uint8"}],"name":"functionWithKeywordParameter","outputs":[],"stateMutability":"pure","type":"function"}]`,
|
|
"",
|
|
"",
|
|
"input name func is a keyword",
|
|
false,
|
|
},
|
|
{
|
|
`HelloWorld`,
|
|
`interface IHelloWorld is IAllowList {
|
|
// sayHello returns the stored greeting string
|
|
function sayHello() external view returns (string calldata result);
|
|
|
|
// setGreeting stores the greeting string
|
|
function setGreeting(string calldata response) external;
|
|
}
|
|
`,
|
|
`[{"inputs":[{"internalType":"address","name":"addr","type":"address"}],"name":"readAllowList","outputs":[{"internalType":"uint256","name":"role","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"sayHello","outputs":[{"internalType":"string","name":"result","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"addr","type":"address"}],"name":"setAdmin","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"addr","type":"address"}],"name":"setManager","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"addr","type":"address"}],"name":"setEnabled","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"response","type":"string"}],"name":"setGreeting","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"addr","type":"address"}],"name":"setNone","outputs":[],"stateMutability":"nonpayable","type":"function"}]`,
|
|
`"github.com/stretchr/testify/require"
|
|
"math/big"
|
|
"github.com/luxfi/geth/common"
|
|
"github.com/luxfi/geth/core/rawdb"
|
|
"github.com/luxfi/evmgpu/core/state"
|
|
"github.com/luxfi/evmgpu/core/extstate"
|
|
"github.com/luxfi/evmgpu/precompile/allowlist"
|
|
`,
|
|
`
|
|
testGreeting := "test"
|
|
packedGreeting, err := PackSetGreeting(testGreeting)
|
|
require.NoError(t, err)
|
|
// remove the first 4 bytes of the packed greeting
|
|
packedGreeting = packedGreeting[4:]
|
|
unpackedGreeting, err := UnpackSetGreetingInput(packedGreeting)
|
|
require.NoError(t, err)
|
|
require.Equal(t, testGreeting, unpackedGreeting)
|
|
|
|
// test that the allow list is generated correctly
|
|
statedb, err := state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase()), nil)
|
|
require.NoError(t, err)
|
|
wrappedStateDB := extstate.New(statedb)
|
|
precompileDB := extstate.NewPrecompileAdapter(wrappedStateDB)
|
|
address := common.BigToAddress(big.NewInt(1))
|
|
SetHelloWorldAllowListStatus(precompileDB, address, allowlist.EnabledRole)
|
|
role := GetHelloWorldAllowListStatus(precompileDB, address)
|
|
require.Equal(t, role, allowlist.EnabledRole)
|
|
`,
|
|
"",
|
|
true,
|
|
},
|
|
{
|
|
`HelloWorldNoAL`,
|
|
`interface IHelloWorld{
|
|
// sayHello returns the stored greeting string
|
|
function sayHello() external view returns (string calldata result);
|
|
|
|
// setGreeting stores the greeting string
|
|
function setGreeting(string calldata response) external;
|
|
}
|
|
`,
|
|
// This ABI does not contain readAllowlist and setEnabled.
|
|
`[{"inputs":[],"name":"sayHello","outputs":[{"internalType":"string","name":"result","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"addr","type":"address"}],"name":"setAdmin","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"response","type":"string"}],"name":"setGreeting","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"addr","type":"address"}],"name":"setNone","outputs":[],"stateMutability":"nonpayable","type":"function"}]`,
|
|
`"github.com/stretchr/testify/require"`,
|
|
`
|
|
testGreeting := "test"
|
|
packedGreeting, err := PackSetGreeting(testGreeting)
|
|
require.NoError(t, err)
|
|
// remove the first 4 bytes of the packed greeting
|
|
packedGreeting = packedGreeting[4:]
|
|
unpackedGreeting, err := UnpackSetGreetingInput(packedGreeting)
|
|
require.NoError(t, err)
|
|
require.Equal(t, testGreeting, unpackedGreeting)
|
|
`,
|
|
"",
|
|
false,
|
|
},
|
|
{
|
|
`IEventer`,
|
|
`
|
|
interface IEventer {
|
|
event test(address indexed addressTest, uint indexed intTest, bytes bytesTest);
|
|
event empty();
|
|
event indexed(address addr, int8 indexed num);
|
|
event mixed(address indexed addr, int8 num);
|
|
event dynamic(string indexed idxStr, bytes indexed idxDat, string str, bytes dat);
|
|
event unnamed(uint8 indexed, uint8 indexed);
|
|
function eventTest() external view returns (string memory result);
|
|
}
|
|
`,
|
|
`[{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"addressTest","type":"address"},{"indexed":true,"internalType":"uint8","name":"intTest","type":"uint8"},{"indexed":false,"internalType":"bytes","name":"bytesTest","type":"bytes"}],"name":"test","type":"event"},{"inputs":[],"name":"eventTest","outputs":[{"internalType":"string","name":"result","type":"string"}],"stateMutability":"view","type":"function"},{"type":"event","name":"empty","inputs":[]},{"type":"event","name":"indexed","inputs":[{"name":"addr","type":"address","indexed":true},{"name":"num","type":"int8","indexed":true}]},{"type":"event","name":"mixed","inputs":[{"name":"addr","type":"address","indexed":true},{"name":"num","type":"int8"}]},{"type":"event","name":"dynamic","inputs":[{"name":"idxStr","type":"string","indexed":true},{"name":"idxDat","type":"bytes","indexed":true},{"name":"str","type":"string"},{"name":"dat","type":"bytes"}]},{"type":"event","name":"unnamed","inputs":[{"name":"","type":"uint8","indexed":true},{"name":"","type":"uint8","indexed":true}]}]`,
|
|
`"github.com/stretchr/testify/require"
|
|
"github.com/luxfi/geth/common"
|
|
"github.com/luxfi/evmgpu/precompile/contract"
|
|
`,
|
|
`
|
|
testAddr := common.Address{1}
|
|
testInt := int8(5)
|
|
testUint := uint8(5)
|
|
testBytes := []byte{1, 2, 3}
|
|
|
|
testEventData := TestEventData{
|
|
BytesTest: testBytes,
|
|
}
|
|
topics, data, err := PackTestEvent(testAddr, testUint, testEventData)
|
|
require.NoError(t, err)
|
|
eventID := IEventerABI.Events["test"].ID
|
|
require.Equal(t, eventID, topics[0])
|
|
unpacked, err := UnpackTestEventData(data)
|
|
require.NoError(t, err)
|
|
require.Equal(t, testBytes, unpacked.BytesTest)
|
|
gasCost := GetTestEventGasCost(testEventData)
|
|
require.Equal(t, contract.LogGas + 3 * contract.LogTopicGas + contract.LogDataGas, gasCost)
|
|
|
|
topics, data, err = PackEmptyEvent()
|
|
require.NoError(t, err)
|
|
eventID = IEventerABI.Events["empty"].ID
|
|
require.Len(t, topics, 1)
|
|
require.Equal(t, eventID, topics[0])
|
|
require.Equal(t, 0, len(data))
|
|
require.Equal(t, contract.LogGas, GetEmptyEventGasCost())
|
|
|
|
topics, data, err = PackIndexedEvent(testAddr, testInt)
|
|
require.NoError(t, err)
|
|
eventID = IEventerABI.Events["indexed"].ID
|
|
require.Len(t, topics, 3)
|
|
require.Equal(t, eventID, topics[0])
|
|
require.Equal(t, common.BytesToHash(testAddr[:]), topics[1])
|
|
require.Equal(t, 0, len(data))
|
|
require.Equal(t, contract.LogGas + 3 * contract.LogTopicGas, GetIndexedEventGasCost())
|
|
|
|
testMixedData := MixedEventData{
|
|
Num: testInt,
|
|
}
|
|
topics, data, err = PackMixedEvent(testAddr, testMixedData)
|
|
require.NoError(t, err)
|
|
eventID = IEventerABI.Events["mixed"].ID
|
|
require.Len(t, topics, 2)
|
|
require.Equal(t, eventID, topics[0])
|
|
require.Equal(t, common.BytesToHash(testAddr[:]), topics[1])
|
|
unpackedMixedData, err := UnpackMixedEventData(data)
|
|
require.NoError(t, err)
|
|
require.Equal(t, testMixedData, unpackedMixedData)
|
|
require.Equal(t, contract.LogGas + 2 * contract.LogTopicGas + contract.LogDataGas, GetMixedEventGasCost(testMixedData))
|
|
|
|
testDynamicData := DynamicEventData{
|
|
Str: "test",
|
|
Dat: testBytes,
|
|
}
|
|
topics, data, err = PackDynamicEvent("test", testBytes, testDynamicData)
|
|
require.NoError(t, err)
|
|
eventID = IEventerABI.Events["dynamic"].ID
|
|
require.Len(t, topics, 3)
|
|
require.Equal(t, eventID, topics[0])
|
|
unpackedDynamicData, err := UnpackDynamicEventData(data)
|
|
require.NoError(t, err)
|
|
require.Equal(t, testDynamicData, unpackedDynamicData)
|
|
require.Equal(t, contract.LogGas + 3 * contract.LogTopicGas + 2 * contract.LogDataGas, GetDynamicEventGasCost(testDynamicData))
|
|
|
|
topics, data, err = PackUnnamedEvent(testUint, testUint)
|
|
require.NoError(t, err)
|
|
eventID = IEventerABI.Events["unnamed"].ID
|
|
require.Len(t, topics, 3)
|
|
require.Equal(t, eventID, topics[0])
|
|
require.Equal(t, 0, len(data))
|
|
require.Equal(t, contract.LogGas + 3 * contract.LogTopicGas, GetUnnamedEventGasCost())
|
|
`,
|
|
"",
|
|
false,
|
|
},
|
|
{
|
|
`IEventerAnonymous`,
|
|
`
|
|
interface IEventer {
|
|
event Anonymous(address indexed, uint indexed, bytes) anonymous;
|
|
function eventTest() external view returns (string memory result);
|
|
}
|
|
`,
|
|
`[{"anonymous":true,"inputs":[{"indexed":true,"internalType":"address","name":"","type":"address"},{"indexed":true,"internalType":"uint256","name":"","type":"uint256"},{"indexed":false,"internalType":"bytes","name":"","type":"bytes"}],"name":"Anonymous","type":"event"},{"inputs":[],"name":"eventTest","outputs":[{"internalType":"string","name":"result","type":"string"}],"stateMutability":"view","type":"function"}]`,
|
|
``,
|
|
``,
|
|
errNoAnonymousEvent.Error(),
|
|
false,
|
|
},
|
|
}
|
|
|
|
// Tests that packages generated by the binder can be successfully compiled and
|
|
// the requested tester run against it.
|
|
func TestPrecompileBind(t *testing.T) {
|
|
// Skip the test if no Go command can be found
|
|
gocmd := runtime.GOROOT() + "/bin/go"
|
|
if !common.FileExist(gocmd) {
|
|
}
|
|
// Create a temporary workspace for the test suite
|
|
ws := t.TempDir()
|
|
|
|
pkg := filepath.Join(ws, "precompilebindtest")
|
|
if err := os.MkdirAll(pkg, 0o700); err != nil {
|
|
t.Fatalf("failed to create package: %v", err)
|
|
}
|
|
// Generate the test suite for all the contracts
|
|
for i, tt := range bindTests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
types := []string{tt.name}
|
|
|
|
// Generate the binding and create a Go source file in the workspace
|
|
bindedFiles, err := PrecompileBind(types, tt.abi, []string{""}, nil, tt.name, bind.LangGo, nil, nil, "contract.abi", true)
|
|
if tt.errMsg != "" {
|
|
require.ErrorContains(t, err, tt.errMsg)
|
|
return
|
|
}
|
|
if err != nil {
|
|
t.Fatalf("test %d: failed to generate binding: %v", i, err)
|
|
}
|
|
|
|
precompilePath := filepath.Join(pkg, tt.name)
|
|
if err := os.MkdirAll(precompilePath, 0o700); err != nil {
|
|
t.Fatalf("failed to create package: %v", err)
|
|
}
|
|
for _, file := range bindedFiles {
|
|
switch file.FileName {
|
|
case ContractFileName:
|
|
// check if the allowlist functions are generated
|
|
if tt.expectAllowlist {
|
|
require.Contains(t, file.Content, "allowlist.CreateAllowListFunctions(", "generated contract does not contain AllowListFunctions")
|
|
} else {
|
|
require.NotContains(t, file.Content, "allowlist.CreateAllowListFunctions(", "generated contract contains AllowListFunctions")
|
|
}
|
|
case ModuleFileName:
|
|
// change address to a suitable one for testing
|
|
// Top of the LP-aligned reserved range (0x10000-0x1ffff) that
|
|
// modules.RegisterModule accepts, and above every precompile
|
|
// this repo registers, so generated modules load without
|
|
// colliding.
|
|
file.Content = strings.Replace(file.Content, `common.HexToAddress("{ASUITABLEHEXADDRESS}")`, `common.HexToAddress("0x000000000000000000000000000000000001ffff")`, 1)
|
|
}
|
|
if err = os.WriteFile(filepath.Join(precompilePath, file.FileName), []byte(file.Content), 0o600); err != nil {
|
|
t.Fatalf("test %d: failed to write binding: %v", i, err)
|
|
}
|
|
}
|
|
if err = os.WriteFile(filepath.Join(precompilePath, "contract.abi"), []byte(tt.abi), 0o600); err != nil {
|
|
t.Fatalf("test %d: failed to write binding: %v", i, err)
|
|
}
|
|
|
|
// Generate the test file with the injected test code
|
|
code := fmt.Sprintf(`
|
|
package %s
|
|
|
|
import (
|
|
"testing"
|
|
%s
|
|
)
|
|
|
|
func Test%s(t *testing.T) {
|
|
%s
|
|
}
|
|
`, tt.name, tt.imports, tt.name, tt.tester)
|
|
if err := os.WriteFile(filepath.Join(precompilePath, strings.ToLower(tt.name)+"_test.go"), []byte(code), 0o600); err != nil {
|
|
t.Fatalf("test %d: failed to write tests: %v", i, err)
|
|
}
|
|
})
|
|
}
|
|
|
|
moder := exec.Command(gocmd, "mod", "init", "precompilebindtest")
|
|
moder.Dir = pkg
|
|
if out, err := moder.CombinedOutput(); err != nil {
|
|
t.Fatalf("failed to convert binding test to modules: %v\n%s", err, out)
|
|
}
|
|
pwd, _ := os.Getwd()
|
|
replacer := exec.Command(gocmd, "mod", "edit", "-x", "-require", "github.com/luxfi/[email protected]", "-replace", "github.com/luxfi/evmgpu="+filepath.Join(pwd, "..", "..", "..", "..")) // Repo root
|
|
replacer.Dir = pkg
|
|
if out, err := replacer.CombinedOutput(); err != nil {
|
|
t.Fatalf("failed to replace binding test dependency to current source tree: %v\n%s", err, out)
|
|
}
|
|
tidier := exec.Command(gocmd, "mod", "tidy", "-compat=1.23")
|
|
tidier.Dir = pkg
|
|
if out, err := tidier.CombinedOutput(); err != nil {
|
|
t.Fatalf("failed to tidy Go module file: %v\n%s", err, out)
|
|
}
|
|
// Test the entire package and report any failures
|
|
cmd := exec.Command(gocmd, "test", "./...", "-v", "-count", "1")
|
|
cmd.Dir = pkg
|
|
if out, err := cmd.CombinedOutput(); err != nil {
|
|
t.Fatalf("failed to run binding test: %v\n%s", err, out)
|
|
}
|
|
}
|