Skip to content

Commit 57a673a

Browse files
JoeGruffinsjagdeep sidhu
authored andcommitted
abi/base: return error for pending call error (ethereum#24649)
If a pending contract call errors, return that error right away rather than ignoring it to allow an error somewhere else. This is helpful for callers to know if perhaps a call failed because of the context deadline being expired. This change mirrors the behavior of non-pending contract calls.
1 parent c0405b7 commit 57a673a

File tree

2 files changed

+162
-12
lines changed

2 files changed

+162
-12
lines changed

accounts/abi/bind/base.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,10 @@ func (c *BoundContract) Call(opts *CallOpts, results *[]interface{}, method stri
171171
return ErrNoPendingState
172172
}
173173
output, err = pb.PendingCallContract(ctx, msg)
174-
if err == nil && len(output) == 0 {
174+
if err != nil {
175+
return err
176+
}
177+
if len(output) == 0 {
175178
// Make sure we have a contract to operate on, and bail out otherwise.
176179
if code, err = pb.PendingCodeAt(ctx, c.address); err != nil {
177180
return err

accounts/abi/bind/base_test.go

Lines changed: 158 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ package bind_test
1818

1919
import (
2020
"context"
21+
"errors"
2122
"math/big"
2223
"reflect"
2324
"strings"
@@ -75,34 +76,51 @@ func (mt *mockTransactor) SendTransaction(ctx context.Context, tx *types.Transac
7576
}
7677

7778
type mockCaller struct {
78-
codeAtBlockNumber *big.Int
79-
callContractBlockNumber *big.Int
80-
pendingCodeAtCalled bool
81-
pendingCallContractCalled bool
79+
codeAtBlockNumber *big.Int
80+
callContractBlockNumber *big.Int
81+
callContractBytes []byte
82+
callContractErr error
83+
codeAtBytes []byte
84+
codeAtErr error
8285
}
8386

8487
func (mc *mockCaller) CodeAt(ctx context.Context, contract common.Address, blockNumber *big.Int) ([]byte, error) {
8588
mc.codeAtBlockNumber = blockNumber
86-
return []byte{1, 2, 3}, nil
89+
return mc.codeAtBytes, mc.codeAtErr
8790
}
8891

8992
func (mc *mockCaller) CallContract(ctx context.Context, call ethereum.CallMsg, blockNumber *big.Int) ([]byte, error) {
9093
mc.callContractBlockNumber = blockNumber
91-
return nil, nil
94+
return mc.callContractBytes, mc.callContractErr
9295
}
9396

94-
func (mc *mockCaller) PendingCodeAt(ctx context.Context, contract common.Address) ([]byte, error) {
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) {
95108
mc.pendingCodeAtCalled = true
96-
return nil, nil
109+
return mc.pendingCodeAtBytes, mc.pendingCodeAtErr
97110
}
98111

99-
func (mc *mockCaller) PendingCallContract(ctx context.Context, call ethereum.CallMsg) ([]byte, error) {
112+
func (mc *mockPendingCaller) PendingCallContract(ctx context.Context, call ethereum.CallMsg) ([]byte, error) {
100113
mc.pendingCallContractCalled = true
101-
return nil, nil
114+
return mc.pendingCallContractBytes, mc.pendingCallContractErr
102115
}
116+
103117
func TestPassingBlockNumber(t *testing.T) {
104118

105-
mc := &mockCaller{}
119+
mc := &mockPendingCaller{
120+
mockCaller: &mockCaller{
121+
codeAtBytes: []byte{1, 2, 3},
122+
},
123+
}
106124

107125
bc := bind.NewBoundContract(common.HexToAddress("0x0"), abi.ABI{
108126
Methods: map[string]abi.Method{
@@ -341,3 +359,132 @@ func newMockLog(topics []common.Hash, txHash common.Hash) types.Log {
341359
Removed: false,
342360
}
343361
}
362+
363+
func TestCall(t *testing.T) {
364+
var method, methodWithArg = "something", "somethingArrrrg"
365+
tests := []struct {
366+
name, method string
367+
opts *bind.CallOpts
368+
mc bind.ContractCaller
369+
results *[]interface{}
370+
wantErr bool
371+
wantErrExact error
372+
}{{
373+
name: "ok not pending",
374+
mc: &mockCaller{
375+
codeAtBytes: []byte{0},
376+
},
377+
method: method,
378+
}, {
379+
name: "ok pending",
380+
mc: &mockPendingCaller{
381+
pendingCodeAtBytes: []byte{0},
382+
},
383+
opts: &bind.CallOpts{
384+
Pending: true,
385+
},
386+
method: method,
387+
}, {
388+
name: "pack error, no method",
389+
mc: new(mockCaller),
390+
method: "else",
391+
wantErr: true,
392+
}, {
393+
name: "interface error, pending but not a PendingContractCaller",
394+
mc: new(mockCaller),
395+
opts: &bind.CallOpts{
396+
Pending: true,
397+
},
398+
method: method,
399+
wantErrExact: bind.ErrNoPendingState,
400+
}, {
401+
name: "pending call canceled",
402+
mc: &mockPendingCaller{
403+
pendingCallContractErr: context.DeadlineExceeded,
404+
},
405+
opts: &bind.CallOpts{
406+
Pending: true,
407+
},
408+
method: method,
409+
wantErrExact: context.DeadlineExceeded,
410+
}, {
411+
name: "pending code at error",
412+
mc: &mockPendingCaller{
413+
pendingCodeAtErr: errors.New(""),
414+
},
415+
opts: &bind.CallOpts{
416+
Pending: true,
417+
},
418+
method: method,
419+
wantErr: true,
420+
}, {
421+
name: "no pending code at",
422+
mc: new(mockPendingCaller),
423+
opts: &bind.CallOpts{
424+
Pending: true,
425+
},
426+
method: method,
427+
wantErrExact: bind.ErrNoCode,
428+
}, {
429+
name: "call contract error",
430+
mc: &mockCaller{
431+
callContractErr: context.DeadlineExceeded,
432+
},
433+
method: method,
434+
wantErrExact: context.DeadlineExceeded,
435+
}, {
436+
name: "code at error",
437+
mc: &mockCaller{
438+
codeAtErr: errors.New(""),
439+
},
440+
method: method,
441+
wantErr: true,
442+
}, {
443+
name: "no code at",
444+
mc: new(mockCaller),
445+
method: method,
446+
wantErrExact: bind.ErrNoCode,
447+
}, {
448+
name: "unpack error missing arg",
449+
mc: &mockCaller{
450+
codeAtBytes: []byte{0},
451+
},
452+
method: methodWithArg,
453+
wantErr: true,
454+
}, {
455+
name: "interface unpack error",
456+
mc: &mockCaller{
457+
codeAtBytes: []byte{0},
458+
},
459+
method: method,
460+
results: &[]interface{}{0},
461+
wantErr: true,
462+
}}
463+
for _, test := range tests {
464+
bc := bind.NewBoundContract(common.HexToAddress("0x0"), abi.ABI{
465+
Methods: map[string]abi.Method{
466+
method: {
467+
Name: method,
468+
Outputs: abi.Arguments{},
469+
},
470+
methodWithArg: {
471+
Name: methodWithArg,
472+
Outputs: abi.Arguments{abi.Argument{}},
473+
},
474+
},
475+
}, test.mc, nil, nil)
476+
err := bc.Call(test.opts, test.results, test.method)
477+
if test.wantErr || test.wantErrExact != nil {
478+
if err == nil {
479+
t.Fatalf("%q expected error", test.name)
480+
}
481+
if test.wantErrExact != nil && !errors.Is(err, test.wantErrExact) {
482+
t.Fatalf("%q expected error %q but got %q", test.name, test.wantErrExact, err)
483+
}
484+
continue
485+
}
486+
if err != nil {
487+
t.Fatalf("%q unexpected error: %v", test.name, err)
488+
}
489+
}
490+
}

0 commit comments

Comments
 (0)