github.com/klaytn/klaytn@v1.12.1/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  
    79  	blockNumber := big.NewInt(42)
    80  
    81  	bc.Call(&bind.CallOpts{BlockNumber: blockNumber}, nil, "something")
    82  
    83  	if mc.callContractBlockNumber != blockNumber {
    84  		t.Fatalf("CallContract() was not passed the block number")
    85  	}
    86  
    87  	if mc.codeAtBlockNumber != blockNumber {
    88  		t.Fatalf("CodeAt() was not passed the block number")
    89  	}
    90  
    91  	bc.Call(&bind.CallOpts{}, nil, "something")
    92  
    93  	if mc.callContractBlockNumber != nil {
    94  		t.Fatalf("CallContract() was passed a block number when it should not have been")
    95  	}
    96  
    97  	if mc.codeAtBlockNumber != nil {
    98  		t.Fatalf("CodeAt() was passed a block number when it should not have been")
    99  	}
   100  
   101  	bc.Call(&bind.CallOpts{BlockNumber: blockNumber, Pending: true}, nil, "something")
   102  
   103  	if !mc.pendingCallContractCalled {
   104  		t.Fatalf("CallContract() was not passed the block number")
   105  	}
   106  
   107  	if !mc.pendingCodeAtCalled {
   108  		t.Fatalf("CodeAt() was not passed the block number")
   109  	}
   110  }
   111  
   112  const hexData = "0x000000000000000000000000376c47978271565f56deb45495afa69e59c16ab200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000158"
   113  
   114  func TestUnpackIndexedStringTyLogIntoMap(t *testing.T) {
   115  	hash := crypto.Keccak256Hash([]byte("testName"))
   116  	topics := []common.Hash{
   117  		common.HexToHash("0x0"),
   118  		hash,
   119  	}
   120  	mockLog := newMockLog(topics, common.HexToHash("0x0"))
   121  
   122  	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"}]`
   123  	parsedAbi, _ := abi.JSON(strings.NewReader(abiString))
   124  	bc := bind.NewBoundContract(common.HexToAddress("0x0"), parsedAbi, nil, nil, nil)
   125  
   126  	expectedReceivedMap := map[string]interface{}{
   127  		"name":   hash,
   128  		"sender": common.HexToAddress("0x376c47978271565f56DEB45495afa69E59c16Ab2"),
   129  		"amount": big.NewInt(1),
   130  		"memo":   []byte{88},
   131  	}
   132  	unpackAndCheck(t, bc, expectedReceivedMap, mockLog)
   133  }
   134  
   135  func TestUnpackIndexedSliceTyLogIntoMap(t *testing.T) {
   136  	sliceBytes, err := rlp.EncodeToBytes([]string{"name1", "name2", "name3", "name4"})
   137  	if err != nil {
   138  		t.Fatal(err)
   139  	}
   140  	hash := crypto.Keccak256Hash(sliceBytes)
   141  	topics := []common.Hash{
   142  		common.HexToHash("0x0"),
   143  		hash,
   144  	}
   145  	mockLog := newMockLog(topics, common.HexToHash("0x0"))
   146  
   147  	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"}]`
   148  	parsedAbi, _ := abi.JSON(strings.NewReader(abiString))
   149  	bc := bind.NewBoundContract(common.HexToAddress("0x0"), parsedAbi, nil, nil, nil)
   150  
   151  	expectedReceivedMap := map[string]interface{}{
   152  		"names":  hash,
   153  		"sender": common.HexToAddress("0x376c47978271565f56DEB45495afa69E59c16Ab2"),
   154  		"amount": big.NewInt(1),
   155  		"memo":   []byte{88},
   156  	}
   157  	unpackAndCheck(t, bc, expectedReceivedMap, mockLog)
   158  }
   159  
   160  func TestUnpackIndexedArrayTyLogIntoMap(t *testing.T) {
   161  	arrBytes, err := rlp.EncodeToBytes([2]common.Address{common.HexToAddress("0x0"), common.HexToAddress("0x376c47978271565f56DEB45495afa69E59c16Ab2")})
   162  	if err != nil {
   163  		t.Fatal(err)
   164  	}
   165  	hash := crypto.Keccak256Hash(arrBytes)
   166  	topics := []common.Hash{
   167  		common.HexToHash("0x0"),
   168  		hash,
   169  	}
   170  	mockLog := newMockLog(topics, common.HexToHash("0x0"))
   171  
   172  	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"}]`
   173  	parsedAbi, _ := abi.JSON(strings.NewReader(abiString))
   174  	bc := bind.NewBoundContract(common.HexToAddress("0x0"), parsedAbi, nil, nil, nil)
   175  
   176  	expectedReceivedMap := map[string]interface{}{
   177  		"addresses": hash,
   178  		"sender":    common.HexToAddress("0x376c47978271565f56DEB45495afa69E59c16Ab2"),
   179  		"amount":    big.NewInt(1),
   180  		"memo":      []byte{88},
   181  	}
   182  	unpackAndCheck(t, bc, expectedReceivedMap, mockLog)
   183  }
   184  
   185  func TestUnpackIndexedFuncTyLogIntoMap(t *testing.T) {
   186  	mockAddress := common.HexToAddress("0x376c47978271565f56DEB45495afa69E59c16Ab2")
   187  	addrBytes := mockAddress.Bytes()
   188  	hash := crypto.Keccak256Hash([]byte("mockFunction(address,uint)"))
   189  	functionSelector := hash[:4]
   190  	functionTyBytes := append(addrBytes, functionSelector...)
   191  	var functionTy [24]byte
   192  	copy(functionTy[:], functionTyBytes[0:24])
   193  	topics := []common.Hash{
   194  		common.HexToHash("0x99b5620489b6ef926d4518936cfec15d305452712b88bd59da2d9c10fb0953e8"),
   195  		common.BytesToHash(functionTyBytes),
   196  	}
   197  	mockLog := newMockLog(topics, common.HexToHash("0x5c698f13940a2153440c6d19660878bc90219d9298fdcf37365aa8d88d40fc42"))
   198  	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"}]`
   199  	parsedAbi, _ := abi.JSON(strings.NewReader(abiString))
   200  	bc := bind.NewBoundContract(common.HexToAddress("0x0"), parsedAbi, nil, nil, nil)
   201  
   202  	expectedReceivedMap := map[string]interface{}{
   203  		"function": functionTy,
   204  		"sender":   common.HexToAddress("0x376c47978271565f56DEB45495afa69E59c16Ab2"),
   205  		"amount":   big.NewInt(1),
   206  		"memo":     []byte{88},
   207  	}
   208  	unpackAndCheck(t, bc, expectedReceivedMap, mockLog)
   209  }
   210  
   211  func TestUnpackIndexedBytesTyLogIntoMap(t *testing.T) {
   212  	bytes := []byte{1, 2, 3, 4, 5}
   213  	hash := crypto.Keccak256Hash(bytes)
   214  	topics := []common.Hash{
   215  		common.HexToHash("0x99b5620489b6ef926d4518936cfec15d305452712b88bd59da2d9c10fb0953e8"),
   216  		hash,
   217  	}
   218  	mockLog := newMockLog(topics, common.HexToHash("0x5c698f13940a2153440c6d19660878bc90219d9298fdcf37365aa8d88d40fc42"))
   219  
   220  	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"}]`
   221  	parsedAbi, _ := abi.JSON(strings.NewReader(abiString))
   222  	bc := bind.NewBoundContract(common.HexToAddress("0x0"), parsedAbi, nil, nil, nil)
   223  
   224  	expectedReceivedMap := map[string]interface{}{
   225  		"content": hash,
   226  		"sender":  common.HexToAddress("0x376c47978271565f56DEB45495afa69E59c16Ab2"),
   227  		"amount":  big.NewInt(1),
   228  		"memo":    []byte{88},
   229  	}
   230  	unpackAndCheck(t, bc, expectedReceivedMap, mockLog)
   231  }
   232  
   233  func unpackAndCheck(t *testing.T, bc *bind.BoundContract, expected map[string]interface{}, mockLog types.Log) {
   234  	received := make(map[string]interface{})
   235  	if err := bc.UnpackLogIntoMap(received, "received", mockLog); err != nil {
   236  		t.Error(err)
   237  	}
   238  
   239  	if len(received) != len(expected) {
   240  		t.Fatalf("unpacked map length %v not equal expected length of %v", len(received), len(expected))
   241  	}
   242  	for name, elem := range expected {
   243  		if !reflect.DeepEqual(elem, received[name]) {
   244  			t.Errorf("field %v does not match expected, want %v, got %v", name, elem, received[name])
   245  		}
   246  	}
   247  }
   248  
   249  func newMockLog(topics []common.Hash, txHash common.Hash) types.Log {
   250  	return types.Log{
   251  		Address:     common.HexToAddress("0x0"),
   252  		Topics:      topics,
   253  		Data:        hexutil.MustDecode(hexData),
   254  		BlockNumber: uint64(26),
   255  		TxHash:      txHash,
   256  		TxIndex:     111,
   257  		BlockHash:   common.BytesToHash([]byte{1, 2, 3, 4, 5}),
   258  		Index:       7,
   259  		Removed:     false,
   260  	}
   261  }