github.com/bigzoro/my_simplechain@v0.0.0-20240315012955-8ad0a2a29bb9/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 "math/big" 22 "reflect" 23 "strings" 24 "testing" 25 26 "github.com/bigzoro/my_simplechain/accounts/abi" 27 "github.com/bigzoro/my_simplechain/accounts/abi/bind" 28 "github.com/bigzoro/my_simplechain/common" 29 "github.com/bigzoro/my_simplechain/common/hexutil" 30 "github.com/bigzoro/my_simplechain/core/types" 31 "github.com/bigzoro/my_simplechain/crypto" 32 "github.com/bigzoro/my_simplechain/rlp" 33 "github.com/stretchr/testify/assert" 34 ) 35 36 func mockSign(addr common.Address, tx *types.Transaction) (*types.Transaction, error) { return tx, nil } 37 38 type mockTransactor struct { 39 baseFee *big.Int 40 gasTipCap *big.Int 41 gasPrice *big.Int 42 suggestGasTipCapCalled bool 43 suggestGasPriceCalled bool 44 } 45 46 func (mt *mockTransactor) HeaderByNumber(ctx context.Context, number *big.Int) (*types.Header, error) { 47 return &types.Header{BaseFee: mt.baseFee}, nil 48 } 49 50 func (mt *mockTransactor) PendingCodeAt(ctx context.Context, account common.Address) ([]byte, error) { 51 return []byte{1}, nil 52 } 53 54 func (mt *mockTransactor) PendingNonceAt(ctx context.Context, account common.Address) (uint64, error) { 55 return 0, nil 56 } 57 58 func (mt *mockTransactor) SuggestGasPrice(ctx context.Context) (*big.Int, error) { 59 mt.suggestGasPriceCalled = true 60 return mt.gasPrice, nil 61 } 62 63 func (mt *mockTransactor) SuggestGasTipCap(ctx context.Context) (*big.Int, error) { 64 mt.suggestGasTipCapCalled = true 65 return mt.gasTipCap, nil 66 } 67 68 func (mt *mockTransactor) EstimateGas(ctx context.Context, call ethereum.CallMsg) (gas uint64, err error) { 69 return 0, nil 70 } 71 72 func (mt *mockTransactor) SendTransaction(ctx context.Context, tx *types.Transaction) error { 73 return nil 74 } 75 76 type mockCaller struct { 77 codeAtBlockNumber *big.Int 78 callContractBlockNumber *big.Int 79 pendingCodeAtCalled bool 80 pendingCallContractCalled bool 81 } 82 83 func (mc *mockCaller) CodeAt(ctx context.Context, contract common.Address, blockNumber *big.Int) ([]byte, error) { 84 mc.codeAtBlockNumber = blockNumber 85 return []byte{1, 2, 3}, nil 86 } 87 88 func (mc *mockCaller) CallContract(ctx context.Context, call ethereum.CallMsg, blockNumber *big.Int) ([]byte, error) { 89 mc.callContractBlockNumber = blockNumber 90 return nil, nil 91 } 92 93 func (mc *mockCaller) PendingCodeAt(ctx context.Context, contract common.Address) ([]byte, error) { 94 mc.pendingCodeAtCalled = true 95 return nil, nil 96 } 97 98 func (mc *mockCaller) PendingCallContract(ctx context.Context, call ethereum.CallMsg) ([]byte, error) { 99 mc.pendingCallContractCalled = true 100 return nil, nil 101 } 102 func TestPassingBlockNumber(t *testing.T) { 103 104 mc := &mockCaller{} 105 106 bc := bind.NewBoundContract(common.HexToAddress("0x0"), abi.ABI{ 107 Methods: map[string]abi.Method{ 108 "something": { 109 Name: "something", 110 Outputs: abi.Arguments{}, 111 }, 112 }, 113 }, mc, nil, nil) 114 115 blockNumber := big.NewInt(42) 116 117 bc.Call(&bind.CallOpts{BlockNumber: blockNumber}, nil, "something") 118 119 if mc.callContractBlockNumber != blockNumber { 120 t.Fatalf("CallContract() was not passed the block number") 121 } 122 123 if mc.codeAtBlockNumber != blockNumber { 124 t.Fatalf("CodeAt() was not passed the block number") 125 } 126 127 bc.Call(&bind.CallOpts{}, nil, "something") 128 129 if mc.callContractBlockNumber != nil { 130 t.Fatalf("CallContract() was passed a block number when it should not have been") 131 } 132 133 if mc.codeAtBlockNumber != nil { 134 t.Fatalf("CodeAt() was passed a block number when it should not have been") 135 } 136 137 bc.Call(&bind.CallOpts{BlockNumber: blockNumber, Pending: true}, nil, "something") 138 139 if !mc.pendingCallContractCalled { 140 t.Fatalf("CallContract() was not passed the block number") 141 } 142 143 if !mc.pendingCodeAtCalled { 144 t.Fatalf("CodeAt() was not passed the block number") 145 } 146 } 147 148 const hexData = "0x000000000000000000000000376c47978271565f56deb45495afa69e59c16ab200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000158" 149 150 func TestUnpackIndexedStringTyLogIntoMap(t *testing.T) { 151 hash := crypto.Keccak256Hash([]byte("testName")) 152 topics := []common.Hash{ 153 crypto.Keccak256Hash([]byte("received(string,address,uint256,bytes)")), 154 hash, 155 } 156 mockLog := newMockLog(topics, common.HexToHash("0x0")) 157 158 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"}]` 159 parsedAbi, _ := abi.JSON(strings.NewReader(abiString)) 160 bc := bind.NewBoundContract(common.HexToAddress("0x0"), parsedAbi, nil, nil, nil) 161 162 expectedReceivedMap := map[string]interface{}{ 163 "name": hash, 164 "sender": common.HexToAddress("0x376c47978271565f56DEB45495afa69E59c16Ab2"), 165 "amount": big.NewInt(1), 166 "memo": []byte{88}, 167 } 168 unpackAndCheck(t, bc, expectedReceivedMap, mockLog) 169 } 170 171 func TestUnpackIndexedSliceTyLogIntoMap(t *testing.T) { 172 sliceBytes, err := rlp.EncodeToBytes([]string{"name1", "name2", "name3", "name4"}) 173 if err != nil { 174 t.Fatal(err) 175 } 176 hash := crypto.Keccak256Hash(sliceBytes) 177 topics := []common.Hash{ 178 crypto.Keccak256Hash([]byte("received(string[],address,uint256,bytes)")), 179 hash, 180 } 181 mockLog := newMockLog(topics, common.HexToHash("0x0")) 182 183 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"}]` 184 parsedAbi, _ := abi.JSON(strings.NewReader(abiString)) 185 bc := bind.NewBoundContract(common.HexToAddress("0x0"), parsedAbi, nil, nil, nil) 186 187 expectedReceivedMap := map[string]interface{}{ 188 "names": hash, 189 "sender": common.HexToAddress("0x376c47978271565f56DEB45495afa69E59c16Ab2"), 190 "amount": big.NewInt(1), 191 "memo": []byte{88}, 192 } 193 unpackAndCheck(t, bc, expectedReceivedMap, mockLog) 194 } 195 196 func TestUnpackIndexedArrayTyLogIntoMap(t *testing.T) { 197 arrBytes, err := rlp.EncodeToBytes([2]common.Address{common.HexToAddress("0x0"), common.HexToAddress("0x376c47978271565f56DEB45495afa69E59c16Ab2")}) 198 if err != nil { 199 t.Fatal(err) 200 } 201 hash := crypto.Keccak256Hash(arrBytes) 202 topics := []common.Hash{ 203 crypto.Keccak256Hash([]byte("received(address[2],address,uint256,bytes)")), 204 hash, 205 } 206 mockLog := newMockLog(topics, common.HexToHash("0x0")) 207 208 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"}]` 209 parsedAbi, _ := abi.JSON(strings.NewReader(abiString)) 210 bc := bind.NewBoundContract(common.HexToAddress("0x0"), parsedAbi, nil, nil, nil) 211 212 expectedReceivedMap := map[string]interface{}{ 213 "addresses": hash, 214 "sender": common.HexToAddress("0x376c47978271565f56DEB45495afa69E59c16Ab2"), 215 "amount": big.NewInt(1), 216 "memo": []byte{88}, 217 } 218 unpackAndCheck(t, bc, expectedReceivedMap, mockLog) 219 } 220 221 func TestUnpackIndexedFuncTyLogIntoMap(t *testing.T) { 222 mockAddress := common.HexToAddress("0x376c47978271565f56DEB45495afa69E59c16Ab2") 223 addrBytes := mockAddress.Bytes() 224 hash := crypto.Keccak256Hash([]byte("mockFunction(address,uint)")) 225 functionSelector := hash[:4] 226 functionTyBytes := append(addrBytes, functionSelector...) 227 var functionTy [24]byte 228 copy(functionTy[:], functionTyBytes[0:24]) 229 topics := []common.Hash{ 230 crypto.Keccak256Hash([]byte("received(function,address,uint256,bytes)")), 231 common.BytesToHash(functionTyBytes), 232 } 233 mockLog := newMockLog(topics, common.HexToHash("0x5c698f13940a2153440c6d19660878bc90219d9298fdcf37365aa8d88d40fc42")) 234 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"}]` 235 parsedAbi, _ := abi.JSON(strings.NewReader(abiString)) 236 bc := bind.NewBoundContract(common.HexToAddress("0x0"), parsedAbi, nil, nil, nil) 237 238 expectedReceivedMap := map[string]interface{}{ 239 "function": functionTy, 240 "sender": common.HexToAddress("0x376c47978271565f56DEB45495afa69E59c16Ab2"), 241 "amount": big.NewInt(1), 242 "memo": []byte{88}, 243 } 244 unpackAndCheck(t, bc, expectedReceivedMap, mockLog) 245 } 246 247 func TestUnpackIndexedBytesTyLogIntoMap(t *testing.T) { 248 bytes := []byte{1, 2, 3, 4, 5} 249 hash := crypto.Keccak256Hash(bytes) 250 topics := []common.Hash{ 251 crypto.Keccak256Hash([]byte("received(bytes,address,uint256,bytes)")), 252 hash, 253 } 254 mockLog := newMockLog(topics, common.HexToHash("0x5c698f13940a2153440c6d19660878bc90219d9298fdcf37365aa8d88d40fc42")) 255 256 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"}]` 257 parsedAbi, _ := abi.JSON(strings.NewReader(abiString)) 258 bc := bind.NewBoundContract(common.HexToAddress("0x0"), parsedAbi, nil, nil, nil) 259 260 expectedReceivedMap := map[string]interface{}{ 261 "content": hash, 262 "sender": common.HexToAddress("0x376c47978271565f56DEB45495afa69E59c16Ab2"), 263 "amount": big.NewInt(1), 264 "memo": []byte{88}, 265 } 266 unpackAndCheck(t, bc, expectedReceivedMap, mockLog) 267 } 268 269 func TestTransactGasFee(t *testing.T) { 270 assert := assert.New(t) 271 272 // GasTipCap and GasFeeCap 273 // When opts.GasTipCap and opts.GasFeeCap are nil 274 mt := &mockTransactor{baseFee: big.NewInt(100), gasTipCap: big.NewInt(5)} 275 bc := bind.NewBoundContract(common.Address{}, abi.ABI{}, nil, mt, nil) 276 opts := &bind.TransactOpts{Signer: mockSign} 277 tx, err := bc.Transact(opts, "") 278 assert.Nil(err) 279 assert.Equal(big.NewInt(5), tx.GasTipCap()) 280 assert.Equal(big.NewInt(205), tx.GasFeeCap()) 281 assert.Nil(opts.GasTipCap) 282 assert.Nil(opts.GasFeeCap) 283 assert.True(mt.suggestGasTipCapCalled) 284 285 // Second call to Transact should use latest suggested GasTipCap 286 mt.gasTipCap = big.NewInt(6) 287 mt.suggestGasTipCapCalled = false 288 tx, err = bc.Transact(opts, "") 289 assert.Nil(err) 290 assert.Equal(big.NewInt(6), tx.GasTipCap()) 291 assert.Equal(big.NewInt(206), tx.GasFeeCap()) 292 assert.True(mt.suggestGasTipCapCalled) 293 294 // GasPrice 295 // When opts.GasPrice is nil 296 mt = &mockTransactor{gasPrice: big.NewInt(5)} 297 bc = bind.NewBoundContract(common.Address{}, abi.ABI{}, nil, mt, nil) 298 opts = &bind.TransactOpts{Signer: mockSign} 299 tx, err = bc.Transact(opts, "") 300 assert.Nil(err) 301 assert.Equal(big.NewInt(5), tx.GasPrice()) 302 assert.Nil(opts.GasPrice) 303 assert.True(mt.suggestGasPriceCalled) 304 305 // Second call to Transact should use latest suggested GasPrice 306 mt.gasPrice = big.NewInt(6) 307 mt.suggestGasPriceCalled = false 308 tx, err = bc.Transact(opts, "") 309 assert.Nil(err) 310 assert.Equal(big.NewInt(6), tx.GasPrice()) 311 assert.True(mt.suggestGasPriceCalled) 312 } 313 314 func unpackAndCheck(t *testing.T, bc *bind.BoundContract, expected map[string]interface{}, mockLog types.Log) { 315 received := make(map[string]interface{}) 316 if err := bc.UnpackLogIntoMap(received, "received", mockLog); err != nil { 317 t.Error(err) 318 } 319 320 if len(received) != len(expected) { 321 t.Fatalf("unpacked map length %v not equal expected length of %v", len(received), len(expected)) 322 } 323 for name, elem := range expected { 324 if !reflect.DeepEqual(elem, received[name]) { 325 t.Errorf("field %v does not match expected, want %v, got %v", name, elem, received[name]) 326 } 327 } 328 } 329 330 func newMockLog(topics []common.Hash, txHash common.Hash) types.Log { 331 return types.Log{ 332 Address: common.HexToAddress("0x0"), 333 Topics: topics, 334 Data: hexutil.MustDecode(hexData), 335 BlockNumber: uint64(26), 336 TxHash: txHash, 337 TxIndex: 111, 338 BlockHash: common.BytesToHash([]byte{1, 2, 3, 4, 5}), 339 Index: 7, 340 Removed: false, 341 } 342 }