From 5598edfe48594132840b94ec25a1b9225124cbd1 Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Wed, 22 Jan 2025 13:47:56 +0100 Subject: [PATCH] accounts/abi/bind/v2: update tests - add go:generate lines for updating the test bindings - move tests to bind_test package because the contracts now import bind/v2 - simplify some code in tests --- accounts/abi/bind/v2/generate_test.go | 100 +++++++++++++ accounts/abi/bind/v2/lib_test.go | 200 ++++++-------------------- 2 files changed, 140 insertions(+), 160 deletions(-) create mode 100644 accounts/abi/bind/v2/generate_test.go diff --git a/accounts/abi/bind/v2/generate_test.go b/accounts/abi/bind/v2/generate_test.go new file mode 100644 index 0000000000..4c42e477d4 --- /dev/null +++ b/accounts/abi/bind/v2/generate_test.go @@ -0,0 +1,100 @@ +// Copyright 2024 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 . + +package bind_test + +import ( + "encoding/json" + "os" + "path/filepath" + "strings" + "testing" + + bind1 "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/cmd/utils" + "github.com/ethereum/go-ethereum/common/compiler" + "github.com/ethereum/go-ethereum/crypto" +) + +//go:generate go run github.com/ethereum/go-ethereum/cmd/abigen -v2 -combined-json internal/contracts/db/combined-abi.json -type DBStats -pkg db -out internal/contracts/db/bindings.go +//go:generate go run github.com/ethereum/go-ethereum/cmd/abigen -v2 -combined-json internal/contracts/events/combined-abi.json -type C -pkg events -out internal/contracts/events/bindings.go +//go:generate go run github.com/ethereum/go-ethereum/cmd/abigen -v2 -combined-json internal/contracts/nested_libraries/combined-abi.json -type C1 -pkg nested_libraries -out internal/contracts/nested_libraries/bindings.go +//go:generate go run github.com/ethereum/go-ethereum/cmd/abigen -v2 -combined-json internal/contracts/solc_errors/combined-abi.json -type C -pkg solc_errors -out internal/contracts/solc_errors/bindings.go +//go:generate go run github.com/ethereum/go-ethereum/cmd/abigen -v2 -combined-json internal/contracts/uint256arrayreturn/combined-abi.json -type C -pkg uint256arrayreturn -out internal/contracts/uint256arrayreturn/bindings.go + +// TestBindingGeneration tests that re-running generation of bindings does not result in +// mutations to the binding code. +func TestBindingGeneration(t *testing.T) { + matches, _ := filepath.Glob("internal/contracts/*") + var dirs []string + for _, match := range matches { + f, _ := os.Stat(match) + if f.IsDir() { + dirs = append(dirs, f.Name()) + } + } + + for _, dir := range dirs { + var ( + abis []string + bins []string + types []string + libs = make(map[string]string) + ) + basePath := filepath.Join("internal/contracts", dir) + combinedJsonPath := filepath.Join(basePath, "combined-abi.json") + abiBytes, err := os.ReadFile(combinedJsonPath) + if err != nil { + t.Fatalf("error trying to read file %s: %v", combinedJsonPath, err) + } + contracts, err := compiler.ParseCombinedJSON(abiBytes, "", "", "", "") + if err != nil { + t.Fatalf("Failed to read contract information from json output: %v", err) + } + + for name, contract := range contracts { + // fully qualified name is of the form : + nameParts := strings.Split(name, ":") + typeName := nameParts[len(nameParts)-1] + abi, err := json.Marshal(contract.Info.AbiDefinition) // Flatten the compiler parse + if err != nil { + utils.Fatalf("Failed to parse ABIs from compiler output: %v", err) + } + abis = append(abis, string(abi)) + bins = append(bins, contract.Code) + types = append(types, typeName) + + // Derive the library placeholder which is a 34 character prefix of the + // hex encoding of the keccak256 hash of the fully qualified library name. + // Note that the fully qualified library name is the path of its source + // file and the library name separated by ":". + libPattern := crypto.Keccak256Hash([]byte(name)).String()[2:36] // the first 2 chars are 0x + libs[libPattern] = typeName + } + code, err := bind1.BindV2(types, abis, bins, dir, libs, make(map[string]string)) + if err != nil { + t.Fatalf("error creating bindings for package %s: %v", dir, err) + } + + existingBindings, err := os.ReadFile(filepath.Join(basePath, "bindings.go")) + if err != nil { + t.Fatalf("ReadFile returned error: %v", err) + } + if code != string(existingBindings) { + t.Fatalf("code mismatch for %s", dir) + } + } +} diff --git a/accounts/abi/bind/v2/lib_test.go b/accounts/abi/bind/v2/lib_test.go index ec44f82e34..eec93e67ab 100644 --- a/accounts/abi/bind/v2/lib_test.go +++ b/accounts/abi/bind/v2/lib_test.go @@ -14,29 +14,24 @@ // You should have received a copy of the GNU Lesser General Public License // along with the go-ethereum library. If not, see . -package bind +package bind_test import ( "context" "encoding/json" "io" "math/big" - "os" - "path/filepath" - "strings" "testing" "time" + "github.com/ethereum/go-ethereum/accounts/abi" bind1 "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/accounts/abi/bind/backends" + "github.com/ethereum/go-ethereum/accounts/abi/bind/v2" "github.com/ethereum/go-ethereum/accounts/abi/bind/v2/internal/contracts/events" "github.com/ethereum/go-ethereum/accounts/abi/bind/v2/internal/contracts/nested_libraries" "github.com/ethereum/go-ethereum/accounts/abi/bind/v2/internal/contracts/solc_errors" - - "github.com/ethereum/go-ethereum/accounts/abi" - "github.com/ethereum/go-ethereum/accounts/abi/bind/backends" - "github.com/ethereum/go-ethereum/cmd/utils" "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/common/compiler" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/eth/ethconfig" @@ -59,7 +54,7 @@ func JSON(reader io.Reader) (abi.ABI, error) { return instance, nil } -func testSetup() (*TransactOpts, *backends.SimulatedBackend, error) { +func testSetup() (*bind.TransactOpts, *backends.SimulatedBackend, error) { testAddr := crypto.PubkeyToAddress(testKey.PublicKey) backend := simulated.NewBackend( types.GenesisAlloc{ @@ -71,7 +66,7 @@ func testSetup() (*TransactOpts, *backends.SimulatedBackend, error) { ) signer := types.LatestSigner(params.AllDevChainProtocolChanges) - opts := &TransactOpts{ + opts := &bind.TransactOpts{ From: testAddr, Nonce: nil, Signer: func(address common.Address, tx *types.Transaction) (*types.Transaction, error) { @@ -97,9 +92,9 @@ func testSetup() (*TransactOpts, *backends.SimulatedBackend, error) { return opts, &bindBackend, nil } -func makeTestDeployer(auth *TransactOpts, backend ContractBackend) func(input, deployer []byte) (common.Address, *types.Transaction, error) { +func makeTestDeployer(auth *bind.TransactOpts, backend bind.ContractBackend) func(input, deployer []byte) (common.Address, *types.Transaction, error) { return func(input, deployer []byte) (common.Address, *types.Transaction, error) { - addr, tx, _, err := DeployContractRaw(auth, deployer, backend, input) + addr, tx, _, err := bind.DeployContractRaw(auth, deployer, backend, input) return addr, tx, err } } @@ -113,13 +108,9 @@ func TestDeploymentLibraries(t *testing.T) { } defer bindBackend.Backend.Close() - ctrct, err := nested_libraries.NewC1() - if err != nil { - panic(err) - } - - constructorInput := ctrct.PackConstructor(big.NewInt(42), big.NewInt(1)) - deploymentParams := bind1.NewDeploymentParams([]*MetaData{&nested_libraries.C1MetaData}, map[string][]byte{nested_libraries.C1MetaData.Pattern: constructorInput}, nil) + c := nested_libraries.NewC1() + constructorInput := c.PackConstructor(big.NewInt(42), big.NewInt(1)) + deploymentParams := bind1.NewDeploymentParams([]*bind.MetaData{&nested_libraries.C1MetaData}, map[string][]byte{nested_libraries.C1MetaData.Pattern: constructorInput}, nil) res, err := bind1.LinkAndDeploy(deploymentParams, makeTestDeployer(opts, bindBackend)) if err != nil { @@ -136,26 +127,15 @@ func TestDeploymentLibraries(t *testing.T) { t.Fatalf("error deploying library: %+v", err) } } - c, err := nested_libraries.NewC1() - if err != nil { - t.Fatalf("err is %v", err) - } + doInput, err := c.PackDo(big.NewInt(1)) if err != nil { t.Fatalf("pack function input err: %v\n", doInput) } - contractAddr := res.Addrs[nested_libraries.C1MetaData.Pattern] - callOpts := &CallOpts{ - From: common.Address{}, - Context: context.Background(), - } - - ctrctInstance := ContractInstance{ - Address: contractAddr, - Backend: bindBackend, - } - internalCallCount, err := Call(ctrctInstance, callOpts, doInput, ctrct.UnpackDo) + callOpts := &bind.CallOpts{From: common.Address{}, Context: context.Background()} + instance := c.Instance(bindBackend, contractAddr) + internalCallCount, err := bind.Call(instance, callOpts, doInput, c.UnpackDo) if err != nil { t.Fatalf("err unpacking result: %v", err) } @@ -192,14 +172,11 @@ func TestDeploymentWithOverrides(t *testing.T) { } } - ctrct, err := nested_libraries.NewC1() - if err != nil { - panic(err) - } - constructorInput := ctrct.PackConstructor(big.NewInt(42), big.NewInt(1)) + c := nested_libraries.NewC1() + constructorInput := c.PackConstructor(big.NewInt(42), big.NewInt(1)) overrides := res.Addrs // deploy the contract - deploymentParams = bind1.NewDeploymentParams([]*MetaData{&nested_libraries.C1MetaData}, map[string][]byte{nested_libraries.C1MetaData.Pattern: constructorInput}, overrides) + deploymentParams = bind1.NewDeploymentParams([]*bind.MetaData{&nested_libraries.C1MetaData}, map[string][]byte{nested_libraries.C1MetaData.Pattern: constructorInput}, overrides) res, err = bind1.LinkAndDeploy(deploymentParams, makeTestDeployer(opts, bindBackend)) if err != nil { t.Fatalf("err: %+v\n", err) @@ -217,32 +194,16 @@ func TestDeploymentWithOverrides(t *testing.T) { } // call the deployed contract and make sure it returns the correct result - c, err := nested_libraries.NewC1() - if err != nil { - t.Fatalf("err is %v", err) - } doInput, err := c.PackDo(big.NewInt(1)) if err != nil { t.Fatalf("pack function input err: %v\n", doInput) } - cABI, err := nested_libraries.C1MetaData.GetAbi() + instance := c.Instance(bindBackend, res.Addrs[nested_libraries.C1MetaData.Pattern]) + callOpts := new(bind.CallOpts) + internalCallCount, err := bind.Call(instance, callOpts, doInput, c.UnpackDo) if err != nil { - t.Fatalf("error getting abi object: %v", err) - } - contractAddr := res.Addrs[nested_libraries.C1MetaData.Pattern] - boundContract := bind1.NewBoundContract(contractAddr, *cABI, bindBackend, bindBackend, bindBackend) - callOpts := &CallOpts{ - From: common.Address{}, - Context: context.Background(), - } - callRes, err := boundContract.CallRaw(callOpts, doInput) - if err != nil { - t.Fatalf("err calling contract: %v", err) - } - internalCallCount, err := c.UnpackDo(callRes) - if err != nil { - t.Fatalf("err unpacking result: %v", err) + t.Fatalf("error calling contract: %v", err) } if internalCallCount.Uint64() != 6 { t.Fatalf("expected internal call count of 6. got %d.", internalCallCount.Uint64()) @@ -256,7 +217,7 @@ func TestEvents(t *testing.T) { t.Fatalf("error setting up testing env: %v", err) } - deploymentParams := bind1.NewDeploymentParams([]*MetaData{&events.CMetaData}, nil, nil) + deploymentParams := bind1.NewDeploymentParams([]*bind.MetaData{&events.CMetaData}, nil, nil) res, err := bind1.LinkAndDeploy(deploymentParams, makeTestDeployer(txAuth, backend)) if err != nil { t.Fatalf("error deploying contract for testing: %v", err) @@ -267,37 +228,25 @@ func TestEvents(t *testing.T) { t.Fatalf("WaitDeployed failed %v", err) } - ctrct, err := events.NewC() - if err != nil { - t.Fatalf("error instantiating contract instance: %v", err) - } - - ctrctABI, _ := events.CMetaData.GetAbi() - instance := ContractInstance{ - res.Addrs[events.CMetaData.Pattern], - backend, - *ctrctABI, - } + c := events.NewC() + instance := c.Instance(backend, res.Addrs[events.CMetaData.Pattern]) newCBasic1Ch := make(chan *events.CBasic1) newCBasic2Ch := make(chan *events.CBasic2) - watchOpts := &bind1.WatchOpts{ - Start: nil, - Context: context.Background(), - } - sub1, err := WatchEvents(instance, watchOpts, events.CBasic1EventName, ctrct.UnpackBasic1Event, newCBasic1Ch) + watchOpts := &bind1.WatchOpts{} + sub1, err := bind.WatchEvents(instance, watchOpts, events.CBasic1EventName, c.UnpackBasic1Event, newCBasic1Ch) if err != nil { t.Fatalf("WatchEvents returned error: %v", err) } - sub2, err := WatchEvents(instance, watchOpts, events.CBasic2EventName, ctrct.UnpackBasic2Event, newCBasic2Ch) + sub2, err := bind.WatchEvents(instance, watchOpts, events.CBasic2EventName, c.UnpackBasic2Event, newCBasic2Ch) if err != nil { t.Fatalf("WatchEvents returned error: %v", err) } defer sub1.Unsubscribe() defer sub2.Unsubscribe() - packedInput, _ := ctrct.PackEmitMulti() - tx, err := Transact(instance, txAuth, packedInput) + packedInput, _ := c.PackEmitMulti() + tx, err := bind.Transact(instance, txAuth, packedInput) if err != nil { t.Fatalf("failed to send transaction: %v", err) } @@ -332,15 +281,15 @@ done: // now, test that we can filter those same logs after they were included in the chain - filterOpts := &FilterOpts{ + filterOpts := &bind.FilterOpts{ Start: 0, Context: context.Background(), } - it, err := FilterEvents(instance, filterOpts, events.CBasic1EventName, ctrct.UnpackBasic1Event) + it, err := bind.FilterEvents(instance, filterOpts, events.CBasic1EventName, c.UnpackBasic1Event) if err != nil { t.Fatalf("error filtering logs %v\n", err) } - it2, err := FilterEvents(instance, filterOpts, events.CBasic2EventName, ctrct.UnpackBasic2Event) + it2, err := bind.FilterEvents(instance, filterOpts, events.CBasic2EventName, c.UnpackBasic2Event) if err != nil { t.Fatalf("error filtering logs %v\n", err) } @@ -367,7 +316,7 @@ func TestErrors(t *testing.T) { t.Fatalf("error setting up testing env: %v", err) } - deploymentParams := bind1.NewDeploymentParams([]*MetaData{&solc_errors.CMetaData}, nil, nil) + deploymentParams := bind1.NewDeploymentParams([]*bind.MetaData{&solc_errors.CMetaData}, nil, nil) res, err := bind1.LinkAndDeploy(deploymentParams, makeTestDeployer(txAuth, backend)) if err != nil { t.Fatalf("error deploying contract for testing: %v", err) @@ -378,17 +327,11 @@ func TestErrors(t *testing.T) { t.Fatalf("WaitDeployed failed %v", err) } - ctrct, _ := solc_errors.NewC() - - var packedInput []byte - opts := &CallOpts{ - From: res.Addrs[solc_errors.CMetaData.Pattern], - } - packedInput, _ = ctrct.PackFoo() - - ctrctABI, _ := solc_errors.CMetaData.GetAbi() - instance := ContractInstance{res.Addrs[solc_errors.CMetaData.Pattern], backend, *ctrctABI} - _, err = Call(instance, opts, packedInput, func(packed []byte) (struct{}, error) { return struct{}{}, nil }) + c := solc_errors.NewC() + instance := c.Instance(backend, res.Addrs[solc_errors.CMetaData.Pattern]) + packedInput, _ := c.PackFoo() + opts := &bind.CallOpts{From: res.Addrs[solc_errors.CMetaData.Pattern]} + _, err = bind.Call[struct{}](instance, opts, packedInput, nil) if err == nil { t.Fatalf("expected call to fail") } @@ -396,7 +339,7 @@ func TestErrors(t *testing.T) { if !hasRevertErrorData { t.Fatalf("expected call error to contain revert error data.") } - rawUnpackedErr := ctrct.UnpackError(raw) + rawUnpackedErr := c.UnpackError(raw) if rawUnpackedErr == nil { t.Fatalf("expected to unpack error") } @@ -418,66 +361,3 @@ func TestErrors(t *testing.T) { t.Fatalf("bad unpacked error result: expected Arg4 to be false. got true") } } - -// TestBindingGeneration tests that re-running generation of bindings does not result in mutations to the binding code -func TestBindingGeneration(t *testing.T) { - matches, _ := filepath.Glob("internal/contracts/*") - var dirs []string - for _, match := range matches { - f, _ := os.Stat(match) - if f.IsDir() { - dirs = append(dirs, f.Name()) - } - } - - for _, dir := range dirs { - var ( - abis []string - bins []string - types []string - libs = make(map[string]string) - ) - basePath := filepath.Join("internal/contracts", dir) - combinedJsonPath := filepath.Join(basePath, "combined-abi.json") - abiBytes, err := os.ReadFile(combinedJsonPath) - if err != nil { - t.Fatalf("error trying to read file %s: %v", combinedJsonPath, err) - } - contracts, err := compiler.ParseCombinedJSON(abiBytes, "", "", "", "") - if err != nil { - t.Fatalf("Failed to read contract information from json output: %v", err) - } - - for name, contract := range contracts { - // fully qualified name is of the form : - nameParts := strings.Split(name, ":") - typeName := nameParts[len(nameParts)-1] - abi, err := json.Marshal(contract.Info.AbiDefinition) // Flatten the compiler parse - if err != nil { - utils.Fatalf("Failed to parse ABIs from compiler output: %v", err) - } - abis = append(abis, string(abi)) - bins = append(bins, contract.Code) - types = append(types, typeName) - - // Derive the library placeholder which is a 34 character prefix of the - // hex encoding of the keccak256 hash of the fully qualified library name. - // Note that the fully qualified library name is the path of its source - // file and the library name separated by ":". - libPattern := crypto.Keccak256Hash([]byte(name)).String()[2:36] // the first 2 chars are 0x - libs[libPattern] = typeName - } - code, err := bind1.BindV2(types, abis, bins, dir, libs, make(map[string]string)) - if err != nil { - t.Fatalf("error creating bindings for package %s: %v", dir, err) - } - - existingBindings, err := os.ReadFile(filepath.Join(basePath, "bindings.go")) - if err != nil { - t.Fatalf("ReadFile returned error: %v", err) - } - if code != string(existingBindings) { - t.Fatalf("code mismatch for %s", dir) - } - } -}