github.com/ava-labs/subnet-evm@v0.6.4/accounts/abi/bind/precompilebind/precompile_bind_test.go (about)

     1  // (c) 2019-2020, Ava Labs, Inc.
     2  //
     3  // This file is a derived work, based on the go-ethereum library whose original
     4  // notices appear below.
     5  //
     6  // It is distributed under a license compatible with the licensing terms of the
     7  // original code from which it is derived.
     8  //
     9  // Much love to the original authors for their work.
    10  // **********
    11  // Copyright 2016 The go-ethereum Authors
    12  // This file is part of the go-ethereum library.
    13  //
    14  // The go-ethereum library is free software: you can redistribute it and/or modify
    15  // it under the terms of the GNU Lesser General Public License as published by
    16  // the Free Software Foundation, either version 3 of the License, or
    17  // (at your option) any later version.
    18  //
    19  // The go-ethereum library is distributed in the hope that it will be useful,
    20  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    21  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    22  // GNU Lesser General Public License for more details.
    23  //
    24  // You should have received a copy of the GNU Lesser General Public License
    25  // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
    26  
    27  package precompilebind
    28  
    29  import (
    30  	"fmt"
    31  	"os"
    32  	"os/exec"
    33  	"path/filepath"
    34  	"runtime"
    35  	"strings"
    36  	"testing"
    37  
    38  	"github.com/ava-labs/subnet-evm/accounts/abi/bind"
    39  	"github.com/ethereum/go-ethereum/common"
    40  	"github.com/stretchr/testify/require"
    41  )
    42  
    43  var bindTests = []struct {
    44  	name            string
    45  	contract        string
    46  	abi             string
    47  	imports         string
    48  	tester          string
    49  	errMsg          string
    50  	expectAllowlist bool
    51  }{
    52  	{
    53  		"AnonOutputChecker",
    54  		"",
    55  		`
    56  			[
    57  				{"type":"function","name":"anonOutput","constant":true,"inputs":[],"outputs":[{"name":"","type":"string"}]}
    58  			]
    59  		`,
    60  		"",
    61  		"",
    62  		"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",
    63  		false,
    64  	},
    65  	{
    66  		"AnonOutputsChecker",
    67  		"",
    68  		`
    69  			[
    70  				{"type":"function","name":"anonOutputs","constant":true,"inputs":[],"outputs":[{"name":"","type":"string"},{"name":"","type":"string"}]}
    71  			]
    72  		`,
    73  		"",
    74  		"",
    75  		"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",
    76  		false,
    77  	},
    78  	{
    79  		"MixedOutputsChecker",
    80  		"",
    81  		`
    82  			[
    83  				{"type":"function","name":"mixedOutputs","constant":true,"inputs":[],"outputs":[{"name":"","type":"string"},{"name":"str","type":"string"}]}
    84  			]
    85  		`,
    86  		"",
    87  		"",
    88  		"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",
    89  		false,
    90  	},
    91  	// Test that module is generated correctly
    92  	{
    93  		`EmptyContract`,
    94  		`contract EmptyContract {}`,
    95  		"[]",
    96  		"",
    97  		"",
    98  		"no ABI methods found",
    99  		false,
   100  	},
   101  	// Test that named and anonymous inputs are handled correctly
   102  	{
   103  		`InputChecker`, ``,
   104  		`
   105  				[
   106  					{"type":"function","name":"noInput","constant":true,"inputs":[],"outputs":[]},
   107  					{"type":"function","name":"namedInput","constant":true,"inputs":[{"name":"str","type":"string"}],"outputs":[]},
   108  					{"type":"function","name":"namedInputs","constant":true,"inputs":[{"name":"str1","type":"string"},{"name":"str2","type":"string"}],"outputs":[]}
   109  				]
   110  			`,
   111  		`
   112  		"github.com/stretchr/testify/require"
   113  		`,
   114  		`
   115  		testInput := "test"
   116  		packedInput, err := PackNamedInput(testInput)
   117  		require.NoError(t, err)
   118  		// remove the first 4 bytes of the packed input
   119  		packedInput = packedInput[4:]
   120  		unpackedInput, err := UnpackNamedInputInput(packedInput)
   121  		require.NoError(t, err)
   122  		require.Equal(t, testInput, unpackedInput)
   123  
   124  		testInputStruct := NamedInputsInput{
   125  			Str1: "test1",
   126  			Str2: "test2",
   127  		}
   128  		packedInputStruct, err := PackNamedInputs(testInputStruct)
   129  		require.NoError(t, err)
   130  		// remove the first 4 bytes of the packed input
   131  		packedInputStruct = packedInputStruct[4:]
   132  		unpackedInputStruct, err := UnpackNamedInputsInput(packedInputStruct)
   133  		require.NoError(t, err)
   134  		require.Equal(t, unpackedInputStruct, testInputStruct)
   135  		`,
   136  		"",
   137  		false,
   138  	},
   139  	// Test that named and anonymous outputs are handled correctly
   140  	{
   141  		`OutputChecker`, ``,
   142  		`
   143  				[
   144  					{"type":"function","name":"noOutput","constant":true,"inputs":[],"outputs":[]},
   145  					{"type":"function","name":"namedOutput","constant":true,"inputs":[],"outputs":[{"name":"str","type":"string"}]},
   146  					{"type":"function","name":"namedOutputs","constant":true,"inputs":[],"outputs":[{"name":"str1","type":"string"},{"name":"str2","type":"string"}]}
   147  				]
   148  			`,
   149  		`
   150  			"github.com/stretchr/testify/require"
   151  			`,
   152  		`
   153  			testOutput := "test"
   154  			packedOutput, err := PackNamedOutputOutput(testOutput)
   155  			require.NoError(t, err)
   156  			unpackedOutput, err := UnpackNamedOutputOutput(packedOutput)
   157  			require.NoError(t, err)
   158  			require.Equal(t, testOutput, unpackedOutput)
   159  
   160  			testNamedOutputs := NamedOutputsOutput{
   161  				Str1: "test1",
   162  				Str2: "test2",
   163  			}
   164  			packedNamedOutputs, err := PackNamedOutputsOutput(testNamedOutputs)
   165  			require.NoError(t, err)
   166  			unpackedNamedOutputs, err := UnpackNamedOutputsOutput(packedNamedOutputs)
   167  			require.NoError(t, err)
   168  			require.Equal(t, testNamedOutputs, unpackedNamedOutputs)
   169  			`,
   170  		"",
   171  		false,
   172  	},
   173  	{
   174  		`Tupler`,
   175  		`
   176  			interface Tupler {
   177  				function tuple() constant returns (string a, int b, bytes32 c);
   178  			}
   179  		`,
   180  		`[{"constant":true,"inputs":[],"name":"tuple","outputs":[{"name":"a","type":"string"},{"name":"b","type":"int256"},{"name":"c","type":"bytes32"}],"type":"function"}]`,
   181  		`
   182  			"math/big"
   183  			"github.com/stretchr/testify/require"
   184  		`,
   185  		`
   186  			testOutput := TupleOutput{"Hi", big.NewInt(123), [32]byte{1, 2, 3}}
   187  			packedOutput, err := PackTupleOutput(testOutput)
   188  			require.NoError(t, err)
   189  			unpackedOutput, err := UnpackTupleOutput(packedOutput)
   190  			require.NoError(t, err)
   191  			require.Equal(t, testOutput, unpackedOutput)
   192  		`,
   193  		"",
   194  		false,
   195  	},
   196  	{
   197  		`Slicer`,
   198  		`
   199  			interface Slicer {
   200  				function echoAddresses(address[] input) constant returns (address[] output);
   201  				function echoInts(int[] input) constant returns (int[] output);
   202  				function echoFancyInts(uint8[23] input) constant returns (uint8[23] output);
   203  				function echoBools(bool[] input) constant returns (bool[] output);
   204  			}
   205  		`,
   206  		`[{"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"}]`,
   207  		`
   208  					"math/big"
   209  		 			"github.com/stretchr/testify/require"
   210  					"github.com/ethereum/go-ethereum/common"
   211  		`,
   212  		`
   213  			testArgs := []common.Address{common.HexToAddress("1"), common.HexToAddress("2"), common.HexToAddress("3")}
   214  			packedOutput, err := PackEchoAddressesOutput(testArgs)
   215  			require.NoError(t, err)
   216  			unpackedOutput, err := UnpackEchoAddressesOutput(packedOutput)
   217  			require.NoError(t, err)
   218  			require.Equal(t, testArgs, unpackedOutput)
   219  			packedInput, err := PackEchoAddresses(testArgs)
   220  			// remove the first 4 bytes of the packed input
   221  			packedInput = packedInput[4:]
   222  			require.NoError(t, err)
   223  			unpackedInput, err := UnpackEchoAddressesInput(packedInput)
   224  			require.NoError(t, err)
   225  			require.Equal(t, testArgs, unpackedInput)
   226  
   227  			testArgs2 := []*big.Int{common.Big1, common.Big2, common.Big3}
   228  			packedOutput2, err := PackEchoIntsOutput(testArgs2)
   229  			require.NoError(t, err)
   230  			unpackedOutput2, err := UnpackEchoIntsOutput(packedOutput2)
   231  			require.NoError(t, err)
   232  			require.Equal(t, testArgs2, unpackedOutput2)
   233  			packedInput2, err := PackEchoInts(testArgs2)
   234  			// remove the first 4 bytes of the packed input
   235  			packedInput2 = packedInput2[4:]
   236  			require.NoError(t, err)
   237  			unpackedInput2, err := UnpackEchoIntsInput(packedInput2)
   238  			require.NoError(t, err)
   239  			require.Equal(t, testArgs2, unpackedInput2)
   240  
   241  			testArgs3 := [23]uint8{1, 2, 3}
   242  			packedOutput3, err := PackEchoFancyIntsOutput(testArgs3)
   243  			require.NoError(t, err)
   244  			unpackedOutput3, err := UnpackEchoFancyIntsOutput(packedOutput3)
   245  			require.NoError(t, err)
   246  			require.Equal(t, testArgs3, unpackedOutput3)
   247  			packedInput3, err := PackEchoFancyInts(testArgs3)
   248  			// remove the first 4 bytes of the packed input
   249  			packedInput3 = packedInput3[4:]
   250  			require.NoError(t, err)
   251  			unpackedInput3, err := UnpackEchoFancyIntsInput(packedInput3)
   252  			require.NoError(t, err)
   253  			require.Equal(t, testArgs3, unpackedInput3)
   254  
   255  			testArgs4 := []bool{true, false, true}
   256  			packedOutput4, err := PackEchoBoolsOutput(testArgs4)
   257  			require.NoError(t, err)
   258  			unpackedOutput4, err := UnpackEchoBoolsOutput(packedOutput4)
   259  			require.NoError(t, err)
   260  			require.Equal(t, testArgs4, unpackedOutput4)
   261  			packedInput4, err := PackEchoBools(testArgs4)
   262  			// remove the first 4 bytes of the packed input
   263  			packedInput4 = packedInput4[4:]
   264  			require.NoError(t, err)
   265  			unpackedInput4, err := UnpackEchoBoolsInput(packedInput4)
   266  			require.NoError(t, err)
   267  			require.Equal(t, testArgs4, unpackedInput4)
   268  		`,
   269  		"",
   270  		false,
   271  	},
   272  	{
   273  		`Fallback`,
   274  		`
   275  			interface Fallback {
   276  				fallback() external payable;
   277  
   278  				receive() external payable;
   279  				function testFunction(uint t) external;
   280  			}
   281  			`,
   282  		`[{"stateMutability":"payable","type":"fallback"},{"inputs":[{"internalType":"uint256","name":"t","type":"uint256"}],"name":"testFunction","outputs":[],"stateMutability":"nonpayable","type":"function"},{"stateMutability":"payable","type":"receive"}]`,
   283  		`
   284  			"github.com/stretchr/testify/require"
   285  			"math/big"
   286  			`,
   287  		`
   288  			packedInput, err := PackTestFunction(big.NewInt(5))
   289  			require.NoError(t, err)
   290  			// remove the first 4 bytes of the packed input
   291  			packedInput = packedInput[4:]
   292  			unpackedInput, err := UnpackTestFunctionInput(packedInput)
   293  			require.NoError(t, err)
   294  			require.Equal(t, big.NewInt(5), unpackedInput)
   295  			`,
   296  		"",
   297  		false,
   298  	},
   299  	{
   300  		`Structs`,
   301  		`
   302  		interface Struct {
   303  			struct A {
   304  				bytes32 B;
   305  			}
   306  			function F() external view returns (A[] memory a, uint256[] memory c, bool[] memory d);
   307  			function G() external view returns (A[] memory a);
   308  		}
   309  		`,
   310  		`[{"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"}]`,
   311  		`
   312  			"github.com/stretchr/testify/require"
   313  			"math/big"
   314  			`,
   315  		`
   316  			testOutput := FOutput{
   317  				A: []StructsA{
   318  					{
   319  						B: [32]byte{1},
   320  					},
   321  				},
   322  				C: []*big.Int{big.NewInt(2)},
   323  				D: []bool{true,false},
   324  			}
   325  			packedOutput, err := PackFOutput(testOutput)
   326  			require.NoError(t, err)
   327  			unpackedInput, err := UnpackFOutput(packedOutput)
   328  			require.NoError(t, err)
   329  			require.Equal(t, testOutput, unpackedInput)
   330  			`,
   331  		"",
   332  		false,
   333  	},
   334  	{
   335  		`Underscorer`,
   336  		`
   337  		interface Underscorer {
   338  			function UnderscoredOutput() external returns (int _int, string _string);
   339  		}
   340  		`,
   341  		`[{"inputs":[],"name":"UnderscoredOutput","outputs":[{"internalType":"int256","name":"_int","type":"int256"},{"internalType":"string","name":"_string","type":"string"}],"stateMutability":"nonpayable","type":"function"}]`,
   342  		`
   343  			"github.com/stretchr/testify/require"
   344  			"math/big"
   345  		`,
   346  		`
   347  			testOutput := UnderscoredOutputOutput{
   348  				Int: big.NewInt(5),
   349  				String: "hello",
   350  			}
   351  			packedOutput, err := PackUnderscoredOutputOutput(testOutput)
   352  			require.NoError(t, err)
   353  			unpackedInput, err := UnpackUnderscoredOutputOutput(packedOutput)
   354  			require.NoError(t, err)
   355  			require.Equal(t, testOutput, unpackedInput)
   356  		`,
   357  		"",
   358  		false,
   359  	},
   360  	{
   361  		`OutputCollision`,
   362  		`
   363  		interface Collision {
   364  			function LowerLowerCollision() external returns (int _res, int res, int res_);
   365  		`,
   366  		`[{"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"}]`,
   367  		"",
   368  		"",
   369  		"normalized output name is duplicated",
   370  		false,
   371  	},
   372  
   373  	{
   374  		`InputCollision`,
   375  		`
   376  		interface Collision {
   377  			function LowerUpperCollision(int _res, int Res) external;
   378  		}
   379  		`,
   380  		`[{"inputs":[{"internalType":"int256","name":"_res","type":"int256"},{"internalType":"int256","name":"Res","type":"int256"}],"name":"LowerUpperCollision","outputs":[],"stateMutability":"nonpayable","type":"function"}]`, "",
   381  		"",
   382  		"normalized input name is duplicated",
   383  		false,
   384  	},
   385  	{
   386  		`DeeplyNestedArray`,
   387  		`
   388  		interface DeeplyNestedArray {
   389  			function storeDeepUintArray(uint64[3][4][5] arr) external public;
   390  			function retrieveDeepArray() public external view returns (uint64[3][4][5] arr);
   391  		}
   392  		`,
   393  		`[{"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"}]`, `
   394  			"github.com/stretchr/testify/require"
   395  		`,
   396  		`
   397  			testArr := [5][4][3]uint64{
   398  				{
   399  					{1, 2, 3},
   400  					{4, 5, 6},
   401  					{7, 8, 9},
   402  					{10, 11, 12},
   403  				},
   404  				{
   405  					{13, 14, 15},
   406  					{16, 17, 18},
   407  					{19, 20, 21},
   408  					{22, 23, 24},
   409  				},
   410  			}
   411  			packedInput, err := PackStoreDeepUintArray(testArr)
   412  			require.NoError(t, err)
   413  			// remove the first 4 bytes of the packed input
   414  			packedInput = packedInput[4:]
   415  			unpackedInput, err := UnpackStoreDeepUintArrayInput(packedInput)
   416  			require.NoError(t, err)
   417  			require.Equal(t, testArr, unpackedInput)
   418  
   419  			packedOutput, err := PackRetrieveDeepArrayOutput(testArr)
   420  			require.NoError(t, err)
   421  			unpackedOutput, err := UnpackRetrieveDeepArrayOutput(packedOutput)
   422  			require.NoError(t, err)
   423  			require.Equal(t, testArr, unpackedOutput)
   424  		`,
   425  		"",
   426  		false,
   427  	},
   428  	{
   429  		"RangeKeyword",
   430  		`
   431  		interface keywordcontract {
   432  			function functionWithKeywordParameter(uint8 func, uint8 range) external pure;
   433  		}
   434  		`,
   435  		`[{"inputs":[{"internalType":"uint8","name":"func","type":"uint8"},{"internalType":"uint8","name":"range","type":"uint8"}],"name":"functionWithKeywordParameter","outputs":[],"stateMutability":"pure","type":"function"}]`,
   436  		"",
   437  		"",
   438  		"input name func is a keyword",
   439  		false,
   440  	},
   441  	{
   442  		`HelloWorld`,
   443  		`interface IHelloWorld is IAllowList {
   444  			// sayHello returns the stored greeting string
   445  			function sayHello() external view returns (string calldata result);
   446  
   447  			// setGreeting  stores the greeting string
   448  			function setGreeting(string calldata response) external;
   449  		}
   450  		`,
   451  		`[{"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"}]`,
   452  		`"github.com/stretchr/testify/require"
   453  		 "math/big"
   454  		 "github.com/ethereum/go-ethereum/common"
   455  		 "github.com/ava-labs/subnet-evm/core/state"
   456  		 "github.com/ava-labs/subnet-evm/precompile/allowlist"
   457  		`,
   458  		`
   459  			testGreeting := "test"
   460  			packedGreeting, err := PackSetGreeting(testGreeting)
   461  			require.NoError(t, err)
   462  			// remove the first 4 bytes of the packed greeting
   463  			packedGreeting = packedGreeting[4:]
   464  			unpackedGreeting, err := UnpackSetGreetingInput(packedGreeting)
   465  			require.NoError(t, err)
   466  			require.Equal(t, testGreeting, unpackedGreeting)
   467  
   468  			// test that the allow list is generated correctly
   469  			stateDB := state.NewTestStateDB(t)
   470  			address := common.BigToAddress(big.NewInt(1))
   471  			SetHelloWorldAllowListStatus(stateDB, address, allowlist.EnabledRole)
   472  			role := GetHelloWorldAllowListStatus(stateDB, address)
   473  			require.Equal(t, role, allowlist.EnabledRole)
   474  		`,
   475  		"",
   476  		true,
   477  	},
   478  	{
   479  		`HelloWorldNoAL`,
   480  		`interface IHelloWorld{
   481  			// sayHello returns the stored greeting string
   482  			function sayHello() external view returns (string calldata result);
   483  
   484  			// setGreeting  stores the greeting string
   485  			function setGreeting(string calldata response) external;
   486  		}
   487  		`,
   488  		// This ABI does not contain readAllowlist and setEnabled.
   489  		`[{"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"}]`,
   490  		`"github.com/stretchr/testify/require"`,
   491  		`
   492  			testGreeting := "test"
   493  			packedGreeting, err := PackSetGreeting(testGreeting)
   494  			require.NoError(t, err)
   495  			// remove the first 4 bytes of the packed greeting
   496  			packedGreeting = packedGreeting[4:]
   497  			unpackedGreeting, err := UnpackSetGreetingInput(packedGreeting)
   498  			require.NoError(t, err)
   499  			require.Equal(t, testGreeting, unpackedGreeting)
   500  		`,
   501  		"",
   502  		false,
   503  	},
   504  	{
   505  		`IEventer`,
   506  		`
   507  		interface IEventer {
   508  			event test(address indexed addressTest, uint indexed intTest, bytes bytesTest);
   509  			event empty();
   510  			event indexed(address addr, int8 indexed num);
   511  			event mixed(address indexed addr, int8 num);
   512  			event dynamic(string indexed idxStr, bytes indexed idxDat, string str, bytes dat);
   513  			event unnamed(uint8 indexed, uint8 indexed);
   514  			function eventTest() external view returns (string memory result);
   515  		}
   516  		`,
   517  		`[{"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}]}]`,
   518  		`"github.com/stretchr/testify/require"
   519  		"github.com/ethereum/go-ethereum/common"
   520  		"github.com/ava-labs/subnet-evm/precompile/contract"
   521  		`,
   522  		`
   523  			testAddr := common.Address{1}
   524  			testInt := int8(5)
   525  			testUint := uint8(5)
   526  			testBytes := []byte{1, 2, 3}
   527  
   528  			testEventData := TestEventData{
   529  				BytesTest: testBytes,
   530  			}
   531  			topics, data, err := PackTestEvent(testAddr, testUint, testEventData)
   532  			require.NoError(t, err)
   533  			eventID := IEventerABI.Events["test"].ID
   534  			require.Equal(t, eventID, topics[0])
   535  			unpacked, err := UnpackTestEventData(data)
   536  			require.NoError(t, err)
   537  			require.Equal(t, testBytes, unpacked.BytesTest)
   538  			gasCost := GetTestEventGasCost(testEventData)
   539  			require.Equal(t, contract.LogGas + 3 * contract.LogTopicGas + contract.LogDataGas, gasCost)
   540  
   541  			topics, data, err = PackEmptyEvent()
   542  			require.NoError(t, err)
   543  			eventID = IEventerABI.Events["empty"].ID
   544  			require.Len(t, topics, 1)
   545  			require.Equal(t, eventID, topics[0])
   546  			require.Equal(t, 0, len(data))
   547  			require.Equal(t, contract.LogGas, GetEmptyEventGasCost())
   548  
   549  			topics, data, err = PackIndexedEvent(testAddr, testInt)
   550  			require.NoError(t, err)
   551  			eventID = IEventerABI.Events["indexed"].ID
   552  			require.Len(t, topics, 3)
   553  			require.Equal(t, eventID, topics[0])
   554  			require.Equal(t, testAddr.Hash(), topics[1])
   555  			require.Equal(t, 0, len(data))
   556  			require.Equal(t, contract.LogGas + 3 * contract.LogTopicGas, GetIndexedEventGasCost())
   557  
   558  			testMixedData := MixedEventData{
   559  				Num: testInt,
   560  			}
   561  			topics, data, err = PackMixedEvent(testAddr, testMixedData)
   562  			require.NoError(t, err)
   563  			eventID = IEventerABI.Events["mixed"].ID
   564  			require.Len(t, topics, 2)
   565  			require.Equal(t, eventID, topics[0])
   566  			require.Equal(t, testAddr.Hash(), topics[1])
   567  			unpackedMixedData, err := UnpackMixedEventData(data)
   568  			require.NoError(t, err)
   569  			require.Equal(t, testMixedData, unpackedMixedData)
   570  			require.Equal(t, contract.LogGas + 2 * contract.LogTopicGas + contract.LogDataGas, GetMixedEventGasCost(testMixedData))
   571  
   572  			testDynamicData := DynamicEventData{
   573  				Str:    "test",
   574  				Dat:    testBytes,
   575  			}
   576  			topics, data, err = PackDynamicEvent("test", testBytes, testDynamicData)
   577  			require.NoError(t, err)
   578  			eventID = IEventerABI.Events["dynamic"].ID
   579  			require.Len(t, topics, 3)
   580  			require.Equal(t, eventID, topics[0])
   581  			unpackedDynamicData, err := UnpackDynamicEventData(data)
   582  			require.NoError(t, err)
   583  			require.Equal(t, testDynamicData, unpackedDynamicData)
   584  			require.Equal(t, contract.LogGas + 3 * contract.LogTopicGas + 2 * contract.LogDataGas, GetDynamicEventGasCost(testDynamicData))
   585  
   586  			topics, data, err = PackUnnamedEvent(testUint, testUint)
   587  			require.NoError(t, err)
   588  			eventID = IEventerABI.Events["unnamed"].ID
   589  			require.Len(t, topics, 3)
   590  			require.Equal(t, eventID, topics[0])
   591  			require.Equal(t, 0, len(data))
   592  			require.Equal(t, contract.LogGas + 3 * contract.LogTopicGas, GetUnnamedEventGasCost())
   593  	`,
   594  		"",
   595  		false,
   596  	},
   597  	{
   598  		`IEventerAnonymous`,
   599  		`
   600  		interface IEventer {
   601  			event Anonymous(address indexed, uint indexed, bytes) anonymous;
   602  			function eventTest() external view returns (string memory result);
   603  		}
   604  		`,
   605  		`[{"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"}]`,
   606  		``,
   607  		``,
   608  		errNoAnonymousEvent.Error(),
   609  		false,
   610  	},
   611  }
   612  
   613  // Tests that packages generated by the binder can be successfully compiled and
   614  // the requested tester run against it.
   615  func TestPrecompileBind(t *testing.T) {
   616  	// Skip the test if no Go command can be found
   617  	gocmd := runtime.GOROOT() + "/bin/go"
   618  	if !common.FileExist(gocmd) {
   619  		t.Skip("go sdk not found for testing")
   620  	}
   621  	// Create a temporary workspace for the test suite
   622  	ws := t.TempDir()
   623  
   624  	pkg := filepath.Join(ws, "precompilebindtest")
   625  	if err := os.MkdirAll(pkg, 0o700); err != nil {
   626  		t.Fatalf("failed to create package: %v", err)
   627  	}
   628  	// Generate the test suite for all the contracts
   629  	for i, tt := range bindTests {
   630  		t.Run(tt.name, func(t *testing.T) {
   631  			types := []string{tt.name}
   632  
   633  			// Generate the binding and create a Go source file in the workspace
   634  			bindedFiles, err := PrecompileBind(types, tt.abi, []string{""}, nil, tt.name, bind.LangGo, nil, nil, "contract.abi", true)
   635  			if tt.errMsg != "" {
   636  				require.ErrorContains(t, err, tt.errMsg)
   637  				return
   638  			}
   639  			if err != nil {
   640  				t.Fatalf("test %d: failed to generate binding: %v", i, err)
   641  			}
   642  
   643  			precompilePath := filepath.Join(pkg, tt.name)
   644  			if err := os.MkdirAll(precompilePath, 0o700); err != nil {
   645  				t.Fatalf("failed to create package: %v", err)
   646  			}
   647  			for _, file := range bindedFiles {
   648  				switch file.FileName {
   649  				case ContractFileName:
   650  					// check if the allowlist functions are generated
   651  					if tt.expectAllowlist {
   652  						require.Contains(t, file.Content, "allowlist.CreateAllowListFunctions(", "generated contract does not contain AllowListFunctions")
   653  					} else {
   654  						require.NotContains(t, file.Content, "allowlist.CreateAllowListFunctions(", "generated contract contains AllowListFunctions")
   655  					}
   656  				case ModuleFileName:
   657  					// change address to a suitable one for testing
   658  					file.Content = strings.Replace(file.Content, `common.HexToAddress("{ASUITABLEHEXADDRESS}")`, `common.HexToAddress("0x03000000000000000000000000000000000000ff")`, 1)
   659  				}
   660  				if err = os.WriteFile(filepath.Join(precompilePath, file.FileName), []byte(file.Content), 0o600); err != nil {
   661  					t.Fatalf("test %d: failed to write binding: %v", i, err)
   662  				}
   663  			}
   664  			if err = os.WriteFile(filepath.Join(precompilePath, "contract.abi"), []byte(tt.abi), 0o600); err != nil {
   665  				t.Fatalf("test %d: failed to write binding: %v", i, err)
   666  			}
   667  
   668  			// Generate the test file with the injected test code
   669  			code := fmt.Sprintf(`
   670  			package %s
   671  
   672  			import (
   673  				"testing"
   674  				%s
   675  			)
   676  
   677  			func Test%s(t *testing.T) {
   678  				%s
   679  			}
   680  		`, tt.name, tt.imports, tt.name, tt.tester)
   681  			if err := os.WriteFile(filepath.Join(precompilePath, strings.ToLower(tt.name)+"_test.go"), []byte(code), 0o600); err != nil {
   682  				t.Fatalf("test %d: failed to write tests: %v", i, err)
   683  			}
   684  		})
   685  	}
   686  
   687  	moder := exec.Command(gocmd, "mod", "init", "precompilebindtest")
   688  	moder.Dir = pkg
   689  	if out, err := moder.CombinedOutput(); err != nil {
   690  		t.Fatalf("failed to convert binding test to modules: %v\n%s", err, out)
   691  	}
   692  	pwd, _ := os.Getwd()
   693  	replacer := exec.Command(gocmd, "mod", "edit", "-x", "-require", "github.com/ava-labs/subnet-evm@v0.0.0", "-replace", "github.com/ava-labs/subnet-evm="+filepath.Join(pwd, "..", "..", "..", "..")) // Repo root
   694  	replacer.Dir = pkg
   695  	if out, err := replacer.CombinedOutput(); err != nil {
   696  		t.Fatalf("failed to replace binding test dependency to current source tree: %v\n%s", err, out)
   697  	}
   698  	tidier := exec.Command(gocmd, "mod", "tidy", "-compat=1.21")
   699  	tidier.Dir = pkg
   700  	if out, err := tidier.CombinedOutput(); err != nil {
   701  		t.Fatalf("failed to tidy Go module file: %v\n%s", err, out)
   702  	}
   703  	// Test the entire package and report any failures
   704  	cmd := exec.Command(gocmd, "test", "./...", "-v", "-count", "1")
   705  	cmd.Dir = pkg
   706  	if out, err := cmd.CombinedOutput(); err != nil {
   707  		t.Fatalf("failed to run binding test: %v\n%s", err, out)
   708  	}
   709  }