github.com/theQRL/go-zond@v0.2.1/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/stretchr/testify/assert" 28 "github.com/theQRL/go-zond" 29 "github.com/theQRL/go-zond/accounts/abi" 30 "github.com/theQRL/go-zond/accounts/abi/bind" 31 "github.com/theQRL/go-zond/common" 32 "github.com/theQRL/go-zond/common/hexutil" 33 "github.com/theQRL/go-zond/core/types" 34 "github.com/theQRL/go-zond/crypto" 35 "github.com/theQRL/go-zond/rlp" 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 zond.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 zond.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 zond.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 contractAddr, _ := common.NewAddressFromString("Z0000000000000000000000000000000000000000") 124 bc := bind.NewBoundContract(contractAddr, 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 contractAddr, _ := common.NewAddressFromString("Z0000000000000000000000000000000000000000") 179 bc := bind.NewBoundContract(contractAddr, parsedAbi, nil, nil, nil) 180 sender, _ := common.NewAddressFromString("Z376c47978271565f56DEB45495afa69E59c16Ab2") 181 182 expectedReceivedMap := map[string]interface{}{ 183 "name": hash, 184 "sender": sender, 185 "amount": big.NewInt(1), 186 "memo": []byte{88}, 187 } 188 unpackAndCheck(t, bc, expectedReceivedMap, mockLog) 189 } 190 191 func TestUnpackAnonymousLogIntoMap(t *testing.T) { 192 mockLog := newMockLog(nil, common.HexToHash("0x0")) 193 194 abiString := `[{"anonymous":false,"inputs":[{"indexed":false,"name":"amount","type":"uint256"}],"name":"received","type":"event"}]` 195 parsedAbi, _ := abi.JSON(strings.NewReader(abiString)) 196 contractAddr, _ := common.NewAddressFromString("Z0000000000000000000000000000000000000000") 197 bc := bind.NewBoundContract(contractAddr, parsedAbi, nil, nil, nil) 198 199 var received map[string]interface{} 200 err := bc.UnpackLogIntoMap(received, "received", mockLog) 201 if err == nil { 202 t.Error("unpacking anonymous event is not supported") 203 } 204 if err.Error() != "no event signature" { 205 t.Errorf("expected error 'no event signature', got '%s'", err) 206 } 207 } 208 209 func TestUnpackIndexedSliceTyLogIntoMap(t *testing.T) { 210 sliceBytes, err := rlp.EncodeToBytes([]string{"name1", "name2", "name3", "name4"}) 211 if err != nil { 212 t.Fatal(err) 213 } 214 hash := crypto.Keccak256Hash(sliceBytes) 215 topics := []common.Hash{ 216 crypto.Keccak256Hash([]byte("received(string[],address,uint256,bytes)")), 217 hash, 218 } 219 mockLog := newMockLog(topics, common.HexToHash("0x0")) 220 221 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"}]` 222 parsedAbi, _ := abi.JSON(strings.NewReader(abiString)) 223 contractAddr, _ := common.NewAddressFromString("Z0000000000000000000000000000000000000000") 224 bc := bind.NewBoundContract(contractAddr, parsedAbi, nil, nil, nil) 225 226 sender, _ := common.NewAddressFromString("Z376c47978271565f56DEB45495afa69E59c16Ab2") 227 expectedReceivedMap := map[string]interface{}{ 228 "names": hash, 229 "sender": sender, 230 "amount": big.NewInt(1), 231 "memo": []byte{88}, 232 } 233 unpackAndCheck(t, bc, expectedReceivedMap, mockLog) 234 } 235 236 func TestUnpackIndexedArrayTyLogIntoMap(t *testing.T) { 237 address1, _ := common.NewAddressFromString("Z0000000000000000000000000000000000000000") 238 address2, _ := common.NewAddressFromString("Z376c47978271565f56DEB45495afa69E59c16Ab2") 239 arrBytes, err := rlp.EncodeToBytes([2]common.Address{address1, address2}) 240 if err != nil { 241 t.Fatal(err) 242 } 243 hash := crypto.Keccak256Hash(arrBytes) 244 topics := []common.Hash{ 245 crypto.Keccak256Hash([]byte("received(address[2],address,uint256,bytes)")), 246 hash, 247 } 248 mockLog := newMockLog(topics, common.HexToHash("0x0")) 249 250 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"}]` 251 parsedAbi, _ := abi.JSON(strings.NewReader(abiString)) 252 contractAddr, _ := common.NewAddressFromString("Z0000000000000000000000000000000000000000") 253 bc := bind.NewBoundContract(contractAddr, parsedAbi, nil, nil, nil) 254 255 sender, _ := common.NewAddressFromString("Z376c47978271565f56DEB45495afa69E59c16Ab2") 256 expectedReceivedMap := map[string]interface{}{ 257 "addresses": hash, 258 "sender": sender, 259 "amount": big.NewInt(1), 260 "memo": []byte{88}, 261 } 262 unpackAndCheck(t, bc, expectedReceivedMap, mockLog) 263 } 264 265 func TestUnpackIndexedFuncTyLogIntoMap(t *testing.T) { 266 mockAddress, _ := common.NewAddressFromString("Z376c47978271565f56DEB45495afa69E59c16Ab2") 267 addrBytes := mockAddress.Bytes() 268 hash := crypto.Keccak256Hash([]byte("mockFunction(address,uint)")) 269 functionSelector := hash[:4] 270 functionTyBytes := append(addrBytes, functionSelector...) 271 var functionTy [24]byte 272 copy(functionTy[:], functionTyBytes[0:24]) 273 topics := []common.Hash{ 274 crypto.Keccak256Hash([]byte("received(function,address,uint256,bytes)")), 275 common.BytesToHash(functionTyBytes), 276 } 277 mockLog := newMockLog(topics, common.HexToHash("0x5c698f13940a2153440c6d19660878bc90219d9298fdcf37365aa8d88d40fc42")) 278 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"}]` 279 parsedAbi, _ := abi.JSON(strings.NewReader(abiString)) 280 contractAddr, _ := common.NewAddressFromString("Z0000000000000000000000000000000000000000") 281 bc := bind.NewBoundContract(contractAddr, parsedAbi, nil, nil, nil) 282 283 sender, _ := common.NewAddressFromString("Z376c47978271565f56DEB45495afa69E59c16Ab2") 284 expectedReceivedMap := map[string]interface{}{ 285 "function": functionTy, 286 "sender": sender, 287 "amount": big.NewInt(1), 288 "memo": []byte{88}, 289 } 290 unpackAndCheck(t, bc, expectedReceivedMap, mockLog) 291 } 292 293 func TestUnpackIndexedBytesTyLogIntoMap(t *testing.T) { 294 bytes := []byte{1, 2, 3, 4, 5} 295 hash := crypto.Keccak256Hash(bytes) 296 topics := []common.Hash{ 297 crypto.Keccak256Hash([]byte("received(bytes,address,uint256,bytes)")), 298 hash, 299 } 300 mockLog := newMockLog(topics, common.HexToHash("0x5c698f13940a2153440c6d19660878bc90219d9298fdcf37365aa8d88d40fc42")) 301 302 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"}]` 303 parsedAbi, _ := abi.JSON(strings.NewReader(abiString)) 304 contractAddr, _ := common.NewAddressFromString("Z0000000000000000000000000000000000000000") 305 bc := bind.NewBoundContract(contractAddr, parsedAbi, nil, nil, nil) 306 307 sender, _ := common.NewAddressFromString("Z376c47978271565f56DEB45495afa69E59c16Ab2") 308 expectedReceivedMap := map[string]interface{}{ 309 "content": hash, 310 "sender": sender, 311 "amount": big.NewInt(1), 312 "memo": []byte{88}, 313 } 314 unpackAndCheck(t, bc, expectedReceivedMap, mockLog) 315 } 316 317 func TestTransactGasFee(t *testing.T) { 318 assert := assert.New(t) 319 320 // GasTipCap and GasFeeCap 321 // When opts.GasTipCap and opts.GasFeeCap are nil 322 mt := &mockTransactor{baseFee: big.NewInt(100), gasTipCap: big.NewInt(5)} 323 bc := bind.NewBoundContract(common.Address{}, abi.ABI{}, nil, mt, nil) 324 opts := &bind.TransactOpts{Signer: mockSign} 325 tx, err := bc.Transact(opts, "") 326 assert.Nil(err) 327 assert.Equal(big.NewInt(5), tx.GasTipCap()) 328 assert.Equal(big.NewInt(205), tx.GasFeeCap()) 329 assert.Nil(opts.GasTipCap) 330 assert.Nil(opts.GasFeeCap) 331 assert.True(mt.suggestGasTipCapCalled) 332 333 // Second call to Transact should use latest suggested GasTipCap 334 mt.gasTipCap = big.NewInt(6) 335 mt.suggestGasTipCapCalled = false 336 tx, err = bc.Transact(opts, "") 337 assert.Nil(err) 338 assert.Equal(big.NewInt(6), tx.GasTipCap()) 339 assert.Equal(big.NewInt(206), tx.GasFeeCap()) 340 assert.True(mt.suggestGasTipCapCalled) 341 } 342 343 func unpackAndCheck(t *testing.T, bc *bind.BoundContract, expected map[string]interface{}, mockLog types.Log) { 344 received := make(map[string]interface{}) 345 if err := bc.UnpackLogIntoMap(received, "received", mockLog); err != nil { 346 t.Error(err) 347 } 348 349 if len(received) != len(expected) { 350 t.Fatalf("unpacked map length %v not equal expected length of %v", len(received), len(expected)) 351 } 352 for name, elem := range expected { 353 if !reflect.DeepEqual(elem, received[name]) { 354 t.Errorf("field %v does not match expected, want %v, got %v", name, elem, received[name]) 355 } 356 } 357 } 358 359 func newMockLog(topics []common.Hash, txHash common.Hash) types.Log { 360 address, _ := common.NewAddressFromString("Z0000000000000000000000000000000000000000") 361 return types.Log{ 362 Address: address, 363 Topics: topics, 364 Data: hexutil.MustDecode(hexData), 365 BlockNumber: uint64(26), 366 TxHash: txHash, 367 TxIndex: 111, 368 BlockHash: common.BytesToHash([]byte{1, 2, 3, 4, 5}), 369 Index: 7, 370 Removed: false, 371 } 372 } 373 374 func TestCall(t *testing.T) { 375 contractAddr, _ := common.NewAddressFromString("Z0000000000000000000000000000000000000000") 376 var method, methodWithArg = "something", "somethingArrrrg" 377 tests := []struct { 378 name, method string 379 opts *bind.CallOpts 380 mc bind.ContractCaller 381 results *[]interface{} 382 wantErr bool 383 wantErrExact error 384 }{{ 385 name: "ok not pending", 386 mc: &mockCaller{ 387 codeAtBytes: []byte{0}, 388 }, 389 method: method, 390 }, { 391 name: "ok pending", 392 mc: &mockPendingCaller{ 393 pendingCodeAtBytes: []byte{0}, 394 }, 395 opts: &bind.CallOpts{ 396 Pending: true, 397 }, 398 method: method, 399 }, { 400 name: "pack error, no method", 401 mc: new(mockCaller), 402 method: "else", 403 wantErr: true, 404 }, { 405 name: "interface error, pending but not a PendingContractCaller", 406 mc: new(mockCaller), 407 opts: &bind.CallOpts{ 408 Pending: true, 409 }, 410 method: method, 411 wantErrExact: bind.ErrNoPendingState, 412 }, { 413 name: "pending call canceled", 414 mc: &mockPendingCaller{ 415 pendingCallContractErr: context.DeadlineExceeded, 416 }, 417 opts: &bind.CallOpts{ 418 Pending: true, 419 }, 420 method: method, 421 wantErrExact: context.DeadlineExceeded, 422 }, { 423 name: "pending code at error", 424 mc: &mockPendingCaller{ 425 pendingCodeAtErr: errors.New(""), 426 }, 427 opts: &bind.CallOpts{ 428 Pending: true, 429 }, 430 method: method, 431 wantErr: true, 432 }, { 433 name: "no pending code at", 434 mc: new(mockPendingCaller), 435 opts: &bind.CallOpts{ 436 Pending: true, 437 }, 438 method: method, 439 wantErrExact: bind.ErrNoCode, 440 }, { 441 name: "call contract error", 442 mc: &mockCaller{ 443 callContractErr: context.DeadlineExceeded, 444 }, 445 method: method, 446 wantErrExact: context.DeadlineExceeded, 447 }, { 448 name: "code at error", 449 mc: &mockCaller{ 450 codeAtErr: errors.New(""), 451 }, 452 method: method, 453 wantErr: true, 454 }, { 455 name: "no code at", 456 mc: new(mockCaller), 457 method: method, 458 wantErrExact: bind.ErrNoCode, 459 }, { 460 name: "unpack error missing arg", 461 mc: &mockCaller{ 462 codeAtBytes: []byte{0}, 463 }, 464 method: methodWithArg, 465 wantErr: true, 466 }, { 467 name: "interface unpack error", 468 mc: &mockCaller{ 469 codeAtBytes: []byte{0}, 470 }, 471 method: method, 472 results: &[]interface{}{0}, 473 wantErr: true, 474 }} 475 for _, test := range tests { 476 bc := bind.NewBoundContract(contractAddr, abi.ABI{ 477 Methods: map[string]abi.Method{ 478 method: { 479 Name: method, 480 Outputs: abi.Arguments{}, 481 }, 482 methodWithArg: { 483 Name: methodWithArg, 484 Outputs: abi.Arguments{abi.Argument{}}, 485 }, 486 }, 487 }, test.mc, nil, nil) 488 err := bc.Call(test.opts, test.results, test.method) 489 if test.wantErr || test.wantErrExact != nil { 490 if err == nil { 491 t.Fatalf("%q expected error", test.name) 492 } 493 if test.wantErrExact != nil && !errors.Is(err, test.wantErrExact) { 494 t.Fatalf("%q expected error %q but got %q", test.name, test.wantErrExact, err) 495 } 496 continue 497 } 498 if err != nil { 499 t.Fatalf("%q unexpected error: %v", test.name, err) 500 } 501 } 502 } 503 504 // TestCrashers contains some strings which previously caused the abi codec to crash. 505 func TestCrashers(t *testing.T) { 506 abi.JSON(strings.NewReader(`[{"inputs":[{"type":"tuple[]","components":[{"type":"bool","name":"_1"}]}]}]`)) 507 abi.JSON(strings.NewReader(`[{"inputs":[{"type":"tuple[]","components":[{"type":"bool","name":"&"}]}]}]`)) 508 abi.JSON(strings.NewReader(`[{"inputs":[{"type":"tuple[]","components":[{"type":"bool","name":"----"}]}]}]`)) 509 abi.JSON(strings.NewReader(`[{"inputs":[{"type":"tuple[]","components":[{"type":"bool","name":"foo.Bar"}]}]}]`)) 510 }