github.com/klaytn/klaytn@v1.10.2/accounts/abi/bind/base_test.go (about)

     1  // Modifications Copyright 2019 The klaytn Authors
     2  // Copyright 2019 The go-ethereum Authors
     3  // This file is part of the go-ethereum library.
     4  //
     5  // The go-ethereum library is free software: you can redistribute it and/or modify
     6  // it under the terms of the GNU Lesser General Public License as published by
     7  // the Free Software Foundation, either version 3 of the License, or
     8  // (at your option) any later version.
     9  //
    10  // The go-ethereum library is distributed in the hope that it will be useful,
    11  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    12  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    13  // GNU Lesser General Public License for more details.
    14  //
    15  // You should have received a copy of the GNU Lesser General Public License
    16  // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
    17  //
    18  // This file is derived from accounts/abi/bind/base_test.go (2019/07/22).
    19  // Modified and improved for the klaytn development.
    20  
    21  package bind_test
    22  
    23  import (
    24  	"context"
    25  	"math/big"
    26  	"reflect"
    27  	"strings"
    28  	"testing"
    29  
    30  	"github.com/klaytn/klaytn"
    31  	"github.com/klaytn/klaytn/accounts/abi"
    32  	"github.com/klaytn/klaytn/accounts/abi/bind"
    33  	"github.com/klaytn/klaytn/blockchain/types"
    34  	"github.com/klaytn/klaytn/common"
    35  	"github.com/klaytn/klaytn/common/hexutil"
    36  	"github.com/klaytn/klaytn/crypto"
    37  	"github.com/klaytn/klaytn/rlp"
    38  )
    39  
    40  type mockCaller struct {
    41  	codeAtBlockNumber         *big.Int
    42  	callContractBlockNumber   *big.Int
    43  	pendingCodeAtCalled       bool
    44  	pendingCallContractCalled bool
    45  }
    46  
    47  func (mc *mockCaller) CodeAt(ctx context.Context, contract common.Address, blockNumber *big.Int) ([]byte, error) {
    48  	mc.codeAtBlockNumber = blockNumber
    49  	return []byte{1, 2, 3}, nil
    50  }
    51  
    52  func (mc *mockCaller) CallContract(ctx context.Context, call klaytn.CallMsg, blockNumber *big.Int) ([]byte, error) {
    53  	mc.callContractBlockNumber = blockNumber
    54  	return nil, nil
    55  }
    56  
    57  func (mc *mockCaller) PendingCodeAt(ctx context.Context, contract common.Address) ([]byte, error) {
    58  	mc.pendingCodeAtCalled = true
    59  	return nil, nil
    60  }
    61  
    62  func (mc *mockCaller) PendingCallContract(ctx context.Context, call klaytn.CallMsg) ([]byte, error) {
    63  	mc.pendingCallContractCalled = true
    64  	return nil, nil
    65  }
    66  
    67  func TestPassingBlockNumber(t *testing.T) {
    68  	mc := &mockCaller{}
    69  
    70  	bc := bind.NewBoundContract(common.HexToAddress("0x0"), abi.ABI{
    71  		Methods: map[string]abi.Method{
    72  			"something": {
    73  				Name:    "something",
    74  				Outputs: abi.Arguments{},
    75  			},
    76  		},
    77  	}, mc, nil, nil)
    78  	var ret string
    79  
    80  	blockNumber := big.NewInt(42)
    81  
    82  	bc.Call(&bind.CallOpts{BlockNumber: blockNumber}, &ret, "something")
    83  
    84  	if mc.callContractBlockNumber != blockNumber {
    85  		t.Fatalf("CallContract() was not passed the block number")
    86  	}
    87  
    88  	if mc.codeAtBlockNumber != blockNumber {
    89  		t.Fatalf("CodeAt() was not passed the block number")
    90  	}
    91  
    92  	bc.Call(&bind.CallOpts{}, &ret, "something")
    93  
    94  	if mc.callContractBlockNumber != nil {
    95  		t.Fatalf("CallContract() was passed a block number when it should not have been")
    96  	}
    97  
    98  	if mc.codeAtBlockNumber != nil {
    99  		t.Fatalf("CodeAt() was passed a block number when it should not have been")
   100  	}
   101  
   102  	bc.Call(&bind.CallOpts{BlockNumber: blockNumber, Pending: true}, &ret, "something")
   103  
   104  	if !mc.pendingCallContractCalled {
   105  		t.Fatalf("CallContract() was not passed the block number")
   106  	}
   107  
   108  	if !mc.pendingCodeAtCalled {
   109  		t.Fatalf("CodeAt() was not passed the block number")
   110  	}
   111  }
   112  
   113  const hexData = "0x000000000000000000000000376c47978271565f56deb45495afa69e59c16ab200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000158"
   114  
   115  func TestUnpackIndexedStringTyLogIntoMap(t *testing.T) {
   116  	hash := crypto.Keccak256Hash([]byte("testName"))
   117  	topics := []common.Hash{
   118  		common.HexToHash("0x0"),
   119  		hash,
   120  	}
   121  	mockLog := newMockLog(topics, common.HexToHash("0x0"))
   122  
   123  	abiString := `[{"anonymous":false,"inputs":[{"indexed":true,"name":"name","type":"string"},{"indexed":false,"name":"sender","type":"address"},{"indexed":false,"name":"amount","type":"uint256"},{"indexed":false,"name":"memo","type":"bytes"}],"name":"received","type":"event"}]`
   124  	parsedAbi, _ := abi.JSON(strings.NewReader(abiString))
   125  	bc := bind.NewBoundContract(common.HexToAddress("0x0"), parsedAbi, nil, nil, nil)
   126  
   127  	expectedReceivedMap := map[string]interface{}{
   128  		"name":   hash,
   129  		"sender": common.HexToAddress("0x376c47978271565f56DEB45495afa69E59c16Ab2"),
   130  		"amount": big.NewInt(1),
   131  		"memo":   []byte{88},
   132  	}
   133  	unpackAndCheck(t, bc, expectedReceivedMap, mockLog)
   134  }
   135  
   136  func TestUnpackIndexedSliceTyLogIntoMap(t *testing.T) {
   137  	sliceBytes, err := rlp.EncodeToBytes([]string{"name1", "name2", "name3", "name4"})
   138  	if err != nil {
   139  		t.Fatal(err)
   140  	}
   141  	hash := crypto.Keccak256Hash(sliceBytes)
   142  	topics := []common.Hash{
   143  		common.HexToHash("0x0"),
   144  		hash,
   145  	}
   146  	mockLog := newMockLog(topics, common.HexToHash("0x0"))
   147  
   148  	abiString := `[{"anonymous":false,"inputs":[{"indexed":true,"name":"names","type":"string[]"},{"indexed":false,"name":"sender","type":"address"},{"indexed":false,"name":"amount","type":"uint256"},{"indexed":false,"name":"memo","type":"bytes"}],"name":"received","type":"event"}]`
   149  	parsedAbi, _ := abi.JSON(strings.NewReader(abiString))
   150  	bc := bind.NewBoundContract(common.HexToAddress("0x0"), parsedAbi, nil, nil, nil)
   151  
   152  	expectedReceivedMap := map[string]interface{}{
   153  		"names":  hash,
   154  		"sender": common.HexToAddress("0x376c47978271565f56DEB45495afa69E59c16Ab2"),
   155  		"amount": big.NewInt(1),
   156  		"memo":   []byte{88},
   157  	}
   158  	unpackAndCheck(t, bc, expectedReceivedMap, mockLog)
   159  }
   160  
   161  func TestUnpackIndexedArrayTyLogIntoMap(t *testing.T) {
   162  	arrBytes, err := rlp.EncodeToBytes([2]common.Address{common.HexToAddress("0x0"), common.HexToAddress("0x376c47978271565f56DEB45495afa69E59c16Ab2")})
   163  	if err != nil {
   164  		t.Fatal(err)
   165  	}
   166  	hash := crypto.Keccak256Hash(arrBytes)
   167  	topics := []common.Hash{
   168  		common.HexToHash("0x0"),
   169  		hash,
   170  	}
   171  	mockLog := newMockLog(topics, common.HexToHash("0x0"))
   172  
   173  	abiString := `[{"anonymous":false,"inputs":[{"indexed":true,"name":"addresses","type":"address[2]"},{"indexed":false,"name":"sender","type":"address"},{"indexed":false,"name":"amount","type":"uint256"},{"indexed":false,"name":"memo","type":"bytes"}],"name":"received","type":"event"}]`
   174  	parsedAbi, _ := abi.JSON(strings.NewReader(abiString))
   175  	bc := bind.NewBoundContract(common.HexToAddress("0x0"), parsedAbi, nil, nil, nil)
   176  
   177  	expectedReceivedMap := map[string]interface{}{
   178  		"addresses": hash,
   179  		"sender":    common.HexToAddress("0x376c47978271565f56DEB45495afa69E59c16Ab2"),
   180  		"amount":    big.NewInt(1),
   181  		"memo":      []byte{88},
   182  	}
   183  	unpackAndCheck(t, bc, expectedReceivedMap, mockLog)
   184  }
   185  
   186  func TestUnpackIndexedFuncTyLogIntoMap(t *testing.T) {
   187  	mockAddress := common.HexToAddress("0x376c47978271565f56DEB45495afa69E59c16Ab2")
   188  	addrBytes := mockAddress.Bytes()
   189  	hash := crypto.Keccak256Hash([]byte("mockFunction(address,uint)"))
   190  	functionSelector := hash[:4]
   191  	functionTyBytes := append(addrBytes, functionSelector...)
   192  	var functionTy [24]byte
   193  	copy(functionTy[:], functionTyBytes[0:24])
   194  	topics := []common.Hash{
   195  		common.HexToHash("0x99b5620489b6ef926d4518936cfec15d305452712b88bd59da2d9c10fb0953e8"),
   196  		common.BytesToHash(functionTyBytes),
   197  	}
   198  	mockLog := newMockLog(topics, common.HexToHash("0x5c698f13940a2153440c6d19660878bc90219d9298fdcf37365aa8d88d40fc42"))
   199  	abiString := `[{"anonymous":false,"inputs":[{"indexed":true,"name":"function","type":"function"},{"indexed":false,"name":"sender","type":"address"},{"indexed":false,"name":"amount","type":"uint256"},{"indexed":false,"name":"memo","type":"bytes"}],"name":"received","type":"event"}]`
   200  	parsedAbi, _ := abi.JSON(strings.NewReader(abiString))
   201  	bc := bind.NewBoundContract(common.HexToAddress("0x0"), parsedAbi, nil, nil, nil)
   202  
   203  	expectedReceivedMap := map[string]interface{}{
   204  		"function": functionTy,
   205  		"sender":   common.HexToAddress("0x376c47978271565f56DEB45495afa69E59c16Ab2"),
   206  		"amount":   big.NewInt(1),
   207  		"memo":     []byte{88},
   208  	}
   209  	unpackAndCheck(t, bc, expectedReceivedMap, mockLog)
   210  }
   211  
   212  func TestUnpackIndexedBytesTyLogIntoMap(t *testing.T) {
   213  	bytes := []byte{1, 2, 3, 4, 5}
   214  	hash := crypto.Keccak256Hash(bytes)
   215  	topics := []common.Hash{
   216  		common.HexToHash("0x99b5620489b6ef926d4518936cfec15d305452712b88bd59da2d9c10fb0953e8"),
   217  		hash,
   218  	}
   219  	mockLog := newMockLog(topics, common.HexToHash("0x5c698f13940a2153440c6d19660878bc90219d9298fdcf37365aa8d88d40fc42"))
   220  
   221  	abiString := `[{"anonymous":false,"inputs":[{"indexed":true,"name":"content","type":"bytes"},{"indexed":false,"name":"sender","type":"address"},{"indexed":false,"name":"amount","type":"uint256"},{"indexed":false,"name":"memo","type":"bytes"}],"name":"received","type":"event"}]`
   222  	parsedAbi, _ := abi.JSON(strings.NewReader(abiString))
   223  	bc := bind.NewBoundContract(common.HexToAddress("0x0"), parsedAbi, nil, nil, nil)
   224  
   225  	expectedReceivedMap := map[string]interface{}{
   226  		"content": hash,
   227  		"sender":  common.HexToAddress("0x376c47978271565f56DEB45495afa69E59c16Ab2"),
   228  		"amount":  big.NewInt(1),
   229  		"memo":    []byte{88},
   230  	}
   231  	unpackAndCheck(t, bc, expectedReceivedMap, mockLog)
   232  }
   233  
   234  func unpackAndCheck(t *testing.T, bc *bind.BoundContract, expected map[string]interface{}, mockLog types.Log) {
   235  	received := make(map[string]interface{})
   236  	if err := bc.UnpackLogIntoMap(received, "received", mockLog); err != nil {
   237  		t.Error(err)
   238  	}
   239  
   240  	if len(received) != len(expected) {
   241  		t.Fatalf("unpacked map length %v not equal expected length of %v", len(received), len(expected))
   242  	}
   243  	for name, elem := range expected {
   244  		if !reflect.DeepEqual(elem, received[name]) {
   245  			t.Errorf("field %v does not match expected, want %v, got %v", name, elem, received[name])
   246  		}
   247  	}
   248  }
   249  
   250  func newMockLog(topics []common.Hash, txHash common.Hash) types.Log {
   251  	return types.Log{
   252  		Address:     common.HexToAddress("0x0"),
   253  		Topics:      topics,
   254  		Data:        hexutil.MustDecode(hexData),
   255  		BlockNumber: uint64(26),
   256  		TxHash:      txHash,
   257  		TxIndex:     111,
   258  		BlockHash:   common.BytesToHash([]byte{1, 2, 3, 4, 5}),
   259  		Index:       7,
   260  		Removed:     false,
   261  	}
   262  }