forked from cerc-io/plugeth
		
	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 <fjl@twurst.com>
This commit is contained in:
		
							parent
							
								
									fa96718512
								
							
						
					
					
						commit
						16341e0563
					
				| @ -233,6 +233,8 @@ func (ec *Client) TransactionSender(ctx context.Context, tx *types.Transaction, | |||||||
| 	if err == nil { | 	if err == nil { | ||||||
| 		return sender, nil | 		return sender, nil | ||||||
| 	} | 	} | ||||||
|  | 
 | ||||||
|  | 	// It was not found in cache, ask the server.
 | ||||||
| 	var meta struct { | 	var meta struct { | ||||||
| 		Hash common.Hash | 		Hash common.Hash | ||||||
| 		From common.Address | 		From common.Address | ||||||
|  | |||||||
| @ -187,9 +187,34 @@ var ( | |||||||
| 	testBalance = big.NewInt(2e15) | 	testBalance = big.NewInt(2e15) | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
|  | var genesis = &core.Genesis{ | ||||||
|  | 	Config:    params.AllEthashProtocolChanges, | ||||||
|  | 	Alloc:     core.GenesisAlloc{testAddr: {Balance: testBalance}}, | ||||||
|  | 	ExtraData: []byte("test genesis"), | ||||||
|  | 	Timestamp: 9000, | ||||||
|  | 	BaseFee:   big.NewInt(params.InitialBaseFee), | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | var testTx1 = types.MustSignNewTx(testKey, types.LatestSigner(genesis.Config), &types.LegacyTx{ | ||||||
|  | 	Nonce:    0, | ||||||
|  | 	Value:    big.NewInt(12), | ||||||
|  | 	GasPrice: big.NewInt(params.InitialBaseFee), | ||||||
|  | 	Gas:      params.TxGas, | ||||||
|  | 	To:       &common.Address{2}, | ||||||
|  | }) | ||||||
|  | 
 | ||||||
|  | var testTx2 = types.MustSignNewTx(testKey, types.LatestSigner(genesis.Config), &types.LegacyTx{ | ||||||
|  | 	Nonce:    1, | ||||||
|  | 	Value:    big.NewInt(8), | ||||||
|  | 	GasPrice: big.NewInt(params.InitialBaseFee), | ||||||
|  | 	Gas:      params.TxGas, | ||||||
|  | 	To:       &common.Address{2}, | ||||||
|  | }) | ||||||
|  | 
 | ||||||
| func newTestBackend(t *testing.T) (*node.Node, []*types.Block) { | func newTestBackend(t *testing.T) (*node.Node, []*types.Block) { | ||||||
| 	// Generate test chain.
 | 	// Generate test chain.
 | ||||||
| 	genesis, blocks := generateTestChain() | 	blocks := generateTestChain() | ||||||
|  | 
 | ||||||
| 	// Create node
 | 	// Create node
 | ||||||
| 	n, err := node.New(&node.Config{}) | 	n, err := node.New(&node.Config{}) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| @ -212,25 +237,22 @@ func newTestBackend(t *testing.T) (*node.Node, []*types.Block) { | |||||||
| 	return n, blocks | 	return n, blocks | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func generateTestChain() (*core.Genesis, []*types.Block) { | func generateTestChain() []*types.Block { | ||||||
| 	db := rawdb.NewMemoryDatabase() | 	db := rawdb.NewMemoryDatabase() | ||||||
| 	config := params.AllEthashProtocolChanges |  | ||||||
| 	genesis := &core.Genesis{ |  | ||||||
| 		Config:    config, |  | ||||||
| 		Alloc:     core.GenesisAlloc{testAddr: {Balance: testBalance}}, |  | ||||||
| 		ExtraData: []byte("test genesis"), |  | ||||||
| 		Timestamp: 9000, |  | ||||||
| 		BaseFee:   big.NewInt(params.InitialBaseFee), |  | ||||||
| 	} |  | ||||||
| 	generate := func(i int, g *core.BlockGen) { | 	generate := func(i int, g *core.BlockGen) { | ||||||
| 		g.OffsetTime(5) | 		g.OffsetTime(5) | ||||||
| 		g.SetExtra([]byte("test")) | 		g.SetExtra([]byte("test")) | ||||||
|  | 		if i == 1 { | ||||||
|  | 			// Test transactions are included in block #2.
 | ||||||
|  | 			g.AddTx(testTx1) | ||||||
|  | 			g.AddTx(testTx2) | ||||||
|  | 		} | ||||||
| 	} | 	} | ||||||
| 	gblock := genesis.ToBlock(db) | 	gblock := genesis.ToBlock(db) | ||||||
| 	engine := ethash.NewFaker() | 	engine := ethash.NewFaker() | ||||||
| 	blocks, _ := core.GenerateChain(config, gblock, engine, db, 1, generate) | 	blocks, _ := core.GenerateChain(genesis.Config, gblock, engine, db, 2, generate) | ||||||
| 	blocks = append([]*types.Block{gblock}, blocks...) | 	blocks = append([]*types.Block{gblock}, blocks...) | ||||||
| 	return genesis, blocks | 	return blocks | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func TestEthClient(t *testing.T) { | func TestEthClient(t *testing.T) { | ||||||
| @ -242,30 +264,33 @@ func TestEthClient(t *testing.T) { | |||||||
| 	tests := map[string]struct { | 	tests := map[string]struct { | ||||||
| 		test func(t *testing.T) | 		test func(t *testing.T) | ||||||
| 	}{ | 	}{ | ||||||
| 		"TestHeader": { | 		"Header": { | ||||||
| 			func(t *testing.T) { testHeader(t, chain, client) }, | 			func(t *testing.T) { testHeader(t, chain, client) }, | ||||||
| 		}, | 		}, | ||||||
| 		"TestBalanceAt": { | 		"BalanceAt": { | ||||||
| 			func(t *testing.T) { testBalanceAt(t, client) }, | 			func(t *testing.T) { testBalanceAt(t, client) }, | ||||||
| 		}, | 		}, | ||||||
| 		"TestTxInBlockInterrupted": { | 		"TxInBlockInterrupted": { | ||||||
| 			func(t *testing.T) { testTransactionInBlockInterrupted(t, client) }, | 			func(t *testing.T) { testTransactionInBlockInterrupted(t, client) }, | ||||||
| 		}, | 		}, | ||||||
| 		"TestChainID": { | 		"ChainID": { | ||||||
| 			func(t *testing.T) { testChainID(t, client) }, | 			func(t *testing.T) { testChainID(t, client) }, | ||||||
| 		}, | 		}, | ||||||
| 		"TestGetBlock": { | 		"GetBlock": { | ||||||
| 			func(t *testing.T) { testGetBlock(t, client) }, | 			func(t *testing.T) { testGetBlock(t, client) }, | ||||||
| 		}, | 		}, | ||||||
| 		"TestStatusFunctions": { | 		"StatusFunctions": { | ||||||
| 			func(t *testing.T) { testStatusFunctions(t, client) }, | 			func(t *testing.T) { testStatusFunctions(t, client) }, | ||||||
| 		}, | 		}, | ||||||
| 		"TestCallContract": { | 		"CallContract": { | ||||||
| 			func(t *testing.T) { testCallContract(t, client) }, | 			func(t *testing.T) { testCallContract(t, client) }, | ||||||
| 		}, | 		}, | ||||||
| 		"TestAtFunctions": { | 		"AtFunctions": { | ||||||
| 			func(t *testing.T) { testAtFunctions(t, client) }, | 			func(t *testing.T) { testAtFunctions(t, client) }, | ||||||
| 		}, | 		}, | ||||||
|  | 		"TransactionSender": { | ||||||
|  | 			func(t *testing.T) { testTransactionSender(t, client) }, | ||||||
|  | 		}, | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	t.Parallel() | 	t.Parallel() | ||||||
| @ -321,6 +346,11 @@ func testBalanceAt(t *testing.T, client *rpc.Client) { | |||||||
| 		want    *big.Int | 		want    *big.Int | ||||||
| 		wantErr error | 		wantErr error | ||||||
| 	}{ | 	}{ | ||||||
|  | 		"valid_account_genesis": { | ||||||
|  | 			account: testAddr, | ||||||
|  | 			block:   big.NewInt(0), | ||||||
|  | 			want:    testBalance, | ||||||
|  | 		}, | ||||||
| 		"valid_account": { | 		"valid_account": { | ||||||
| 			account: testAddr, | 			account: testAddr, | ||||||
| 			block:   big.NewInt(1), | 			block:   big.NewInt(1), | ||||||
| @ -358,23 +388,25 @@ func testBalanceAt(t *testing.T, client *rpc.Client) { | |||||||
| func testTransactionInBlockInterrupted(t *testing.T, client *rpc.Client) { | func testTransactionInBlockInterrupted(t *testing.T, client *rpc.Client) { | ||||||
| 	ec := NewClient(client) | 	ec := NewClient(client) | ||||||
| 
 | 
 | ||||||
| 	// Get current block by number
 | 	// Get current block by number.
 | ||||||
| 	block, err := ec.BlockByNumber(context.Background(), nil) | 	block, err := ec.BlockByNumber(context.Background(), nil) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		t.Fatalf("unexpected error: %v", err) | 		t.Fatalf("unexpected error: %v", err) | ||||||
| 	} | 	} | ||||||
| 	// Test tx in block interupted
 | 
 | ||||||
|  | 	// Test tx in block interupted.
 | ||||||
| 	ctx, cancel := context.WithCancel(context.Background()) | 	ctx, cancel := context.WithCancel(context.Background()) | ||||||
| 	cancel() | 	cancel() | ||||||
| 	tx, err := ec.TransactionInBlock(ctx, block.Hash(), 1) | 	tx, err := ec.TransactionInBlock(ctx, block.Hash(), 0) | ||||||
| 	if tx != nil { | 	if tx != nil { | ||||||
| 		t.Fatal("transaction should be nil") | 		t.Fatal("transaction should be nil") | ||||||
| 	} | 	} | ||||||
| 	if err == nil || err == ethereum.NotFound { | 	if err == nil || err == ethereum.NotFound { | ||||||
| 		t.Fatal("error should not be nil/notfound") | 		t.Fatal("error should not be nil/notfound") | ||||||
| 	} | 	} | ||||||
| 	// Test tx in block not found
 | 
 | ||||||
| 	if _, err := ec.TransactionInBlock(context.Background(), block.Hash(), 1); err != ethereum.NotFound { | 	// Test tx in block not found.
 | ||||||
|  | 	if _, err := ec.TransactionInBlock(context.Background(), block.Hash(), 20); err != ethereum.NotFound { | ||||||
| 		t.Fatal("error should be ethereum.NotFound") | 		t.Fatal("error should be ethereum.NotFound") | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| @ -392,12 +424,13 @@ func testChainID(t *testing.T, client *rpc.Client) { | |||||||
| 
 | 
 | ||||||
| func testGetBlock(t *testing.T, client *rpc.Client) { | func testGetBlock(t *testing.T, client *rpc.Client) { | ||||||
| 	ec := NewClient(client) | 	ec := NewClient(client) | ||||||
|  | 
 | ||||||
| 	// Get current block number
 | 	// Get current block number
 | ||||||
| 	blockNumber, err := ec.BlockNumber(context.Background()) | 	blockNumber, err := ec.BlockNumber(context.Background()) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		t.Fatalf("unexpected error: %v", err) | 		t.Fatalf("unexpected error: %v", err) | ||||||
| 	} | 	} | ||||||
| 	if blockNumber != 1 { | 	if blockNumber != 2 { | ||||||
| 		t.Fatalf("BlockNumber returned wrong number: %d", blockNumber) | 		t.Fatalf("BlockNumber returned wrong number: %d", blockNumber) | ||||||
| 	} | 	} | ||||||
| 	// Get current block by number
 | 	// Get current block by number
 | ||||||
| @ -445,6 +478,7 @@ func testStatusFunctions(t *testing.T, client *rpc.Client) { | |||||||
| 	if progress != nil { | 	if progress != nil { | ||||||
| 		t.Fatalf("unexpected progress: %v", progress) | 		t.Fatalf("unexpected progress: %v", progress) | ||||||
| 	} | 	} | ||||||
|  | 
 | ||||||
| 	// NetworkID
 | 	// NetworkID
 | ||||||
| 	networkID, err := ec.NetworkID(context.Background()) | 	networkID, err := ec.NetworkID(context.Background()) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| @ -453,20 +487,22 @@ func testStatusFunctions(t *testing.T, client *rpc.Client) { | |||||||
| 	if networkID.Cmp(big.NewInt(0)) != 0 { | 	if networkID.Cmp(big.NewInt(0)) != 0 { | ||||||
| 		t.Fatalf("unexpected networkID: %v", networkID) | 		t.Fatalf("unexpected networkID: %v", networkID) | ||||||
| 	} | 	} | ||||||
| 	// SuggestGasPrice (should suggest 1 Gwei)
 | 
 | ||||||
|  | 	// SuggestGasPrice
 | ||||||
| 	gasPrice, err := ec.SuggestGasPrice(context.Background()) | 	gasPrice, err := ec.SuggestGasPrice(context.Background()) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		t.Fatalf("unexpected error: %v", err) | 		t.Fatalf("unexpected error: %v", err) | ||||||
| 	} | 	} | ||||||
| 	if gasPrice.Cmp(big.NewInt(1875000000)) != 0 { // 1 gwei tip + 0.875 basefee after a 1 gwei fee empty block
 | 	if gasPrice.Cmp(big.NewInt(1000000000)) != 0 { | ||||||
| 		t.Fatalf("unexpected gas price: %v", gasPrice) | 		t.Fatalf("unexpected gas price: %v", gasPrice) | ||||||
| 	} | 	} | ||||||
| 	// SuggestGasTipCap (should suggest 1 Gwei)
 | 
 | ||||||
|  | 	// SuggestGasTipCap
 | ||||||
| 	gasTipCap, err := ec.SuggestGasTipCap(context.Background()) | 	gasTipCap, err := ec.SuggestGasTipCap(context.Background()) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		t.Fatalf("unexpected error: %v", err) | 		t.Fatalf("unexpected error: %v", err) | ||||||
| 	} | 	} | ||||||
| 	if gasTipCap.Cmp(big.NewInt(1000000000)) != 0 { | 	if gasTipCap.Cmp(big.NewInt(234375000)) != 0 { | ||||||
| 		t.Fatalf("unexpected gas tip cap: %v", gasTipCap) | 		t.Fatalf("unexpected gas tip cap: %v", gasTipCap) | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| @ -500,9 +536,11 @@ func testCallContract(t *testing.T, client *rpc.Client) { | |||||||
| 
 | 
 | ||||||
| func testAtFunctions(t *testing.T, client *rpc.Client) { | func testAtFunctions(t *testing.T, client *rpc.Client) { | ||||||
| 	ec := NewClient(client) | 	ec := NewClient(client) | ||||||
|  | 
 | ||||||
| 	// send a transaction for some interesting pending status
 | 	// send a transaction for some interesting pending status
 | ||||||
| 	sendTransaction(ec) | 	sendTransaction(ec) | ||||||
| 	time.Sleep(100 * time.Millisecond) | 	time.Sleep(100 * time.Millisecond) | ||||||
|  | 
 | ||||||
| 	// Check pending transaction count
 | 	// Check pending transaction count
 | ||||||
| 	pending, err := ec.PendingTransactionCount(context.Background()) | 	pending, err := ec.PendingTransactionCount(context.Background()) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| @ -561,23 +599,66 @@ func testAtFunctions(t *testing.T, client *rpc.Client) { | |||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | func testTransactionSender(t *testing.T, client *rpc.Client) { | ||||||
|  | 	ec := NewClient(client) | ||||||
|  | 	ctx := context.Background() | ||||||
|  | 
 | ||||||
|  | 	// Retrieve testTx1 via RPC.
 | ||||||
|  | 	block2, err := ec.HeaderByNumber(ctx, big.NewInt(2)) | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.Fatal("can't get block 1:", err) | ||||||
|  | 	} | ||||||
|  | 	tx1, err := ec.TransactionInBlock(ctx, block2.Hash(), 0) | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.Fatal("can't get tx:", err) | ||||||
|  | 	} | ||||||
|  | 	if tx1.Hash() != testTx1.Hash() { | ||||||
|  | 		t.Fatalf("wrong tx hash %v, want %v", tx1.Hash(), testTx1.Hash()) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	// The sender address is cached in tx1, so no additional RPC should be required in
 | ||||||
|  | 	// TransactionSender. Ensure the server is not asked by canceling the context here.
 | ||||||
|  | 	canceledCtx, cancel := context.WithCancel(context.Background()) | ||||||
|  | 	cancel() | ||||||
|  | 	sender1, err := ec.TransactionSender(canceledCtx, tx1, block2.Hash(), 0) | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.Fatal(err) | ||||||
|  | 	} | ||||||
|  | 	if sender1 != testAddr { | ||||||
|  | 		t.Fatal("wrong sender:", sender1) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	// Now try to get the sender of testTx2, which was not fetched through RPC.
 | ||||||
|  | 	// TransactionSender should query the server here.
 | ||||||
|  | 	sender2, err := ec.TransactionSender(ctx, testTx2, block2.Hash(), 1) | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.Fatal(err) | ||||||
|  | 	} | ||||||
|  | 	if sender2 != testAddr { | ||||||
|  | 		t.Fatal("wrong sender:", sender2) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
| func sendTransaction(ec *Client) error { | func sendTransaction(ec *Client) error { | ||||||
| 	// Retrieve chainID
 |  | ||||||
| 	chainID, err := ec.ChainID(context.Background()) | 	chainID, err := ec.ChainID(context.Background()) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
| 	// Create transaction
 | 	nonce, err := ec.PendingNonceAt(context.Background(), testAddr) | ||||||
| 	tx := types.NewTransaction(0, common.Address{1}, big.NewInt(1), 22000, big.NewInt(params.InitialBaseFee), nil) | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	signer := types.LatestSignerForChainID(chainID) | 	signer := types.LatestSignerForChainID(chainID) | ||||||
| 	signature, err := crypto.Sign(signer.Hash(tx).Bytes(), testKey) | 	tx, err := types.SignNewTx(testKey, signer, &types.LegacyTx{ | ||||||
|  | 		Nonce:    nonce, | ||||||
|  | 		To:       &common.Address{2}, | ||||||
|  | 		Value:    big.NewInt(1), | ||||||
|  | 		Gas:      22000, | ||||||
|  | 		GasPrice: big.NewInt(params.InitialBaseFee), | ||||||
|  | 	}) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
| 	signedTx, err := tx.WithSignature(signer, signature) | 	return ec.SendTransaction(context.Background(), tx) | ||||||
| 	if err != nil { |  | ||||||
| 		return err |  | ||||||
| 	} |  | ||||||
| 	// Send transaction
 |  | ||||||
| 	return ec.SendTransaction(context.Background(), signedTx) |  | ||||||
| } | } | ||||||
|  | |||||||
| @ -45,7 +45,7 @@ func (s *senderFromServer) Equal(other types.Signer) bool { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (s *senderFromServer) Sender(tx *types.Transaction) (common.Address, error) { | func (s *senderFromServer) Sender(tx *types.Transaction) (common.Address, error) { | ||||||
| 	if s.blockhash == (common.Hash{}) { | 	if s.addr == (common.Address{}) { | ||||||
| 		return common.Address{}, errNotCached | 		return common.Address{}, errNotCached | ||||||
| 	} | 	} | ||||||
| 	return s.addr, nil | 	return s.addr, nil | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user