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