github.com/ethereum/go-ethereum@v1.16.1/accounts/abi/bind/v2/lib_test.go (about)

     1  // Copyright 2024 The go-ethereum Authors
     2  // This file is part of the go-ethereum library.
     3  //
     4  // The go-ethereum library is free software: you can redistribute it and/or modify
     5  // it under the terms of the GNU Lesser General Public License as published by
     6  // the Free Software Foundation, either version 3 of the License, or
     7  // (at your option) any later version.
     8  //
     9  // The go-ethereum library is distributed in the hope that it will be useful,
    10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    12  // GNU Lesser General Public License for more details.
    13  //
    14  // You should have received a copy of the GNU Lesser General Public License
    15  // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
    16  
    17  package bind_test
    18  
    19  import (
    20  	"context"
    21  	"math/big"
    22  	"testing"
    23  	"time"
    24  
    25  	"github.com/ethereum/go-ethereum/accounts/abi/bind/backends"
    26  	"github.com/ethereum/go-ethereum/accounts/abi/bind/v2"
    27  	"github.com/ethereum/go-ethereum/accounts/abi/bind/v2/internal/contracts/events"
    28  	"github.com/ethereum/go-ethereum/accounts/abi/bind/v2/internal/contracts/nested_libraries"
    29  	"github.com/ethereum/go-ethereum/accounts/abi/bind/v2/internal/contracts/solc_errors"
    30  	"github.com/ethereum/go-ethereum/common"
    31  	"github.com/ethereum/go-ethereum/core/types"
    32  	"github.com/ethereum/go-ethereum/crypto"
    33  	"github.com/ethereum/go-ethereum/eth/ethconfig"
    34  	"github.com/ethereum/go-ethereum/ethclient"
    35  	"github.com/ethereum/go-ethereum/ethclient/simulated"
    36  	"github.com/ethereum/go-ethereum/node"
    37  	"github.com/ethereum/go-ethereum/params"
    38  )
    39  
    40  var testKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
    41  var testAddr = crypto.PubkeyToAddress(testKey.PublicKey)
    42  
    43  func testSetup() (*backends.SimulatedBackend, error) {
    44  	backend := simulated.NewBackend(
    45  		types.GenesisAlloc{
    46  			testAddr: {Balance: big.NewInt(10000000000000000)},
    47  		},
    48  		func(nodeConf *node.Config, ethConf *ethconfig.Config) {
    49  			ethConf.Genesis.Difficulty = big.NewInt(0)
    50  		},
    51  	)
    52  
    53  	// we should just be able to use the backend directly, instead of using
    54  	// this deprecated interface. However, the simulated backend no longer
    55  	// implements backends.SimulatedBackend...
    56  	bindBackend := backends.SimulatedBackend{
    57  		Backend: backend,
    58  		Client:  backend.Client(),
    59  	}
    60  	return &bindBackend, nil
    61  }
    62  
    63  func makeTestDeployer(backend simulated.Client) func(input, deployer []byte) (common.Address, *types.Transaction, error) {
    64  	chainId, _ := backend.ChainID(context.Background())
    65  	return bind.DefaultDeployer(bind.NewKeyedTransactor(testKey, chainId), backend)
    66  }
    67  
    68  // test that deploying a contract with library dependencies works,
    69  // verifying by calling method on the deployed contract.
    70  func TestDeploymentLibraries(t *testing.T) {
    71  	bindBackend, err := testSetup()
    72  	if err != nil {
    73  		t.Fatalf("err setting up test: %v", err)
    74  	}
    75  	defer bindBackend.Backend.Close()
    76  
    77  	c := nested_libraries.NewC1()
    78  	constructorInput := c.PackConstructor(big.NewInt(42), big.NewInt(1))
    79  	deploymentParams := &bind.DeploymentParams{
    80  		Contracts: []*bind.MetaData{&nested_libraries.C1MetaData},
    81  		Inputs:    map[string][]byte{nested_libraries.C1MetaData.ID: constructorInput},
    82  	}
    83  	res, err := bind.LinkAndDeploy(deploymentParams, makeTestDeployer(bindBackend.Client))
    84  	if err != nil {
    85  		t.Fatalf("err: %+v\n", err)
    86  	}
    87  	bindBackend.Commit()
    88  
    89  	if len(res.Addresses) != 5 {
    90  		t.Fatalf("deployment should have generated 5 addresses.  got %d", len(res.Addresses))
    91  	}
    92  	for _, tx := range res.Txs {
    93  		_, err = bind.WaitDeployed(context.Background(), bindBackend, tx.Hash())
    94  		if err != nil {
    95  			t.Fatalf("error deploying library: %+v", err)
    96  		}
    97  	}
    98  
    99  	doInput := c.PackDo(big.NewInt(1))
   100  	contractAddr := res.Addresses[nested_libraries.C1MetaData.ID]
   101  	callOpts := &bind.CallOpts{From: common.Address{}, Context: context.Background()}
   102  	instance := c.Instance(bindBackend, contractAddr)
   103  	internalCallCount, err := bind.Call(instance, callOpts, doInput, c.UnpackDo)
   104  	if err != nil {
   105  		t.Fatalf("err unpacking result: %v", err)
   106  	}
   107  	if internalCallCount.Uint64() != 6 {
   108  		t.Fatalf("expected internal call count of 6.  got %d.", internalCallCount.Uint64())
   109  	}
   110  }
   111  
   112  // Same as TestDeployment.  However, stagger the deployments with overrides:
   113  // first deploy the library deps and then the contract.
   114  func TestDeploymentWithOverrides(t *testing.T) {
   115  	bindBackend, err := testSetup()
   116  	if err != nil {
   117  		t.Fatalf("err setting up test: %v", err)
   118  	}
   119  	defer bindBackend.Backend.Close()
   120  
   121  	// deploy all the library dependencies of our target contract, but not the target contract itself.
   122  	deploymentParams := &bind.DeploymentParams{
   123  		Contracts: nested_libraries.C1MetaData.Deps,
   124  	}
   125  	res, err := bind.LinkAndDeploy(deploymentParams, makeTestDeployer(bindBackend))
   126  	if err != nil {
   127  		t.Fatalf("err: %+v\n", err)
   128  	}
   129  	bindBackend.Commit()
   130  
   131  	if len(res.Addresses) != 4 {
   132  		t.Fatalf("deployment should have generated 4 addresses.  got %d", len(res.Addresses))
   133  	}
   134  	for _, tx := range res.Txs {
   135  		_, err = bind.WaitDeployed(context.Background(), bindBackend, tx.Hash())
   136  		if err != nil {
   137  			t.Fatalf("error deploying library: %+v", err)
   138  		}
   139  	}
   140  
   141  	c := nested_libraries.NewC1()
   142  	constructorInput := c.PackConstructor(big.NewInt(42), big.NewInt(1))
   143  	overrides := res.Addresses
   144  
   145  	// deploy the contract
   146  	deploymentParams = &bind.DeploymentParams{
   147  		Contracts: []*bind.MetaData{&nested_libraries.C1MetaData},
   148  		Inputs:    map[string][]byte{nested_libraries.C1MetaData.ID: constructorInput},
   149  		Overrides: overrides,
   150  	}
   151  	res, err = bind.LinkAndDeploy(deploymentParams, makeTestDeployer(bindBackend))
   152  	if err != nil {
   153  		t.Fatalf("err: %+v\n", err)
   154  	}
   155  	bindBackend.Commit()
   156  
   157  	if len(res.Addresses) != 1 {
   158  		t.Fatalf("deployment should have generated 1 address.  got %d", len(res.Addresses))
   159  	}
   160  	for _, tx := range res.Txs {
   161  		_, err = bind.WaitDeployed(context.Background(), bindBackend, tx.Hash())
   162  		if err != nil {
   163  			t.Fatalf("error deploying library: %+v", err)
   164  		}
   165  	}
   166  
   167  	// call the deployed contract and make sure it returns the correct result
   168  	doInput := c.PackDo(big.NewInt(1))
   169  	instance := c.Instance(bindBackend, res.Addresses[nested_libraries.C1MetaData.ID])
   170  	callOpts := new(bind.CallOpts)
   171  	internalCallCount, err := bind.Call(instance, callOpts, doInput, c.UnpackDo)
   172  	if err != nil {
   173  		t.Fatalf("error calling contract: %v", err)
   174  	}
   175  	if internalCallCount.Uint64() != 6 {
   176  		t.Fatalf("expected internal call count of 6.  got %d.", internalCallCount.Uint64())
   177  	}
   178  }
   179  
   180  // returns transaction auth to send a basic transaction from testAddr
   181  func defaultTxAuth() *bind.TransactOpts {
   182  	signer := types.LatestSigner(params.AllDevChainProtocolChanges)
   183  	opts := &bind.TransactOpts{
   184  		From:  testAddr,
   185  		Nonce: nil,
   186  		Signer: func(address common.Address, tx *types.Transaction) (*types.Transaction, error) {
   187  			signature, err := crypto.Sign(signer.Hash(tx).Bytes(), testKey)
   188  			if err != nil {
   189  				return nil, err
   190  			}
   191  			signedTx, err := tx.WithSignature(signer, signature)
   192  			if err != nil {
   193  				return nil, err
   194  			}
   195  			return signedTx, nil
   196  		},
   197  		Context: context.Background(),
   198  	}
   199  	return opts
   200  }
   201  
   202  func TestEvents(t *testing.T) {
   203  	// test watch/filter logs method on a contract that emits various kinds of events (struct-containing, etc.)
   204  	backend, err := testSetup()
   205  	if err != nil {
   206  		t.Fatalf("error setting up testing env: %v", err)
   207  	}
   208  	deploymentParams := &bind.DeploymentParams{
   209  		Contracts: []*bind.MetaData{&events.CMetaData},
   210  	}
   211  	res, err := bind.LinkAndDeploy(deploymentParams, makeTestDeployer(backend))
   212  	if err != nil {
   213  		t.Fatalf("error deploying contract for testing: %v", err)
   214  	}
   215  
   216  	backend.Commit()
   217  	if _, err := bind.WaitDeployed(context.Background(), backend, res.Txs[events.CMetaData.ID].Hash()); err != nil {
   218  		t.Fatalf("WaitDeployed failed %v", err)
   219  	}
   220  
   221  	c := events.NewC()
   222  	instance := c.Instance(backend, res.Addresses[events.CMetaData.ID])
   223  
   224  	newCBasic1Ch := make(chan *events.CBasic1)
   225  	newCBasic2Ch := make(chan *events.CBasic2)
   226  	watchOpts := &bind.WatchOpts{}
   227  	sub1, err := bind.WatchEvents(instance, watchOpts, c.UnpackBasic1Event, newCBasic1Ch)
   228  	if err != nil {
   229  		t.Fatalf("WatchEvents returned error: %v", err)
   230  	}
   231  	sub2, err := bind.WatchEvents(instance, watchOpts, c.UnpackBasic2Event, newCBasic2Ch)
   232  	if err != nil {
   233  		t.Fatalf("WatchEvents returned error: %v", err)
   234  	}
   235  	defer sub1.Unsubscribe()
   236  	defer sub2.Unsubscribe()
   237  
   238  	packedInput := c.PackEmitMulti()
   239  	tx, err := bind.Transact(instance, defaultTxAuth(), packedInput)
   240  	if err != nil {
   241  		t.Fatalf("failed to send transaction: %v", err)
   242  	}
   243  	backend.Commit()
   244  	if _, err := bind.WaitMined(context.Background(), backend, tx.Hash()); err != nil {
   245  		t.Fatalf("error waiting for tx to be mined: %v", err)
   246  	}
   247  
   248  	timeout := time.NewTimer(2 * time.Second)
   249  	e1Count := 0
   250  	e2Count := 0
   251  	for {
   252  		select {
   253  		case <-newCBasic1Ch:
   254  			e1Count++
   255  		case <-newCBasic2Ch:
   256  			e2Count++
   257  		case <-timeout.C:
   258  			goto done
   259  		}
   260  		if e1Count == 2 && e2Count == 1 {
   261  			break
   262  		}
   263  	}
   264  done:
   265  	if e1Count != 2 {
   266  		t.Fatalf("expected event type 1 count to be 2.  got %d", e1Count)
   267  	}
   268  	if e2Count != 1 {
   269  		t.Fatalf("expected event type 2 count to be 1.  got %d", e2Count)
   270  	}
   271  
   272  	// now, test that we can filter those same logs after they were included in the chain
   273  
   274  	filterOpts := &bind.FilterOpts{
   275  		Start:   0,
   276  		Context: context.Background(),
   277  	}
   278  	it, err := bind.FilterEvents(instance, filterOpts, c.UnpackBasic1Event)
   279  	if err != nil {
   280  		t.Fatalf("error filtering logs %v\n", err)
   281  	}
   282  	it2, err := bind.FilterEvents(instance, filterOpts, c.UnpackBasic2Event)
   283  	if err != nil {
   284  		t.Fatalf("error filtering logs %v\n", err)
   285  	}
   286  
   287  	e1Count = 0
   288  	e2Count = 0
   289  	for it.Next() {
   290  		if err := it.Error(); err != nil {
   291  			t.Fatalf("got error while iterating events for e1: %v", err)
   292  		}
   293  		e1Count++
   294  	}
   295  	for it2.Next() {
   296  		if err := it2.Error(); err != nil {
   297  			t.Fatalf("got error while iterating events for e2: %v", err)
   298  		}
   299  		e2Count++
   300  	}
   301  	if e1Count != 2 {
   302  		t.Fatalf("expected e1Count of 2 from filter call.  got %d", e1Count)
   303  	}
   304  	if e2Count != 1 {
   305  		t.Fatalf("expected e2Count of 1 from filter call.  got %d", e1Count)
   306  	}
   307  }
   308  
   309  func TestErrors(t *testing.T) {
   310  	// test watch/filter logs method on a contract that emits various kinds of events (struct-containing, etc.)
   311  	backend, err := testSetup()
   312  	if err != nil {
   313  		t.Fatalf("error setting up testing env: %v", err)
   314  	}
   315  	deploymentParams := &bind.DeploymentParams{
   316  		Contracts: []*bind.MetaData{&solc_errors.CMetaData},
   317  	}
   318  	res, err := bind.LinkAndDeploy(deploymentParams, makeTestDeployer(backend))
   319  	if err != nil {
   320  		t.Fatalf("error deploying contract for testing: %v", err)
   321  	}
   322  
   323  	backend.Commit()
   324  	if _, err := bind.WaitDeployed(context.Background(), backend, res.Txs[solc_errors.CMetaData.ID].Hash()); err != nil {
   325  		t.Fatalf("WaitDeployed failed %v", err)
   326  	}
   327  
   328  	c := solc_errors.NewC()
   329  	instance := c.Instance(backend, res.Addresses[solc_errors.CMetaData.ID])
   330  	packedInput := c.PackFoo()
   331  	opts := &bind.CallOpts{From: res.Addresses[solc_errors.CMetaData.ID]}
   332  	_, err = bind.Call[struct{}](instance, opts, packedInput, nil)
   333  	if err == nil {
   334  		t.Fatalf("expected call to fail")
   335  	}
   336  	raw, hasRevertErrorData := ethclient.RevertErrorData(err)
   337  	if !hasRevertErrorData {
   338  		t.Fatalf("expected call error to contain revert error data.")
   339  	}
   340  	rawUnpackedErr, err := c.UnpackError(raw)
   341  	if err != nil {
   342  		t.Fatalf("expected to unpack error")
   343  	}
   344  
   345  	unpackedErr, ok := rawUnpackedErr.(*solc_errors.CBadThing)
   346  	if !ok {
   347  		t.Fatalf("unexpected type for error")
   348  	}
   349  	if unpackedErr.Arg1.Cmp(big.NewInt(0)) != 0 {
   350  		t.Fatalf("bad unpacked error result: expected Arg1 field to be 0.  got %s", unpackedErr.Arg1.String())
   351  	}
   352  	if unpackedErr.Arg2.Cmp(big.NewInt(1)) != 0 {
   353  		t.Fatalf("bad unpacked error result: expected Arg2 field to be 1.  got %s", unpackedErr.Arg2.String())
   354  	}
   355  	if unpackedErr.Arg3.Cmp(big.NewInt(2)) != 0 {
   356  		t.Fatalf("bad unpacked error result: expected Arg3 to be 2.  got %s", unpackedErr.Arg3.String())
   357  	}
   358  	if unpackedErr.Arg4 != false {
   359  		t.Fatalf("bad unpacked error result: expected Arg4 to be false.  got true")
   360  	}
   361  }