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 }