mirror of
https://github.com/status-im/status-go.git
synced 2025-02-26 05:35:36 +00:00
fix(wallet): Fix block hash for detected blocks with ETH transfers
Part of #10251
This commit is contained in:
parent
ffc1006953
commit
da8e8716b2
@ -838,3 +838,26 @@ func (c *ClientWithFallback) GetBaseFeeFromBlock(blockNumber *big.Int) (string,
|
|||||||
|
|
||||||
return baseGasFee, err
|
return baseGasFee, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// go-ethereum's `Transaction` items drop the blkHash obtained during the RPC call.
|
||||||
|
// This function preserves the additional data. This is the cheapest way to obtain
|
||||||
|
// the block hash for a given block number.
|
||||||
|
func (c *ClientWithFallback) FullTransactionByBlockNumberAndIndex(ctx context.Context, blockNumber *big.Int, index uint) (*FullTransaction, error) {
|
||||||
|
rpcstats.CountCall("eth_FullTransactionByBlockNumberAndIndex")
|
||||||
|
|
||||||
|
tx, err := c.makeCallSingleReturn(
|
||||||
|
func() (any, error) {
|
||||||
|
return callFullTransactionByBlockNumberAndIndex(ctx, c.mainRPC, blockNumber, index)
|
||||||
|
},
|
||||||
|
func() (any, error) {
|
||||||
|
return callFullTransactionByBlockNumberAndIndex(ctx, c.fallbackRPC, blockNumber, index)
|
||||||
|
},
|
||||||
|
true,
|
||||||
|
)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return tx.(*FullTransaction), nil
|
||||||
|
}
|
||||||
|
108
rpc/chain/rpc.go
Normal file
108
rpc/chain/rpc.go
Normal file
@ -0,0 +1,108 @@
|
|||||||
|
package chain
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"math/big"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum"
|
||||||
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||||
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
|
"github.com/ethereum/go-ethereum/rpc"
|
||||||
|
)
|
||||||
|
|
||||||
|
// The code below is mostly copied from go-ethereum/ethclient (see TransactionInBlock), to keep the exact same behavior as the
|
||||||
|
// normal Transaction items, but exposing the additional data obtained in the `rpcTransaction` struct`.
|
||||||
|
// Unfortunately, the functions and classes used are not exposed outside of the package.
|
||||||
|
type FullTransaction struct {
|
||||||
|
Tx *types.Transaction
|
||||||
|
TxExtraInfo
|
||||||
|
}
|
||||||
|
|
||||||
|
type TxExtraInfo struct {
|
||||||
|
BlockNumber *hexutil.Big `json:"blockNumber,omitempty"`
|
||||||
|
BlockHash *common.Hash `json:"blockHash,omitempty"`
|
||||||
|
From *common.Address `json:"from,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func callFullTransactionByBlockNumberAndIndex(ctx context.Context, rpc *rpc.Client, number *big.Int, index uint) (*FullTransaction, error) {
|
||||||
|
var json *FullTransaction
|
||||||
|
err := rpc.CallContext(ctx, &json, "eth_getTransactionByBlockNumberAndIndex", toBlockNumArg(number), hexutil.Uint64(index))
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if json == nil {
|
||||||
|
return nil, ethereum.NotFound
|
||||||
|
} else if _, r, _ := json.Tx.RawSignatureValues(); r == nil {
|
||||||
|
return nil, fmt.Errorf("server returned transaction without signature")
|
||||||
|
}
|
||||||
|
if json.From != nil && json.BlockHash != nil {
|
||||||
|
setSenderFromServer(json.Tx, *json.From, *json.BlockHash)
|
||||||
|
}
|
||||||
|
return json, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tx *FullTransaction) UnmarshalJSON(msg []byte) error {
|
||||||
|
if err := json.Unmarshal(msg, &tx.Tx); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return json.Unmarshal(msg, &tx.TxExtraInfo)
|
||||||
|
}
|
||||||
|
|
||||||
|
func toBlockNumArg(number *big.Int) string {
|
||||||
|
if number == nil {
|
||||||
|
return "latest"
|
||||||
|
}
|
||||||
|
pending := big.NewInt(-1)
|
||||||
|
if number.Cmp(pending) == 0 {
|
||||||
|
return "pending"
|
||||||
|
}
|
||||||
|
finalized := big.NewInt(int64(rpc.FinalizedBlockNumber))
|
||||||
|
if number.Cmp(finalized) == 0 {
|
||||||
|
return "finalized"
|
||||||
|
}
|
||||||
|
safe := big.NewInt(int64(rpc.SafeBlockNumber))
|
||||||
|
if number.Cmp(safe) == 0 {
|
||||||
|
return "safe"
|
||||||
|
}
|
||||||
|
return hexutil.EncodeBig(number)
|
||||||
|
}
|
||||||
|
|
||||||
|
type senderFromServer struct {
|
||||||
|
addr common.Address
|
||||||
|
blockhash common.Hash
|
||||||
|
}
|
||||||
|
|
||||||
|
var errNotCached = errors.New("sender not cached")
|
||||||
|
|
||||||
|
//nolint:errcheck
|
||||||
|
func setSenderFromServer(tx *types.Transaction, addr common.Address, block common.Hash) {
|
||||||
|
// Use types.Sender for side-effect to store our signer into the cache.
|
||||||
|
types.Sender(&senderFromServer{addr, block}, tx)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *senderFromServer) Equal(other types.Signer) bool {
|
||||||
|
os, ok := other.(*senderFromServer)
|
||||||
|
return ok && os.blockhash == s.blockhash
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *senderFromServer) Sender(tx *types.Transaction) (common.Address, error) {
|
||||||
|
if s.addr == (common.Address{}) {
|
||||||
|
return common.Address{}, errNotCached
|
||||||
|
}
|
||||||
|
return s.addr, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *senderFromServer) ChainID() *big.Int {
|
||||||
|
panic("can't sign with senderFromServer")
|
||||||
|
}
|
||||||
|
func (s *senderFromServer) Hash(tx *types.Transaction) common.Hash {
|
||||||
|
panic("can't sign with senderFromServer")
|
||||||
|
}
|
||||||
|
func (s *senderFromServer) SignatureValues(tx *types.Transaction, sig []byte) (R, S, V *big.Int, err error) {
|
||||||
|
panic("can't sign with senderFromServer")
|
||||||
|
}
|
@ -173,11 +173,17 @@ func checkRangesWithStartBlock(parent context.Context, client BalanceReader, cac
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if new(big.Int).Sub(to, from).Cmp(one) == 0 {
|
if new(big.Int).Sub(to, from).Cmp(one) == 0 {
|
||||||
|
// WARNING: Block hash calculation from plain header returns a wrong value.
|
||||||
header, err := client.HeaderByNumber(ctx, to)
|
header, err := client.HeaderByNumber(ctx, to)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
c.PushHeader(toDBHeader(header))
|
// Obtain block hash from first transaction
|
||||||
|
firstTransaction, err := client.FullTransactionByBlockNumberAndIndex(ctx, to, 0)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
c.PushHeader(toDBHeader(header, *firstTransaction.BlockHash))
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
mid := new(big.Int).Add(from, to)
|
mid := new(big.Int).Add(from, to)
|
||||||
|
@ -8,8 +8,11 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/status-im/status-go/rpc/chain"
|
||||||
|
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
@ -75,6 +78,17 @@ func (f balancesFixture) HeaderByHash(ctx context.Context, hash common.Hash) (*t
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (f balancesFixture) FullTransactionByBlockNumberAndIndex(ctx context.Context, blockNumber *big.Int, index uint) (*chain.FullTransaction, error) {
|
||||||
|
blockHash := common.HexToHash("0x0")
|
||||||
|
return &chain.FullTransaction{
|
||||||
|
Tx: &types.Transaction{},
|
||||||
|
TxExtraInfo: chain.TxExtraInfo{
|
||||||
|
BlockNumber: (*hexutil.Big)(big.NewInt(0)),
|
||||||
|
BlockHash: &blockHash,
|
||||||
|
},
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
type batchesFixture [][]Transfer
|
type batchesFixture [][]Transfer
|
||||||
|
|
||||||
func (f batchesFixture) GetTransfersByNumber(ctx context.Context, number *big.Int) (rst []Transfer, err error) {
|
func (f batchesFixture) GetTransfersByNumber(ctx context.Context, number *big.Int) (rst []Transfer, err error) {
|
||||||
|
@ -29,9 +29,9 @@ type DBHeader struct {
|
|||||||
Loaded bool
|
Loaded bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func toDBHeader(header *types.Header) *DBHeader {
|
func toDBHeader(header *types.Header, blockHash common.Hash) *DBHeader {
|
||||||
return &DBHeader{
|
return &DBHeader{
|
||||||
Hash: header.Hash(),
|
Hash: blockHash,
|
||||||
Number: header.Number,
|
Number: header.Number,
|
||||||
Timestamp: header.Time,
|
Timestamp: header.Time,
|
||||||
Loaded: false,
|
Loaded: false,
|
||||||
|
@ -43,6 +43,7 @@ type BalanceReader interface {
|
|||||||
BalanceAt(ctx context.Context, account common.Address, blockNumber *big.Int) (*big.Int, error)
|
BalanceAt(ctx context.Context, account common.Address, blockNumber *big.Int) (*big.Int, error)
|
||||||
NonceAt(ctx context.Context, account common.Address, blockNumber *big.Int) (uint64, error)
|
NonceAt(ctx context.Context, account common.Address, blockNumber *big.Int) (uint64, error)
|
||||||
HeaderByNumber(ctx context.Context, number *big.Int) (*types.Header, error)
|
HeaderByNumber(ctx context.Context, number *big.Int) (*types.Header, error)
|
||||||
|
FullTransactionByBlockNumberAndIndex(ctx context.Context, blockNumber *big.Int, index uint) (*chain.FullTransaction, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type HistoryFetcher interface {
|
type HistoryFetcher interface {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user