14
14
// You should have received a copy of the GNU Lesser General Public License
15
15
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
16
16
17
- package ethclient
17
+ package ethclient_test
18
18
19
19
import (
20
20
"bytes"
21
21
"context"
22
22
"errors"
23
+ "fmt"
23
24
"math/big"
24
25
"reflect"
25
26
"testing"
26
27
"time"
27
28
28
29
"github.com/ethereum/go-ethereum"
30
+ "github.com/ethereum/go-ethereum/accounts/abi"
29
31
"github.com/ethereum/go-ethereum/common"
30
32
"github.com/ethereum/go-ethereum/consensus/ethash"
31
33
"github.com/ethereum/go-ethereum/core"
32
34
"github.com/ethereum/go-ethereum/core/types"
33
35
"github.com/ethereum/go-ethereum/crypto"
34
36
"github.com/ethereum/go-ethereum/eth"
35
37
"github.com/ethereum/go-ethereum/eth/ethconfig"
38
+ "github.com/ethereum/go-ethereum/ethclient"
36
39
"github.com/ethereum/go-ethereum/node"
37
40
"github.com/ethereum/go-ethereum/params"
38
41
"github.com/ethereum/go-ethereum/rpc"
39
42
)
40
43
41
44
// Verify that Client implements the ethereum interfaces.
42
45
var (
43
- _ = ethereum .ChainReader (& Client {})
44
- _ = ethereum .TransactionReader (& Client {})
45
- _ = ethereum .ChainStateReader (& Client {})
46
- _ = ethereum .ChainSyncReader (& Client {})
47
- _ = ethereum .ContractCaller (& Client {})
48
- _ = ethereum .GasEstimator (& Client {})
49
- _ = ethereum .GasPricer (& Client {})
50
- _ = ethereum .LogFilterer (& Client {})
51
- _ = ethereum .PendingStateReader (& Client {})
52
- // _ = ethereum.PendingStateEventer(&Client{})
53
- _ = ethereum .PendingContractCaller (& Client {})
46
+ _ = ethereum .ChainReader (& ethclient. Client {})
47
+ _ = ethereum .TransactionReader (& ethclient. Client {})
48
+ _ = ethereum .ChainStateReader (& ethclient. Client {})
49
+ _ = ethereum .ChainSyncReader (& ethclient. Client {})
50
+ _ = ethereum .ContractCaller (& ethclient. Client {})
51
+ _ = ethereum .GasEstimator (& ethclient. Client {})
52
+ _ = ethereum .GasPricer (& ethclient. Client {})
53
+ _ = ethereum .LogFilterer (& ethclient. Client {})
54
+ _ = ethereum .PendingStateReader (& ethclient. Client {})
55
+ // _ = ethereum.PendingStateEventer(ðclient. Client{})
56
+ _ = ethereum .PendingContractCaller (& ethclient. Client {})
54
57
)
55
58
56
- func TestToFilterArg (t * testing.T ) {
57
- blockHashErr := errors .New ("cannot specify both BlockHash and FromBlock/ToBlock" )
58
- addresses := []common.Address {
59
- common .HexToAddress ("0xD36722ADeC3EdCB29c8e7b5a47f352D701393462" ),
60
- }
61
- blockHash := common .HexToHash (
62
- "0xeb94bb7d78b73657a9d7a99792413f50c0a45c51fc62bdcb08a53f18e9a2b4eb" ,
63
- )
64
-
65
- for _ , testCase := range []struct {
66
- name string
67
- input ethereum.FilterQuery
68
- output interface {}
69
- err error
70
- }{
71
- {
72
- "without BlockHash" ,
73
- ethereum.FilterQuery {
74
- Addresses : addresses ,
75
- FromBlock : big .NewInt (1 ),
76
- ToBlock : big .NewInt (2 ),
77
- Topics : [][]common.Hash {},
78
- },
79
- map [string ]interface {}{
80
- "address" : addresses ,
81
- "fromBlock" : "0x1" ,
82
- "toBlock" : "0x2" ,
83
- "topics" : [][]common.Hash {},
84
- },
85
- nil ,
86
- },
87
- {
88
- "with nil fromBlock and nil toBlock" ,
89
- ethereum.FilterQuery {
90
- Addresses : addresses ,
91
- Topics : [][]common.Hash {},
92
- },
93
- map [string ]interface {}{
94
- "address" : addresses ,
95
- "fromBlock" : "0x0" ,
96
- "toBlock" : "latest" ,
97
- "topics" : [][]common.Hash {},
98
- },
99
- nil ,
100
- },
101
- {
102
- "with negative fromBlock and negative toBlock" ,
103
- ethereum.FilterQuery {
104
- Addresses : addresses ,
105
- FromBlock : big .NewInt (- 1 ),
106
- ToBlock : big .NewInt (- 1 ),
107
- Topics : [][]common.Hash {},
108
- },
109
- map [string ]interface {}{
110
- "address" : addresses ,
111
- "fromBlock" : "pending" ,
112
- "toBlock" : "pending" ,
113
- "topics" : [][]common.Hash {},
114
- },
115
- nil ,
116
- },
117
- {
118
- "with blockhash" ,
119
- ethereum.FilterQuery {
120
- Addresses : addresses ,
121
- BlockHash : & blockHash ,
122
- Topics : [][]common.Hash {},
123
- },
124
- map [string ]interface {}{
125
- "address" : addresses ,
126
- "blockHash" : blockHash ,
127
- "topics" : [][]common.Hash {},
128
- },
129
- nil ,
130
- },
131
- {
132
- "with blockhash and from block" ,
133
- ethereum.FilterQuery {
134
- Addresses : addresses ,
135
- BlockHash : & blockHash ,
136
- FromBlock : big .NewInt (1 ),
137
- Topics : [][]common.Hash {},
138
- },
139
- nil ,
140
- blockHashErr ,
141
- },
142
- {
143
- "with blockhash and to block" ,
144
- ethereum.FilterQuery {
145
- Addresses : addresses ,
146
- BlockHash : & blockHash ,
147
- ToBlock : big .NewInt (1 ),
148
- Topics : [][]common.Hash {},
149
- },
150
- nil ,
151
- blockHashErr ,
152
- },
153
- {
154
- "with blockhash and both from / to block" ,
155
- ethereum.FilterQuery {
156
- Addresses : addresses ,
157
- BlockHash : & blockHash ,
158
- FromBlock : big .NewInt (1 ),
159
- ToBlock : big .NewInt (2 ),
160
- Topics : [][]common.Hash {},
161
- },
162
- nil ,
163
- blockHashErr ,
164
- },
165
- } {
166
- t .Run (testCase .name , func (t * testing.T ) {
167
- output , err := toFilterArg (testCase .input )
168
- if (testCase .err == nil ) != (err == nil ) {
169
- t .Fatalf ("expected error %v but got %v" , testCase .err , err )
170
- }
171
- if testCase .err != nil {
172
- if testCase .err .Error () != err .Error () {
173
- t .Fatalf ("expected error %v but got %v" , testCase .err , err )
174
- }
175
- } else if ! reflect .DeepEqual (testCase .output , output ) {
176
- t .Fatalf ("expected filter arg %v but got %v" , testCase .output , output )
177
- }
178
- })
179
- }
180
- }
181
-
182
59
var (
183
- testKey , _ = crypto .HexToECDSA ("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291" )
184
- testAddr = crypto .PubkeyToAddress (testKey .PublicKey )
185
- testBalance = big .NewInt (2e15 )
60
+ testKey , _ = crypto .HexToECDSA ("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291" )
61
+ testAddr = crypto .PubkeyToAddress (testKey .PublicKey )
62
+ testBalance = big .NewInt (2e15 )
63
+ revertContractAddr = common .HexToAddress ("290f1b36649a61e369c6276f6d29463335b4400c" )
64
+ revertCode = common .FromHex ("7f08c379a0000000000000000000000000000000000000000000000000000000006000526020600452600a6024527f75736572206572726f7200000000000000000000000000000000000000000000604452604e6000fd" )
186
65
)
187
66
188
67
var genesis = & core.Genesis {
189
- Config : params .AllEthashProtocolChanges ,
190
- Alloc : types.GenesisAlloc {testAddr : {Balance : testBalance }},
68
+ Config : params .AllEthashProtocolChanges ,
69
+ Alloc : types.GenesisAlloc {
70
+ testAddr : {Balance : testBalance },
71
+ revertContractAddr : {Code : revertCode },
72
+ },
191
73
ExtraData : []byte ("test genesis" ),
192
74
Timestamp : 9000 ,
193
75
BaseFee : big .NewInt (params .InitialBaseFee ),
@@ -209,27 +91,30 @@ var testTx2 = types.MustSignNewTx(testKey, types.LatestSigner(genesis.Config), &
209
91
To : & common.Address {2 },
210
92
})
211
93
212
- func newTestBackend (t * testing. T ) (* node.Node , []* types.Block ) {
94
+ func newTestBackend (config * node. Config ) (* node.Node , []* types.Block , error ) {
213
95
// Generate test chain.
214
96
blocks := generateTestChain ()
215
97
216
98
// Create node
217
- n , err := node .New (& node.Config {})
99
+ if config == nil {
100
+ config = new (node.Config )
101
+ }
102
+ n , err := node .New (config )
218
103
if err != nil {
219
- t . Fatalf ("can't create new node: %v" , err )
104
+ return nil , nil , fmt . Errorf ("can't create new node: %v" , err )
220
105
}
221
106
// Create Ethereum Service
222
- config := & ethconfig.Config {Genesis : genesis , RPCGasCap : 1000000 }
223
- ethservice , err := eth .New (n , config )
107
+ ecfg := & ethconfig.Config {Genesis : genesis , RPCGasCap : 1000000 }
108
+ ethservice , err := eth .New (n , ecfg )
224
109
if err != nil {
225
- t . Fatalf ("can't create new ethereum service: %v" , err )
110
+ return nil , nil , fmt . Errorf ("can't create new ethereum service: %v" , err )
226
111
}
227
112
// Import the test chain.
228
113
if err := n .Start (); err != nil {
229
- t . Fatalf ("can't start test node: %v" , err )
114
+ return nil , nil , fmt . Errorf ("can't start test node: %v" , err )
230
115
}
231
116
if _ , err := ethservice .BlockChain ().InsertChain (blocks [1 :]); err != nil {
232
- t . Fatalf ("can't import test blocks: %v" , err )
117
+ return nil , nil , fmt . Errorf ("can't import test blocks: %v" , err )
233
118
}
234
119
// Ensure the tx indexing is fully generated
235
120
for ; ; time .Sleep (time .Millisecond * 100 ) {
@@ -238,7 +123,7 @@ func newTestBackend(t *testing.T) (*node.Node, []*types.Block) {
238
123
break
239
124
}
240
125
}
241
- return n , blocks
126
+ return n , blocks , nil
242
127
}
243
128
244
129
func generateTestChain () []* types.Block {
@@ -256,7 +141,10 @@ func generateTestChain() []*types.Block {
256
141
}
257
142
258
143
func TestEthClient (t * testing.T ) {
259
- backend , chain := newTestBackend (t )
144
+ backend , chain , err := newTestBackend (nil )
145
+ if err != nil {
146
+ t .Fatal (err )
147
+ }
260
148
client := backend .Attach ()
261
149
defer backend .Close ()
262
150
defer client .Close ()
@@ -324,7 +212,7 @@ func testHeader(t *testing.T, chain []*types.Block, client *rpc.Client) {
324
212
}
325
213
for name , tt := range tests {
326
214
t .Run (name , func (t * testing.T ) {
327
- ec := NewClient (client )
215
+ ec := ethclient . NewClient (client )
328
216
ctx , cancel := context .WithTimeout (context .Background (), 100 * time .Millisecond )
329
217
defer cancel ()
330
218
@@ -373,7 +261,7 @@ func testBalanceAt(t *testing.T, client *rpc.Client) {
373
261
}
374
262
for name , tt := range tests {
375
263
t .Run (name , func (t * testing.T ) {
376
- ec := NewClient (client )
264
+ ec := ethclient . NewClient (client )
377
265
ctx , cancel := context .WithTimeout (context .Background (), 100 * time .Millisecond )
378
266
defer cancel ()
379
267
@@ -389,7 +277,7 @@ func testBalanceAt(t *testing.T, client *rpc.Client) {
389
277
}
390
278
391
279
func testTransactionInBlock (t * testing.T , client * rpc.Client ) {
392
- ec := NewClient (client )
280
+ ec := ethclient . NewClient (client )
393
281
394
282
// Get current block by number.
395
283
block , err := ec .BlockByNumber (context .Background (), nil )
@@ -421,7 +309,7 @@ func testTransactionInBlock(t *testing.T, client *rpc.Client) {
421
309
}
422
310
423
311
func testChainID (t * testing.T , client * rpc.Client ) {
424
- ec := NewClient (client )
312
+ ec := ethclient . NewClient (client )
425
313
id , err := ec .ChainID (context .Background ())
426
314
if err != nil {
427
315
t .Fatalf ("unexpected error: %v" , err )
@@ -432,7 +320,7 @@ func testChainID(t *testing.T, client *rpc.Client) {
432
320
}
433
321
434
322
func testGetBlock (t * testing.T , client * rpc.Client ) {
435
- ec := NewClient (client )
323
+ ec := ethclient . NewClient (client )
436
324
437
325
// Get current block number
438
326
blockNumber , err := ec .BlockNumber (context .Background ())
@@ -477,7 +365,7 @@ func testGetBlock(t *testing.T, client *rpc.Client) {
477
365
}
478
366
479
367
func testStatusFunctions (t * testing.T , client * rpc.Client ) {
480
- ec := NewClient (client )
368
+ ec := ethclient . NewClient (client )
481
369
482
370
// Sync progress
483
371
progress , err := ec .SyncProgress (context .Background ())
@@ -540,7 +428,7 @@ func testStatusFunctions(t *testing.T, client *rpc.Client) {
540
428
}
541
429
542
430
func testCallContractAtHash (t * testing.T , client * rpc.Client ) {
543
- ec := NewClient (client )
431
+ ec := ethclient . NewClient (client )
544
432
545
433
// EstimateGas
546
434
msg := ethereum.CallMsg {
@@ -567,7 +455,7 @@ func testCallContractAtHash(t *testing.T, client *rpc.Client) {
567
455
}
568
456
569
457
func testCallContract (t * testing.T , client * rpc.Client ) {
570
- ec := NewClient (client )
458
+ ec := ethclient . NewClient (client )
571
459
572
460
// EstimateGas
573
461
msg := ethereum.CallMsg {
@@ -594,7 +482,7 @@ func testCallContract(t *testing.T, client *rpc.Client) {
594
482
}
595
483
596
484
func testAtFunctions (t * testing.T , client * rpc.Client ) {
597
- ec := NewClient (client )
485
+ ec := ethclient . NewClient (client )
598
486
599
487
block , err := ec .HeaderByNumber (context .Background (), big .NewInt (1 ))
600
488
if err != nil {
@@ -697,7 +585,7 @@ func testAtFunctions(t *testing.T, client *rpc.Client) {
697
585
}
698
586
699
587
func testTransactionSender (t * testing.T , client * rpc.Client ) {
700
- ec := NewClient (client )
588
+ ec := ethclient . NewClient (client )
701
589
ctx := context .Background ()
702
590
703
591
// Retrieve testTx1 via RPC.
@@ -737,7 +625,7 @@ func testTransactionSender(t *testing.T, client *rpc.Client) {
737
625
}
738
626
}
739
627
740
- func sendTransaction (ec * Client ) error {
628
+ func sendTransaction (ec * ethclient. Client ) error {
741
629
chainID , err := ec .ChainID (context .Background ())
742
630
if err != nil {
743
631
return err
@@ -760,3 +648,40 @@ func sendTransaction(ec *Client) error {
760
648
}
761
649
return ec .SendTransaction (context .Background (), tx )
762
650
}
651
+
652
+ // Here we show how to get the error message of reverted contract call.
653
+ func ExampleRevertErrorData () {
654
+ // First create an ethclient.Client instance.
655
+ ctx := context .Background ()
656
+ ec , _ := ethclient .DialContext (ctx , exampleNode .HTTPEndpoint ())
657
+
658
+ // Call the contract.
659
+ // Note we expect the call to return an error.
660
+ contract := common .HexToAddress ("290f1b36649a61e369c6276f6d29463335b4400c" )
661
+ call := ethereum.CallMsg {To : & contract , Gas : 30000 }
662
+ result , err := ec .CallContract (ctx , call , nil )
663
+ if len (result ) > 0 {
664
+ panic ("got result" )
665
+ }
666
+ if err == nil {
667
+ panic ("call did not return error" )
668
+ }
669
+
670
+ // Extract the low-level revert data from the error.
671
+ revertData , ok := ethclient .RevertErrorData (err )
672
+ if ! ok {
673
+ panic ("unpacking revert failed" )
674
+ }
675
+ fmt .Printf ("revert: %x\n " , revertData )
676
+
677
+ // Parse the revert data to obtain the error message.
678
+ message , err := abi .UnpackRevert (revertData )
679
+ if err != nil {
680
+ panic ("parsing ABI error failed: " + err .Error ())
681
+ }
682
+ fmt .Println ("message:" , message )
683
+
684
+ // Output:
685
+ // revert: 08c379a00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000a75736572206572726f72
686
+ // message: user error
687
+ }
0 commit comments