github.com/Cleverse/go-ethereum@v0.0.0-20220927095127-45113064e7f2/accounts/abi/bind/base_test.go (about) 1 // Copyright 2019 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 "errors" 22 "math/big" 23 "reflect" 24 "strings" 25 "testing" 26 27 "github.com/ethereum/go-ethereum" 28 "github.com/ethereum/go-ethereum/accounts/abi" 29 "github.com/ethereum/go-ethereum/accounts/abi/bind" 30 "github.com/ethereum/go-ethereum/common" 31 "github.com/ethereum/go-ethereum/common/hexutil" 32 "github.com/ethereum/go-ethereum/core/types" 33 "github.com/ethereum/go-ethereum/crypto" 34 "github.com/ethereum/go-ethereum/rlp" 35 "github.com/stretchr/testify/assert" 36 ) 37 38 func mockSign(addr common.Address, tx *types.Transaction) (*types.Transaction, error) { return tx, nil } 39 40 type mockTransactor struct { 41 baseFee *big.Int 42 gasTipCap *big.Int 43 gasPrice *big.Int 44 suggestGasTipCapCalled bool 45 suggestGasPriceCalled bool 46 } 47 48 func (mt *mockTransactor) HeaderByNumber(ctx context.Context, number *big.Int) (*types.Header, error) { 49 return &types.Header{BaseFee: mt.baseFee}, nil 50 } 51 52 func (mt *mockTransactor) PendingCodeAt(ctx context.Context, account common.Address) ([]byte, error) { 53 return []byte{1}, nil 54 } 55 56 func (mt *mockTransactor) PendingNonceAt(ctx context.Context, account common.Address) (uint64, error) { 57 return 0, nil 58 } 59 60 func (mt *mockTransactor) SuggestGasPrice(ctx context.Context) (*big.Int, error) { 61 mt.suggestGasPriceCalled = true 62 return mt.gasPrice, nil 63 } 64 65 func (mt *mockTransactor) SuggestGasTipCap(ctx context.Context) (*big.Int, error) { 66 mt.suggestGasTipCapCalled = true 67 return mt.gasTipCap, nil 68 } 69 70 func (mt *mockTransactor) EstimateGas(ctx context.Context, call ethereum.CallMsg) (gas uint64, err error) { 71 return 0, nil 72 } 73 74 func (mt *mockTransactor) SendTransaction(ctx context.Context, tx *types.Transaction) error { 75 return nil 76 } 77 78 type mockCaller struct { 79 codeAtBlockNumber *big.Int 80 callContractBlockNumber *big.Int 81 callContractBytes []byte 82 callContractErr error 83 codeAtBytes []byte 84 codeAtErr error 85 } 86 87 func (mc *mockCaller) CodeAt(ctx context.Context, contract common.Address, blockNumber *big.Int) ([]byte, error) { 88 mc.codeAtBlockNumber = blockNumber 89 return mc.codeAtBytes, mc.codeAtErr 90 } 91 92 func (mc *mockCaller) CallContract(ctx context.Context, call ethereum.CallMsg, blockNumber *big.Int) ([]byte, error) { 93 mc.callContractBlockNumber = blockNumber 94 return mc.callContractBytes, mc.callContractErr 95 } 96 97 type mockPendingCaller struct { 98 *mockCaller 99 pendingCodeAtBytes []byte 100 pendingCodeAtErr error 101 pendingCodeAtCalled bool 102 pendingCallContractCalled bool 103 pendingCallContractBytes []byte 104 pendingCallContractErr error 105 } 106 107 func (mc *mockPendingCaller) PendingCodeAt(ctx context.Context, contract common.Address) ([]byte, error) { 108 mc.pendingCodeAtCalled = true 109 return mc.pendingCodeAtBytes, mc.pendingCodeAtErr 110 } 111 112 func (mc *mockPendingCaller) PendingCallContract(ctx context.Context, call ethereum.CallMsg) ([]byte, error) { 113 mc.pendingCallContractCalled = true 114 return mc.pendingCallContractBytes, mc.pendingCallContractErr 115 } 116 117 func TestPassingBlockNumber(t *testing.T) { 118 mc := &mockPendingCaller{ 119 mockCaller: &mockCaller{ 120 codeAtBytes: []byte{1, 2, 3}, 121 }, 122 } 123 124 bc := bind.NewBoundContract(common.HexToAddress("0x0"), abi.ABI{ 125 Methods: map[string]abi.Method{ 126 "something": { 127 Name: "something", 128 Outputs: abi.Arguments{}, 129 }, 130 }, 131 }, mc, nil, nil) 132 133 blockNumber := big.NewInt(42) 134 135 bc.Call(&bind.CallOpts{BlockNumber: blockNumber}, nil, "something") 136 137 if mc.callContractBlockNumber != blockNumber { 138 t.Fatalf("CallContract() was not passed the block number") 139 } 140 141 if mc.codeAtBlockNumber != blockNumber { 142 t.Fatalf("CodeAt() was not passed the block number") 143 } 144 145 bc.Call(&bind.CallOpts{}, nil, "something") 146 147 if mc.callContractBlockNumber != nil { 148 t.Fatalf("CallContract() was passed a block number when it should not have been") 149 } 150 151 if mc.codeAtBlockNumber != nil { 152 t.Fatalf("CodeAt() was passed a block number when it should not have been") 153 } 154 155 bc.Call(&bind.CallOpts{BlockNumber: blockNumber, Pending: true}, nil, "something") 156 157 if !mc.pendingCallContractCalled { 158 t.Fatalf("CallContract() was not passed the block number") 159 } 160 161 if !mc.pendingCodeAtCalled { 162 t.Fatalf("CodeAt() was not passed the block number") 163 } 164 } 165 166 const hexData = "0x000000000000000000000000376c47978271565f56deb45495afa69e59c16ab200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000158" 167 168 func TestUnpackIndexedStringTyLogIntoMap(t *testing.T) { 169 hash := crypto.Keccak256Hash([]byte("testName")) 170 topics := []common.Hash{ 171 crypto.Keccak256Hash([]byte("received(string,address,uint256,bytes)")), 172 hash, 173 } 174 mockLog := newMockLog(topics, common.HexToHash("0x0")) 175 176 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"}]` 177 parsedAbi, _ := abi.JSON(strings.NewReader(abiString)) 178 bc := bind.NewBoundContract(common.HexToAddress("0x0"), parsedAbi, nil, nil, nil) 179 180 expectedReceivedMap := map[string]interface{}{ 181 "name": hash, 182 "sender": common.HexToAddress("0x376c47978271565f56DEB45495afa69E59c16Ab2"), 183 "amount": big.NewInt(1), 184 "memo": []byte{88}, 185 } 186 unpackAndCheck(t, bc, expectedReceivedMap, mockLog) 187 } 188 189 func TestUnpackIndexedSliceTyLogIntoMap(t *testing.T) { 190 sliceBytes, err := rlp.EncodeToBytes([]string{"name1", "name2", "name3", "name4"}) 191 if err != nil { 192 t.Fatal(err) 193 } 194 hash := crypto.Keccak256Hash(sliceBytes) 195 topics := []common.Hash{ 196 crypto.Keccak256Hash([]byte("received(string[],address,uint256,bytes)")), 197 hash, 198 } 199 mockLog := newMockLog(topics, common.HexToHash("0x0")) 200 201 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"}]` 202 parsedAbi, _ := abi.JSON(strings.NewReader(abiString)) 203 bc := bind.NewBoundContract(common.HexToAddress("0x0"), parsedAbi, nil, nil, nil) 204 205 expectedReceivedMap := map[string]interface{}{ 206 "names": hash, 207 "sender": common.HexToAddress("0x376c47978271565f56DEB45495afa69E59c16Ab2"), 208 "amount": big.NewInt(1), 209 "memo": []byte{88}, 210 } 211 unpackAndCheck(t, bc, expectedReceivedMap, mockLog) 212 } 213 214 func TestUnpackIndexedArrayTyLogIntoMap(t *testing.T) { 215 arrBytes, err := rlp.EncodeToBytes([2]common.Address{common.HexToAddress("0x0"), common.HexToAddress("0x376c47978271565f56DEB45495afa69E59c16Ab2")}) 216 if err != nil { 217 t.Fatal(err) 218 } 219 hash := crypto.Keccak256Hash(arrBytes) 220 topics := []common.Hash{ 221 crypto.Keccak256Hash([]byte("received(address[2],address,uint256,bytes)")), 222 hash, 223 } 224 mockLog := newMockLog(topics, common.HexToHash("0x0")) 225 226 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"}]` 227 parsedAbi, _ := abi.JSON(strings.NewReader(abiString)) 228 bc := bind.NewBoundContract(common.HexToAddress("0x0"), parsedAbi, nil, nil, nil) 229 230 expectedReceivedMap := map[string]interface{}{ 231 "addresses": hash, 232 "sender": common.HexToAddress("0x376c47978271565f56DEB45495afa69E59c16Ab2"), 233 "amount": big.NewInt(1), 234 "memo": []byte{88}, 235 } 236 unpackAndCheck(t, bc, expectedReceivedMap, mockLog) 237 } 238 239 func TestUnpackIndexedFuncTyLogIntoMap(t *testing.T) { 240 mockAddress := common.HexToAddress("0x376c47978271565f56DEB45495afa69E59c16Ab2") 241 addrBytes := mockAddress.Bytes() 242 hash := crypto.Keccak256Hash([]byte("mockFunction(address,uint)")) 243 functionSelector := hash[:4] 244 functionTyBytes := append(addrBytes, functionSelector...) 245 var functionTy [24]byte 246 copy(functionTy[:], functionTyBytes[0:24]) 247 topics := []common.Hash{ 248 crypto.Keccak256Hash([]byte("received(function,address,uint256,bytes)")), 249 common.BytesToHash(functionTyBytes), 250 } 251 mockLog := newMockLog(topics, common.HexToHash("0x5c698f13940a2153440c6d19660878bc90219d9298fdcf37365aa8d88d40fc42")) 252 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"}]` 253 parsedAbi, _ := abi.JSON(strings.NewReader(abiString)) 254 bc := bind.NewBoundContract(common.HexToAddress("0x0"), parsedAbi, nil, nil, nil) 255 256 expectedReceivedMap := map[string]interface{}{ 257 "function": functionTy, 258 "sender": common.HexToAddress("0x376c47978271565f56DEB45495afa69E59c16Ab2"), 259 "amount": big.NewInt(1), 260 "memo": []byte{88}, 261 } 262 unpackAndCheck(t, bc, expectedReceivedMap, mockLog) 263 } 264 265 func TestUnpackIndexedBytesTyLogIntoMap(t *testing.T) { 266 bytes := []byte{1, 2, 3, 4, 5} 267 hash := crypto.Keccak256Hash(bytes) 268 topics := []common.Hash{ 269 crypto.Keccak256Hash([]byte("received(bytes,address,uint256,bytes)")), 270 hash, 271 } 272 mockLog := newMockLog(topics, common.HexToHash("0x5c698f13940a2153440c6d19660878bc90219d9298fdcf37365aa8d88d40fc42")) 273 274 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"}]` 275 parsedAbi, _ := abi.JSON(strings.NewReader(abiString)) 276 bc := bind.NewBoundContract(common.HexToAddress("0x0"), parsedAbi, nil, nil, nil) 277 278 expectedReceivedMap := map[string]interface{}{ 279 "content": hash, 280 "sender": common.HexToAddress("0x376c47978271565f56DEB45495afa69E59c16Ab2"), 281 "amount": big.NewInt(1), 282 "memo": []byte{88}, 283 } 284 unpackAndCheck(t, bc, expectedReceivedMap, mockLog) 285 } 286 287 func TestTransactGasFee(t *testing.T) { 288 assert := assert.New(t) 289 290 // GasTipCap and GasFeeCap 291 // When opts.GasTipCap and opts.GasFeeCap are nil 292 mt := &mockTransactor{baseFee: big.NewInt(100), gasTipCap: big.NewInt(5)} 293 bc := bind.NewBoundContract(common.Address{}, abi.ABI{}, nil, mt, nil) 294 opts := &bind.TransactOpts{Signer: mockSign} 295 tx, err := bc.Transact(opts, "") 296 assert.Nil(err) 297 assert.Equal(big.NewInt(5), tx.GasTipCap()) 298 assert.Equal(big.NewInt(205), tx.GasFeeCap()) 299 assert.Nil(opts.GasTipCap) 300 assert.Nil(opts.GasFeeCap) 301 assert.True(mt.suggestGasTipCapCalled) 302 303 // Second call to Transact should use latest suggested GasTipCap 304 mt.gasTipCap = big.NewInt(6) 305 mt.suggestGasTipCapCalled = false 306 tx, err = bc.Transact(opts, "") 307 assert.Nil(err) 308 assert.Equal(big.NewInt(6), tx.GasTipCap()) 309 assert.Equal(big.NewInt(206), tx.GasFeeCap()) 310 assert.True(mt.suggestGasTipCapCalled) 311 312 // GasPrice 313 // When opts.GasPrice is nil 314 mt = &mockTransactor{gasPrice: big.NewInt(5)} 315 bc = bind.NewBoundContract(common.Address{}, abi.ABI{}, nil, mt, nil) 316 opts = &bind.TransactOpts{Signer: mockSign} 317 tx, err = bc.Transact(opts, "") 318 assert.Nil(err) 319 assert.Equal(big.NewInt(5), tx.GasPrice()) 320 assert.Nil(opts.GasPrice) 321 assert.True(mt.suggestGasPriceCalled) 322 323 // Second call to Transact should use latest suggested GasPrice 324 mt.gasPrice = big.NewInt(6) 325 mt.suggestGasPriceCalled = false 326 tx, err = bc.Transact(opts, "") 327 assert.Nil(err) 328 assert.Equal(big.NewInt(6), tx.GasPrice()) 329 assert.True(mt.suggestGasPriceCalled) 330 } 331 332 func unpackAndCheck(t *testing.T, bc *bind.BoundContract, expected map[string]interface{}, mockLog types.Log) { 333 received := make(map[string]interface{}) 334 if err := bc.UnpackLogIntoMap(received, "received", mockLog); err != nil { 335 t.Error(err) 336 } 337 338 if len(received) != len(expected) { 339 t.Fatalf("unpacked map length %v not equal expected length of %v", len(received), len(expected)) 340 } 341 for name, elem := range expected { 342 if !reflect.DeepEqual(elem, received[name]) { 343 t.Errorf("field %v does not match expected, want %v, got %v", name, elem, received[name]) 344 } 345 } 346 } 347 348 func newMockLog(topics []common.Hash, txHash common.Hash) types.Log { 349 return types.Log{ 350 Address: common.HexToAddress("0x0"), 351 Topics: topics, 352 Data: hexutil.MustDecode(hexData), 353 BlockNumber: uint64(26), 354 TxHash: txHash, 355 TxIndex: 111, 356 BlockHash: common.BytesToHash([]byte{1, 2, 3, 4, 5}), 357 Index: 7, 358 Removed: false, 359 } 360 } 361 362 func TestCall(t *testing.T) { 363 var method, methodWithArg = "something", "somethingArrrrg" 364 tests := []struct { 365 name, method string 366 opts *bind.CallOpts 367 mc bind.ContractCaller 368 results *[]interface{} 369 wantErr bool 370 wantErrExact error 371 }{{ 372 name: "ok not pending", 373 mc: &mockCaller{ 374 codeAtBytes: []byte{0}, 375 }, 376 method: method, 377 }, { 378 name: "ok pending", 379 mc: &mockPendingCaller{ 380 pendingCodeAtBytes: []byte{0}, 381 }, 382 opts: &bind.CallOpts{ 383 Pending: true, 384 }, 385 method: method, 386 }, { 387 name: "pack error, no method", 388 mc: new(mockCaller), 389 method: "else", 390 wantErr: true, 391 }, { 392 name: "interface error, pending but not a PendingContractCaller", 393 mc: new(mockCaller), 394 opts: &bind.CallOpts{ 395 Pending: true, 396 }, 397 method: method, 398 wantErrExact: bind.ErrNoPendingState, 399 }, { 400 name: "pending call canceled", 401 mc: &mockPendingCaller{ 402 pendingCallContractErr: context.DeadlineExceeded, 403 }, 404 opts: &bind.CallOpts{ 405 Pending: true, 406 }, 407 method: method, 408 wantErrExact: context.DeadlineExceeded, 409 }, { 410 name: "pending code at error", 411 mc: &mockPendingCaller{ 412 pendingCodeAtErr: errors.New(""), 413 }, 414 opts: &bind.CallOpts{ 415 Pending: true, 416 }, 417 method: method, 418 wantErr: true, 419 }, { 420 name: "no pending code at", 421 mc: new(mockPendingCaller), 422 opts: &bind.CallOpts{ 423 Pending: true, 424 }, 425 method: method, 426 wantErrExact: bind.ErrNoCode, 427 }, { 428 name: "call contract error", 429 mc: &mockCaller{ 430 callContractErr: context.DeadlineExceeded, 431 }, 432 method: method, 433 wantErrExact: context.DeadlineExceeded, 434 }, { 435 name: "code at error", 436 mc: &mockCaller{ 437 codeAtErr: errors.New(""), 438 }, 439 method: method, 440 wantErr: true, 441 }, { 442 name: "no code at", 443 mc: new(mockCaller), 444 method: method, 445 wantErrExact: bind.ErrNoCode, 446 }, { 447 name: "unpack error missing arg", 448 mc: &mockCaller{ 449 codeAtBytes: []byte{0}, 450 }, 451 method: methodWithArg, 452 wantErr: true, 453 }, { 454 name: "interface unpack error", 455 mc: &mockCaller{ 456 codeAtBytes: []byte{0}, 457 }, 458 method: method, 459 results: &[]interface{}{0}, 460 wantErr: true, 461 }} 462 for _, test := range tests { 463 bc := bind.NewBoundContract(common.HexToAddress("0x0"), abi.ABI{ 464 Methods: map[string]abi.Method{ 465 method: { 466 Name: method, 467 Outputs: abi.Arguments{}, 468 }, 469 methodWithArg: { 470 Name: methodWithArg, 471 Outputs: abi.Arguments{abi.Argument{}}, 472 }, 473 }, 474 }, test.mc, nil, nil) 475 err := bc.Call(test.opts, test.results, test.method) 476 if test.wantErr || test.wantErrExact != nil { 477 if err == nil { 478 t.Fatalf("%q expected error", test.name) 479 } 480 if test.wantErrExact != nil && !errors.Is(err, test.wantErrExact) { 481 t.Fatalf("%q expected error %q but got %q", test.name, test.wantErrExact, err) 482 } 483 continue 484 } 485 if err != nil { 486 t.Fatalf("%q unexpected error: %v", test.name, err) 487 } 488 } 489 } 490 491 // TestCrashers contains some strings which previously caused the abi codec to crash. 492 func TestCrashers(t *testing.T) { 493 abi.JSON(strings.NewReader(`[{"inputs":[{"type":"tuple[]","components":[{"type":"bool","name":"_1"}]}]}]`)) 494 abi.JSON(strings.NewReader(`[{"inputs":[{"type":"tuple[]","components":[{"type":"bool","name":"&"}]}]}]`)) 495 abi.JSON(strings.NewReader(`[{"inputs":[{"type":"tuple[]","components":[{"type":"bool","name":"----"}]}]}]`)) 496 abi.JSON(strings.NewReader(`[{"inputs":[{"type":"tuple[]","components":[{"type":"bool","name":"foo.Bar"}]}]}]`)) 497 }