Skip to content

Commit 5f0c85c

Browse files
PlasmaPowerfjl
authored andcommitted
ethclient: fix tx sender cache miss detection (#23877)
This fixes a bug in TransactionSender where it would return the zero address for transactions where the sender address wasn't cached already. Co-authored-by: Felix Lange <[email protected]>
1 parent b4a8596 commit 5f0c85c

File tree

3 files changed

+122
-39
lines changed

3 files changed

+122
-39
lines changed

ethclient/ethclient.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -233,6 +233,8 @@ func (ec *Client) TransactionSender(ctx context.Context, tx *types.Transaction,
233233
if err == nil {
234234
return sender, nil
235235
}
236+
237+
// It was not found in cache, ask the server.
236238
var meta struct {
237239
Hash common.Hash
238240
From common.Address

ethclient/ethclient_test.go

Lines changed: 119 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -187,9 +187,34 @@ var (
187187
testBalance = big.NewInt(2e15)
188188
)
189189

190+
var genesis = &core.Genesis{
191+
Config: params.AllEthashProtocolChanges,
192+
Alloc: core.GenesisAlloc{testAddr: {Balance: testBalance}},
193+
ExtraData: []byte("test genesis"),
194+
Timestamp: 9000,
195+
BaseFee: big.NewInt(params.InitialBaseFee),
196+
}
197+
198+
var testTx1 = types.MustSignNewTx(testKey, types.LatestSigner(genesis.Config), &types.LegacyTx{
199+
Nonce: 0,
200+
Value: big.NewInt(12),
201+
GasPrice: big.NewInt(params.InitialBaseFee),
202+
Gas: params.TxGas,
203+
To: &common.Address{2},
204+
})
205+
206+
var testTx2 = types.MustSignNewTx(testKey, types.LatestSigner(genesis.Config), &types.LegacyTx{
207+
Nonce: 1,
208+
Value: big.NewInt(8),
209+
GasPrice: big.NewInt(params.InitialBaseFee),
210+
Gas: params.TxGas,
211+
To: &common.Address{2},
212+
})
213+
190214
func newTestBackend(t *testing.T) (*node.Node, []*types.Block) {
191215
// Generate test chain.
192-
genesis, blocks := generateTestChain()
216+
blocks := generateTestChain()
217+
193218
// Create node
194219
n, err := node.New(&node.Config{})
195220
if err != nil {
@@ -212,25 +237,22 @@ func newTestBackend(t *testing.T) (*node.Node, []*types.Block) {
212237
return n, blocks
213238
}
214239

215-
func generateTestChain() (*core.Genesis, []*types.Block) {
240+
func generateTestChain() []*types.Block {
216241
db := rawdb.NewMemoryDatabase()
217-
config := params.AllEthashProtocolChanges
218-
genesis := &core.Genesis{
219-
Config: config,
220-
Alloc: core.GenesisAlloc{testAddr: {Balance: testBalance}},
221-
ExtraData: []byte("test genesis"),
222-
Timestamp: 9000,
223-
BaseFee: big.NewInt(params.InitialBaseFee),
224-
}
225242
generate := func(i int, g *core.BlockGen) {
226243
g.OffsetTime(5)
227244
g.SetExtra([]byte("test"))
245+
if i == 1 {
246+
// Test transactions are included in block #2.
247+
g.AddTx(testTx1)
248+
g.AddTx(testTx2)
249+
}
228250
}
229251
gblock := genesis.ToBlock(db)
230252
engine := ethash.NewFaker()
231-
blocks, _ := core.GenerateChain(config, gblock, engine, db, 1, generate)
253+
blocks, _ := core.GenerateChain(genesis.Config, gblock, engine, db, 2, generate)
232254
blocks = append([]*types.Block{gblock}, blocks...)
233-
return genesis, blocks
255+
return blocks
234256
}
235257

236258
func TestEthClient(t *testing.T) {
@@ -242,30 +264,33 @@ func TestEthClient(t *testing.T) {
242264
tests := map[string]struct {
243265
test func(t *testing.T)
244266
}{
245-
"TestHeader": {
267+
"Header": {
246268
func(t *testing.T) { testHeader(t, chain, client) },
247269
},
248-
"TestBalanceAt": {
270+
"BalanceAt": {
249271
func(t *testing.T) { testBalanceAt(t, client) },
250272
},
251-
"TestTxInBlockInterrupted": {
273+
"TxInBlockInterrupted": {
252274
func(t *testing.T) { testTransactionInBlockInterrupted(t, client) },
253275
},
254-
"TestChainID": {
276+
"ChainID": {
255277
func(t *testing.T) { testChainID(t, client) },
256278
},
257-
"TestGetBlock": {
279+
"GetBlock": {
258280
func(t *testing.T) { testGetBlock(t, client) },
259281
},
260-
"TestStatusFunctions": {
282+
"StatusFunctions": {
261283
func(t *testing.T) { testStatusFunctions(t, client) },
262284
},
263-
"TestCallContract": {
285+
"CallContract": {
264286
func(t *testing.T) { testCallContract(t, client) },
265287
},
266-
"TestAtFunctions": {
288+
"AtFunctions": {
267289
func(t *testing.T) { testAtFunctions(t, client) },
268290
},
291+
"TransactionSender": {
292+
func(t *testing.T) { testTransactionSender(t, client) },
293+
},
269294
}
270295

271296
t.Parallel()
@@ -321,6 +346,11 @@ func testBalanceAt(t *testing.T, client *rpc.Client) {
321346
want *big.Int
322347
wantErr error
323348
}{
349+
"valid_account_genesis": {
350+
account: testAddr,
351+
block: big.NewInt(0),
352+
want: testBalance,
353+
},
324354
"valid_account": {
325355
account: testAddr,
326356
block: big.NewInt(1),
@@ -358,23 +388,25 @@ func testBalanceAt(t *testing.T, client *rpc.Client) {
358388
func testTransactionInBlockInterrupted(t *testing.T, client *rpc.Client) {
359389
ec := NewClient(client)
360390

361-
// Get current block by number
391+
// Get current block by number.
362392
block, err := ec.BlockByNumber(context.Background(), nil)
363393
if err != nil {
364394
t.Fatalf("unexpected error: %v", err)
365395
}
366-
// Test tx in block interupted
396+
397+
// Test tx in block interupted.
367398
ctx, cancel := context.WithCancel(context.Background())
368399
cancel()
369-
tx, err := ec.TransactionInBlock(ctx, block.Hash(), 1)
400+
tx, err := ec.TransactionInBlock(ctx, block.Hash(), 0)
370401
if tx != nil {
371402
t.Fatal("transaction should be nil")
372403
}
373404
if err == nil || err == ethereum.NotFound {
374405
t.Fatal("error should not be nil/notfound")
375406
}
376-
// Test tx in block not found
377-
if _, err := ec.TransactionInBlock(context.Background(), block.Hash(), 1); err != ethereum.NotFound {
407+
408+
// Test tx in block not found.
409+
if _, err := ec.TransactionInBlock(context.Background(), block.Hash(), 20); err != ethereum.NotFound {
378410
t.Fatal("error should be ethereum.NotFound")
379411
}
380412
}
@@ -392,12 +424,13 @@ func testChainID(t *testing.T, client *rpc.Client) {
392424

393425
func testGetBlock(t *testing.T, client *rpc.Client) {
394426
ec := NewClient(client)
427+
395428
// Get current block number
396429
blockNumber, err := ec.BlockNumber(context.Background())
397430
if err != nil {
398431
t.Fatalf("unexpected error: %v", err)
399432
}
400-
if blockNumber != 1 {
433+
if blockNumber != 2 {
401434
t.Fatalf("BlockNumber returned wrong number: %d", blockNumber)
402435
}
403436
// Get current block by number
@@ -445,6 +478,7 @@ func testStatusFunctions(t *testing.T, client *rpc.Client) {
445478
if progress != nil {
446479
t.Fatalf("unexpected progress: %v", progress)
447480
}
481+
448482
// NetworkID
449483
networkID, err := ec.NetworkID(context.Background())
450484
if err != nil {
@@ -453,20 +487,22 @@ func testStatusFunctions(t *testing.T, client *rpc.Client) {
453487
if networkID.Cmp(big.NewInt(0)) != 0 {
454488
t.Fatalf("unexpected networkID: %v", networkID)
455489
}
456-
// SuggestGasPrice (should suggest 1 Gwei)
490+
491+
// SuggestGasPrice
457492
gasPrice, err := ec.SuggestGasPrice(context.Background())
458493
if err != nil {
459494
t.Fatalf("unexpected error: %v", err)
460495
}
461-
if gasPrice.Cmp(big.NewInt(1875000000)) != 0 { // 1 gwei tip + 0.875 basefee after a 1 gwei fee empty block
496+
if gasPrice.Cmp(big.NewInt(1000000000)) != 0 {
462497
t.Fatalf("unexpected gas price: %v", gasPrice)
463498
}
464-
// SuggestGasTipCap (should suggest 1 Gwei)
499+
500+
// SuggestGasTipCap
465501
gasTipCap, err := ec.SuggestGasTipCap(context.Background())
466502
if err != nil {
467503
t.Fatalf("unexpected error: %v", err)
468504
}
469-
if gasTipCap.Cmp(big.NewInt(1000000000)) != 0 {
505+
if gasTipCap.Cmp(big.NewInt(234375000)) != 0 {
470506
t.Fatalf("unexpected gas tip cap: %v", gasTipCap)
471507
}
472508
}
@@ -500,9 +536,11 @@ func testCallContract(t *testing.T, client *rpc.Client) {
500536

501537
func testAtFunctions(t *testing.T, client *rpc.Client) {
502538
ec := NewClient(client)
539+
503540
// send a transaction for some interesting pending status
504541
sendTransaction(ec)
505542
time.Sleep(100 * time.Millisecond)
543+
506544
// Check pending transaction count
507545
pending, err := ec.PendingTransactionCount(context.Background())
508546
if err != nil {
@@ -561,23 +599,66 @@ func testAtFunctions(t *testing.T, client *rpc.Client) {
561599
}
562600
}
563601

602+
func testTransactionSender(t *testing.T, client *rpc.Client) {
603+
ec := NewClient(client)
604+
ctx := context.Background()
605+
606+
// Retrieve testTx1 via RPC.
607+
block2, err := ec.HeaderByNumber(ctx, big.NewInt(2))
608+
if err != nil {
609+
t.Fatal("can't get block 1:", err)
610+
}
611+
tx1, err := ec.TransactionInBlock(ctx, block2.Hash(), 0)
612+
if err != nil {
613+
t.Fatal("can't get tx:", err)
614+
}
615+
if tx1.Hash() != testTx1.Hash() {
616+
t.Fatalf("wrong tx hash %v, want %v", tx1.Hash(), testTx1.Hash())
617+
}
618+
619+
// The sender address is cached in tx1, so no additional RPC should be required in
620+
// TransactionSender. Ensure the server is not asked by canceling the context here.
621+
canceledCtx, cancel := context.WithCancel(context.Background())
622+
cancel()
623+
sender1, err := ec.TransactionSender(canceledCtx, tx1, block2.Hash(), 0)
624+
if err != nil {
625+
t.Fatal(err)
626+
}
627+
if sender1 != testAddr {
628+
t.Fatal("wrong sender:", sender1)
629+
}
630+
631+
// Now try to get the sender of testTx2, which was not fetched through RPC.
632+
// TransactionSender should query the server here.
633+
sender2, err := ec.TransactionSender(ctx, testTx2, block2.Hash(), 1)
634+
if err != nil {
635+
t.Fatal(err)
636+
}
637+
if sender2 != testAddr {
638+
t.Fatal("wrong sender:", sender2)
639+
}
640+
}
641+
564642
func sendTransaction(ec *Client) error {
565-
// Retrieve chainID
566643
chainID, err := ec.ChainID(context.Background())
567644
if err != nil {
568645
return err
569646
}
570-
// Create transaction
571-
tx := types.NewTransaction(0, common.Address{1}, big.NewInt(1), 22000, big.NewInt(params.InitialBaseFee), nil)
572-
signer := types.LatestSignerForChainID(chainID)
573-
signature, err := crypto.Sign(signer.Hash(tx).Bytes(), testKey)
647+
nonce, err := ec.PendingNonceAt(context.Background(), testAddr)
574648
if err != nil {
575649
return err
576650
}
577-
signedTx, err := tx.WithSignature(signer, signature)
651+
652+
signer := types.LatestSignerForChainID(chainID)
653+
tx, err := types.SignNewTx(testKey, signer, &types.LegacyTx{
654+
Nonce: nonce,
655+
To: &common.Address{2},
656+
Value: big.NewInt(1),
657+
Gas: 22000,
658+
GasPrice: big.NewInt(params.InitialBaseFee),
659+
})
578660
if err != nil {
579661
return err
580662
}
581-
// Send transaction
582-
return ec.SendTransaction(context.Background(), signedTx)
663+
return ec.SendTransaction(context.Background(), tx)
583664
}

ethclient/signer.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ func (s *senderFromServer) Equal(other types.Signer) bool {
4545
}
4646

4747
func (s *senderFromServer) Sender(tx *types.Transaction) (common.Address, error) {
48-
if s.blockhash == (common.Hash{}) {
48+
if s.addr == (common.Address{}) {
4949
return common.Address{}, errNotCached
5050
}
5151
return s.addr, nil

0 commit comments

Comments
 (0)