mirror of
https://github.com/status-im/status-go.git
synced 2025-02-18 17:57:28 +00:00
feat_: added eth client cache for nonce and balance
This commit is contained in:
parent
f263fdfad7
commit
ca5e93f778
@ -275,3 +275,71 @@ func (c *CachedEthClient) TransactionReceipt(ctx context.Context, hash common.Ha
|
||||
|
||||
return r, nil
|
||||
}
|
||||
|
||||
func (c *CachedEthClient) callGetBalance(ctx context.Context, account common.Address, blockNumber *big.Int) (*big.Int, error) {
|
||||
var result *hexutil.Big
|
||||
err := c.CallContext(ctx, &result, "eth_getBalance", account, toBlockNumArg(blockNumber))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return result.ToInt(), nil
|
||||
}
|
||||
|
||||
func (c *CachedEthClient) BalanceAt(ctx context.Context, account common.Address, blockNumber *big.Int) (*big.Int, error) {
|
||||
var result *big.Int
|
||||
|
||||
cacheValid := true
|
||||
result, err := c.storage.GetBalance(account, blockNumber)
|
||||
if err != nil && err != sql.ErrNoRows {
|
||||
return nil, err
|
||||
} else if err == sql.ErrNoRows {
|
||||
cacheValid = false
|
||||
result, err = c.callGetBalance(ctx, account, blockNumber)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if !cacheValid && isConcreteBlockNumber(blockNumber) {
|
||||
if err := c.storage.PutBalance(account, blockNumber, result); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (c *CachedEthClient) callGetTransactionCount(ctx context.Context, account common.Address, blockNumber *big.Int) (uint64, error) {
|
||||
var result hexutil.Uint64
|
||||
err := c.CallContext(ctx, &result, "eth_getTransactionCount", account, toBlockNumArg(blockNumber))
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return uint64(result), nil
|
||||
}
|
||||
|
||||
func (c *CachedEthClient) NonceAt(ctx context.Context, account common.Address, blockNumber *big.Int) (uint64, error) {
|
||||
var result uint64
|
||||
|
||||
cacheValid := true
|
||||
result, err := c.storage.GetTransactionCount(account, blockNumber)
|
||||
if err != nil && err != sql.ErrNoRows {
|
||||
return 0, err
|
||||
} else if err == sql.ErrNoRows {
|
||||
cacheValid = false
|
||||
result, err = c.callGetTransactionCount(ctx, account, blockNumber)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
}
|
||||
|
||||
if !cacheValid && isConcreteBlockNumber(blockNumber) {
|
||||
if err := c.storage.PutTransactionCount(account, blockNumber, result); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
@ -23,6 +23,8 @@ type EthClientStorageReader interface {
|
||||
GetBlockUncleJSONByHashAndIndex(chainID uint64, blockHash common.Hash, index uint64) (json.RawMessage, error)
|
||||
GetTransactionJSONByHash(chainID uint64, transactionHash common.Hash) (json.RawMessage, error)
|
||||
GetTransactionReceiptJSONByHash(chainID uint64, transactionHash common.Hash) (json.RawMessage, error)
|
||||
GetBalance(chainID uint64, account common.Address, blockNumber *big.Int) (*big.Int, error)
|
||||
GetTransactionCount(chainID uint64, account common.Address, blockNumber *big.Int) (uint64, error)
|
||||
}
|
||||
|
||||
type EthClientStorageWriter interface {
|
||||
@ -30,6 +32,8 @@ type EthClientStorageWriter interface {
|
||||
PutBlockUnclesJSON(chainID uint64, blockHash common.Hash, unclesJSON []json.RawMessage) error
|
||||
PutTransactionsJSON(chainID uint64, transactionsJSON []json.RawMessage) error
|
||||
PutTransactionReceiptsJSON(chainID uint64, receiptsJSON []json.RawMessage) error
|
||||
PutBalance(chainID uint64, account common.Address, blockNumber *big.Int, balance *big.Int) error
|
||||
PutTransactionCount(chainID uint64, account common.Address, blockNumber *big.Int, txCount uint64) error
|
||||
}
|
||||
|
||||
type EthClientStorage interface {
|
||||
@ -145,6 +149,54 @@ func (b *DB) GetTransactionReceiptJSONByHash(chainID uint64, transactionHash com
|
||||
return receiptJSON, nil
|
||||
}
|
||||
|
||||
func (b *DB) GetBalance(chainID uint64, account common.Address, blockNumber *big.Int) (*big.Int, error) {
|
||||
if !isConcreteBlockNumber(blockNumber) {
|
||||
return nil, sql.ErrNoRows
|
||||
}
|
||||
|
||||
q := sq.Select("balance").
|
||||
From("blockchain_data_balances").
|
||||
Where(sq.Eq{"chain_id": chainID, "account": account, "block_number": (*bigint.SQLBigIntBytes)(blockNumber)})
|
||||
|
||||
query, args, err := q.ToSql()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
balanceInt := (*bigint.SQLBigIntBytes)(big.NewInt(0))
|
||||
|
||||
err = b.db.QueryRow(query, args...).Scan(&balanceInt)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return (*big.Int)(balanceInt), nil
|
||||
}
|
||||
|
||||
func (b *DB) GetTransactionCount(chainID uint64, account common.Address, blockNumber *big.Int) (uint64, error) {
|
||||
if !isConcreteBlockNumber(blockNumber) {
|
||||
return 0, sql.ErrNoRows
|
||||
}
|
||||
|
||||
q := sq.Select("transaction_count").
|
||||
From("blockchain_data_transaction_counts").
|
||||
Where(sq.Eq{"chain_id": chainID, "account": account, "block_number": (*bigint.SQLBigIntBytes)(blockNumber)})
|
||||
|
||||
query, args, err := q.ToSql()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
txCount := uint64(0)
|
||||
|
||||
err = b.db.QueryRow(query, args...).Scan(&txCount)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return txCount, nil
|
||||
}
|
||||
|
||||
func (b *DB) PutBlockJSON(chainID uint64, blkJSON json.RawMessage, transactionDetailsFlag bool) (err error) {
|
||||
var tx *sql.Tx
|
||||
tx, err = b.db.Begin()
|
||||
@ -234,6 +286,42 @@ func (b *DB) PutTransactionReceiptsJSON(chainID uint64, receiptsJSON []json.RawM
|
||||
return
|
||||
}
|
||||
|
||||
func (b *DB) PutBalance(chainID uint64, account common.Address, blockNumber *big.Int, balance *big.Int) (err error) {
|
||||
var tx *sql.Tx
|
||||
tx, err = b.db.Begin()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer func() {
|
||||
if err == nil {
|
||||
err = tx.Commit()
|
||||
return
|
||||
}
|
||||
_ = tx.Rollback()
|
||||
}()
|
||||
|
||||
err = putBalance(tx, chainID, account, blockNumber, balance)
|
||||
return
|
||||
}
|
||||
|
||||
func (b *DB) PutTransactionCount(chainID uint64, account common.Address, blockNumber *big.Int, txCount uint64) (err error) {
|
||||
var tx *sql.Tx
|
||||
tx, err = b.db.Begin()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer func() {
|
||||
if err == nil {
|
||||
err = tx.Commit()
|
||||
return
|
||||
}
|
||||
_ = tx.Rollback()
|
||||
}()
|
||||
|
||||
err = putTransactionCount(tx, chainID, account, blockNumber, txCount)
|
||||
return
|
||||
}
|
||||
|
||||
func putBlockJSON(creator sqlite.StatementCreator, chainID uint64, blkJSON json.RawMessage, transactionDetailsFlag bool) error {
|
||||
var rpcBlock rpcBlock
|
||||
if err := json.Unmarshal(blkJSON, &rpcBlock); err != nil {
|
||||
@ -346,3 +434,55 @@ func putReceiptJSON(creator sqlite.StatementCreator, chainID uint64, receiptJSON
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func putBalance(creator sqlite.StatementCreator, chainID uint64, account common.Address, blockNumber *big.Int, balance *big.Int) error {
|
||||
if !isConcreteBlockNumber(blockNumber) {
|
||||
return errors.New("invalid block number")
|
||||
}
|
||||
|
||||
q := sq.Replace("blockchain_data_balances").
|
||||
SetMap(sq.Eq{"chain_id": chainID, "account": account, "block_number": (*bigint.SQLBigIntBytes)(blockNumber),
|
||||
"balance": (*bigint.SQLBigIntBytes)(balance),
|
||||
})
|
||||
|
||||
query, args, err := q.ToSql()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
stmt, err := creator.Prepare(query)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer stmt.Close()
|
||||
|
||||
_, err = stmt.Exec(args...)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func putTransactionCount(creator sqlite.StatementCreator, chainID uint64, account common.Address, blockNumber *big.Int, txCount uint64) error {
|
||||
if !isConcreteBlockNumber(blockNumber) {
|
||||
return errors.New("invalid block number")
|
||||
}
|
||||
|
||||
q := sq.Replace("blockchain_data_transaction_counts").
|
||||
SetMap(sq.Eq{"chain_id": chainID, "account": account, "block_number": (*bigint.SQLBigIntBytes)(blockNumber),
|
||||
"transaction_count": txCount,
|
||||
})
|
||||
|
||||
query, args, err := q.ToSql()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
stmt, err := creator.Prepare(query)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer stmt.Close()
|
||||
|
||||
_, err = stmt.Exec(args...)
|
||||
|
||||
return err
|
||||
}
|
||||
|
@ -13,6 +13,8 @@ type EthClientChainStorageReader interface {
|
||||
GetBlockUncleJSONByHashAndIndex(blockHash common.Hash, index uint64) (json.RawMessage, error)
|
||||
GetTransactionJSONByHash(transactionHash common.Hash) (json.RawMessage, error)
|
||||
GetTransactionReceiptJSONByHash(transactionHash common.Hash) (json.RawMessage, error)
|
||||
GetBalance(account common.Address, blockNumber *big.Int) (*big.Int, error)
|
||||
GetTransactionCount(account common.Address, blockNumber *big.Int) (uint64, error)
|
||||
}
|
||||
|
||||
type EthClientChainStorageWriter interface {
|
||||
@ -20,6 +22,8 @@ type EthClientChainStorageWriter interface {
|
||||
PutBlockUnclesJSON(blockHash common.Hash, unclesJSON []json.RawMessage) error
|
||||
PutTransactionsJSON(transactionsJSON []json.RawMessage) error
|
||||
PutTransactionReceiptsJSON(receiptsJSON []json.RawMessage) error
|
||||
PutBalance(account common.Address, blockNumber *big.Int, balance *big.Int) error
|
||||
PutTransactionCount(account common.Address, blockNumber *big.Int, txCount uint64) error
|
||||
}
|
||||
|
||||
type EthClientChainStorage interface {
|
||||
@ -59,6 +63,14 @@ func (b *DBChain) GetTransactionReceiptJSONByHash(transactionHash common.Hash) (
|
||||
return b.s.GetTransactionReceiptJSONByHash(b.chainID, transactionHash)
|
||||
}
|
||||
|
||||
func (b *DBChain) GetBalance(account common.Address, blockNumber *big.Int) (*big.Int, error) {
|
||||
return b.s.GetBalance(b.chainID, account, blockNumber)
|
||||
}
|
||||
|
||||
func (b *DBChain) GetTransactionCount(account common.Address, blockNumber *big.Int) (uint64, error) {
|
||||
return b.s.GetTransactionCount(b.chainID, account, blockNumber)
|
||||
}
|
||||
|
||||
func (b *DBChain) PutBlockJSON(blkJSON json.RawMessage, transactionDetailsFlag bool) error {
|
||||
return b.s.PutBlockJSON(b.chainID, blkJSON, transactionDetailsFlag)
|
||||
}
|
||||
@ -74,3 +86,11 @@ func (b *DBChain) PutTransactionsJSON(transactionsJSON []json.RawMessage) error
|
||||
func (b *DBChain) PutTransactionReceiptsJSON(receiptsJSON []json.RawMessage) error {
|
||||
return b.s.PutTransactionReceiptsJSON(b.chainID, receiptsJSON)
|
||||
}
|
||||
|
||||
func (b *DBChain) PutBalance(account common.Address, blockNumber *big.Int, balance *big.Int) error {
|
||||
return b.s.PutBalance(b.chainID, account, blockNumber, balance)
|
||||
}
|
||||
|
||||
func (b *DBChain) PutTransactionCount(account common.Address, blockNumber *big.Int, txCount uint64) error {
|
||||
return b.s.PutTransactionCount(b.chainID, account, blockNumber, txCount)
|
||||
}
|
||||
|
@ -3,9 +3,13 @@ package ethclient_test
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"math/big"
|
||||
"testing"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
"github.com/ethereum/go-ethereum/rpc"
|
||||
|
||||
ethclient "github.com/status-im/status-go/rpc/chain/ethclient"
|
||||
mock_ethclient "github.com/status-im/status-go/rpc/chain/ethclient/mock/client/ethclient"
|
||||
@ -33,6 +37,16 @@ func setupCachedEthClientTest(t *testing.T) (*ethclient.CachedEthClient, *mock_e
|
||||
return cachedEthClient, ethClient, cleanup
|
||||
}
|
||||
|
||||
func specialBlockNumbers() []*big.Int {
|
||||
return []*big.Int{
|
||||
nil,
|
||||
big.NewInt(int64(rpc.LatestBlockNumber)),
|
||||
big.NewInt(int64(rpc.PendingBlockNumber)),
|
||||
big.NewInt(int64(rpc.PendingBlockNumber)),
|
||||
big.NewInt(int64(rpc.SafeBlockNumber)),
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetBlock(t *testing.T) {
|
||||
client, ethClient, cleanup := setupCachedEthClientTest(t)
|
||||
defer cleanup()
|
||||
@ -83,6 +97,20 @@ func TestGetBlock(t *testing.T) {
|
||||
}).Times(1)
|
||||
_, err = client.BlockByHash(ctx, common.HexToHash("0x1234"))
|
||||
require.Error(t, err)
|
||||
|
||||
// Calls with non-concrete block numbers always go to chain
|
||||
for i := 0; i < 3; i++ {
|
||||
for _, blockNumber := range specialBlockNumbers() {
|
||||
newBlkJSON, _, _ := getTestBlockJSONWithTxDetails()
|
||||
ethClient.EXPECT().CallContext(ctx, gomock.Any(), gomock.Any(), gomock.Any()).
|
||||
DoAndReturn(func(ctx context.Context, result interface{}, method string, args ...interface{}) error {
|
||||
*result.(*json.RawMessage) = newBlkJSON
|
||||
return nil
|
||||
}).Times(1)
|
||||
_, err = client.BlockByNumber(ctx, blockNumber)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetHeader(t *testing.T) {
|
||||
@ -135,6 +163,20 @@ func TestGetHeader(t *testing.T) {
|
||||
}).Times(1)
|
||||
_, err = client.BlockByHash(ctx, common.HexToHash("0x1234"))
|
||||
require.Error(t, err)
|
||||
|
||||
// Calls with non-concrete block numbers always go to chain
|
||||
for i := 0; i < 3; i++ {
|
||||
for _, blockNumber := range specialBlockNumbers() {
|
||||
newBlkJSON, _, _ := getTestBlockJSONWithTxDetails()
|
||||
ethClient.EXPECT().CallContext(ctx, gomock.Any(), gomock.Any(), gomock.Any()).
|
||||
DoAndReturn(func(ctx context.Context, result interface{}, method string, args ...interface{}) error {
|
||||
*result.(*json.RawMessage) = newBlkJSON
|
||||
return nil
|
||||
}).Times(1)
|
||||
_, err = client.HeaderByNumber(ctx, blockNumber)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetTransaction(t *testing.T) {
|
||||
@ -222,3 +264,159 @@ func TestGetReceipt(t *testing.T) {
|
||||
_, err = client.TransactionReceipt(ctx, common.HexToHash("0x1234"))
|
||||
require.Error(t, err)
|
||||
}
|
||||
|
||||
func TestGetBalance(t *testing.T) {
|
||||
client, ethClient, cleanup := setupCachedEthClientTest(t)
|
||||
defer cleanup()
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
account := common.HexToAddress("0x1234")
|
||||
blockNumber := big.NewInt(1234)
|
||||
valueHex, valueInt := getTestBalance()
|
||||
|
||||
// First call goes to the chain, through raw endpoint
|
||||
ethClient.EXPECT().CallContext(ctx, gomock.Any(), gomock.Any(), gomock.Any()).
|
||||
DoAndReturn(func(ctx context.Context, result interface{}, method string, args ...interface{}) error {
|
||||
*result.(**hexutil.Big) = valueHex
|
||||
return nil
|
||||
}).Times(1)
|
||||
res, err := client.BalanceAt(ctx, account, blockNumber)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, valueInt, res)
|
||||
|
||||
// Next calls are read from cache
|
||||
ethClient.EXPECT().CallContext(ctx, gomock.Any(), gomock.Any(), gomock.Any()).Times(0)
|
||||
res, err = client.BalanceAt(ctx, account, blockNumber)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, valueInt, res)
|
||||
|
||||
// Fetching a different account goes to the chain
|
||||
ethClient.EXPECT().CallContext(ctx, gomock.Any(), gomock.Any(), gomock.Any()).
|
||||
DoAndReturn(func(ctx context.Context, result interface{}, method string, args ...interface{}) error {
|
||||
*result.(**hexutil.Big) = nil
|
||||
return errors.New("Some error")
|
||||
}).Times(1)
|
||||
_, err = client.BalanceAt(ctx, common.HexToAddress("0x4567"), blockNumber)
|
||||
require.Error(t, err)
|
||||
|
||||
// No cache due to error, should hit chain again
|
||||
ethClient.EXPECT().CallContext(ctx, gomock.Any(), gomock.Any(), gomock.Any()).
|
||||
DoAndReturn(func(ctx context.Context, result interface{}, method string, args ...interface{}) error {
|
||||
*result.(**hexutil.Big) = nil
|
||||
return errors.New("Some error")
|
||||
}).Times(1)
|
||||
_, err = client.BalanceAt(ctx, common.HexToAddress("0x4567"), blockNumber)
|
||||
require.Error(t, err)
|
||||
|
||||
// Fetching a different block goes to the chain
|
||||
ethClient.EXPECT().CallContext(ctx, gomock.Any(), gomock.Any(), gomock.Any()).
|
||||
DoAndReturn(func(ctx context.Context, result interface{}, method string, args ...interface{}) error {
|
||||
*result.(**hexutil.Big) = nil
|
||||
return errors.New("Some error")
|
||||
}).Times(1)
|
||||
_, err = client.BalanceAt(ctx, account, big.NewInt(5))
|
||||
require.Error(t, err)
|
||||
|
||||
// No cache due to error, should hit chain again
|
||||
ethClient.EXPECT().CallContext(ctx, gomock.Any(), gomock.Any(), gomock.Any()).
|
||||
DoAndReturn(func(ctx context.Context, result interface{}, method string, args ...interface{}) error {
|
||||
*result.(**hexutil.Big) = nil
|
||||
return errors.New("Some error")
|
||||
}).Times(1)
|
||||
_, err = client.BalanceAt(ctx, account, big.NewInt(5))
|
||||
require.Error(t, err)
|
||||
|
||||
// Non-concrete block numbers always go to chain
|
||||
for i := 0; i < 3; i++ {
|
||||
newValueHex, newValueInt := getTestBalance()
|
||||
for _, blockNumber := range specialBlockNumbers() {
|
||||
ethClient.EXPECT().CallContext(ctx, gomock.Any(), gomock.Any(), gomock.Any()).
|
||||
DoAndReturn(func(ctx context.Context, result interface{}, method string, args ...interface{}) error {
|
||||
*result.(**hexutil.Big) = newValueHex
|
||||
return nil
|
||||
}).Times(1)
|
||||
res, err := client.BalanceAt(ctx, account, blockNumber)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, newValueInt, res)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetTransactionCount(t *testing.T) {
|
||||
client, ethClient, cleanup := setupCachedEthClientTest(t)
|
||||
defer cleanup()
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
account := common.HexToAddress("0x1234")
|
||||
blockNumber := big.NewInt(1234)
|
||||
valueHex, valueInt := getTestTransactionCount()
|
||||
|
||||
// First call goes to the chain, through raw endpoint
|
||||
ethClient.EXPECT().CallContext(ctx, gomock.Any(), gomock.Any(), gomock.Any()).
|
||||
DoAndReturn(func(ctx context.Context, result interface{}, method string, args ...interface{}) error {
|
||||
*result.(*hexutil.Uint64) = valueHex
|
||||
return nil
|
||||
}).Times(1)
|
||||
res, err := client.NonceAt(ctx, account, blockNumber)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, valueInt, res)
|
||||
|
||||
// Next calls are read from cache
|
||||
ethClient.EXPECT().CallContext(ctx, gomock.Any(), gomock.Any(), gomock.Any()).Times(0)
|
||||
res, err = client.NonceAt(ctx, account, blockNumber)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, valueInt, res)
|
||||
|
||||
// Fetching a different account goes to the chain
|
||||
ethClient.EXPECT().CallContext(ctx, gomock.Any(), gomock.Any(), gomock.Any()).
|
||||
DoAndReturn(func(ctx context.Context, result interface{}, method string, args ...interface{}) error {
|
||||
*result.(*hexutil.Uint64) = 0
|
||||
return errors.New("Some error")
|
||||
}).Times(1)
|
||||
_, err = client.NonceAt(ctx, common.HexToAddress("0x4567"), blockNumber)
|
||||
require.Error(t, err)
|
||||
|
||||
// No cache due to error, should hit chain again
|
||||
ethClient.EXPECT().CallContext(ctx, gomock.Any(), gomock.Any(), gomock.Any()).
|
||||
DoAndReturn(func(ctx context.Context, result interface{}, method string, args ...interface{}) error {
|
||||
*result.(*hexutil.Uint64) = 0
|
||||
return errors.New("Some error")
|
||||
}).Times(1)
|
||||
_, err = client.NonceAt(ctx, common.HexToAddress("0x4567"), blockNumber)
|
||||
require.Error(t, err)
|
||||
|
||||
// Fetching a different block goes to the chain
|
||||
ethClient.EXPECT().CallContext(ctx, gomock.Any(), gomock.Any(), gomock.Any()).
|
||||
DoAndReturn(func(ctx context.Context, result interface{}, method string, args ...interface{}) error {
|
||||
*result.(*hexutil.Uint64) = 0
|
||||
return errors.New("Some error")
|
||||
}).Times(1)
|
||||
_, err = client.NonceAt(ctx, account, big.NewInt(5))
|
||||
require.Error(t, err)
|
||||
|
||||
// No cache due to error, should hit chain again
|
||||
ethClient.EXPECT().CallContext(ctx, gomock.Any(), gomock.Any(), gomock.Any()).
|
||||
DoAndReturn(func(ctx context.Context, result interface{}, method string, args ...interface{}) error {
|
||||
*result.(*hexutil.Uint64) = 0
|
||||
return errors.New("Some error")
|
||||
}).Times(1)
|
||||
_, err = client.NonceAt(ctx, account, big.NewInt(5))
|
||||
require.Error(t, err)
|
||||
|
||||
// Non-concrete block numbers always go to chain
|
||||
for i := 0; i < 3; i++ {
|
||||
newValueHex, newValueInt := getTestTransactionCount()
|
||||
for _, blockNumber := range specialBlockNumbers() {
|
||||
ethClient.EXPECT().CallContext(ctx, gomock.Any(), gomock.Any(), gomock.Any()).
|
||||
DoAndReturn(func(ctx context.Context, result interface{}, method string, args ...interface{}) error {
|
||||
*result.(*hexutil.Uint64) = newValueHex
|
||||
return nil
|
||||
}).Times(1)
|
||||
res, err := client.NonceAt(ctx, account, blockNumber)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, newValueInt, res)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3,8 +3,10 @@ package ethclient_test
|
||||
import (
|
||||
"encoding/json"
|
||||
"math/big"
|
||||
"math/rand"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
)
|
||||
|
||||
@ -71,3 +73,16 @@ func getTestBlockUnclesJSON() []json.RawMessage {
|
||||
|
||||
return []json.RawMessage{json.RawMessage(uncle0)}
|
||||
}
|
||||
|
||||
func getTestBalance() (*hexutil.Big, *big.Int) {
|
||||
balance := uint64(rand.Uint32()) // nolint: gosec
|
||||
balanceInt := new(big.Int).SetUint64(balance)
|
||||
|
||||
return (*hexutil.Big)(balanceInt), balanceInt
|
||||
}
|
||||
|
||||
func getTestTransactionCount() (hexutil.Uint64, uint64) {
|
||||
txCount := uint64(rand.Uint32()) // nolint: gosec
|
||||
|
||||
return (hexutil.Uint64)(txCount), txCount
|
||||
}
|
||||
|
@ -125,3 +125,7 @@ type txExtraInfo struct {
|
||||
BlockHash *common.Hash `json:"blockHash,omitempty"`
|
||||
From *common.Address `json:"from,omitempty"`
|
||||
}
|
||||
|
||||
func isConcreteBlockNumber(blockNumber *big.Int) bool {
|
||||
return blockNumber != nil && blockNumber.Cmp(big.NewInt(0)) >= 0
|
||||
}
|
||||
|
@ -149,3 +149,23 @@ func (pa *PrivateAPI) BlockNumber(ctx context.Context, chainId uint64) (uint64,
|
||||
|
||||
return client.BlockNumber(ctx)
|
||||
}
|
||||
|
||||
func (pa *PrivateAPI) BalanceAt(ctx context.Context, chainId uint64, account common.Address, blockNumber *hexutil.Big) (*hexutil.Big, error) {
|
||||
client, err := pa.client.EthClient(chainId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
balance, err := client.BalanceAt(ctx, account, (*big.Int)(blockNumber))
|
||||
return (*hexutil.Big)(balance), err
|
||||
}
|
||||
|
||||
func (pa *PrivateAPI) NonceAt(ctx context.Context, chainId uint64, account common.Address, blockNumber *hexutil.Big) (uint64, error) {
|
||||
client, err := pa.client.EthClient(chainId)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
nonce, err := client.NonceAt(ctx, account, (*big.Int)(blockNumber))
|
||||
return nonce, err
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
import pytest
|
||||
|
||||
from conftest import user_1
|
||||
from test_cases import EthRpcTestCase
|
||||
|
||||
|
||||
@ -42,6 +43,14 @@ class TestEth(EthRpcTestCase):
|
||||
def test_suggest_gas_price(self, tx_data, iterations):
|
||||
self.rpc_client.rpc_valid_request("ethclient_suggestGasPrice", [self.network_id])
|
||||
|
||||
def test_balance_at(self, tx_data, iterations):
|
||||
self.rpc_client.rpc_valid_request("ethclient_balanceAt", [self.network_id, user_1.address, "0x0"])
|
||||
self.rpc_client.rpc_valid_request("ethclient_balanceAt", [self.network_id, user_1.address, tx_data.block_number])
|
||||
|
||||
def test_nonce_at(self, tx_data, iterations):
|
||||
self.rpc_client.rpc_valid_request("ethclient_nonceAt", [self.network_id, user_1.address, "0x0"])
|
||||
self.rpc_client.rpc_valid_request("ethclient_nonceAt", [self.network_id, user_1.address, tx_data.block_number])
|
||||
|
||||
def test_header_by_number(self, tx_data, iterations):
|
||||
response = self.rpc_client.rpc_valid_request("ethclient_headerByNumber",
|
||||
[self.network_id, tx_data.block_number])
|
||||
|
@ -45,3 +45,27 @@ CREATE TABLE IF NOT EXISTS blockchain_data_receipts (
|
||||
) WITHOUT ROWID;
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_blockchain_data_receipts_chain_id_transaction_hash ON blockchain_data_receipts (chain_id, transaction_hash);
|
||||
|
||||
-- store balances
|
||||
CREATE TABLE IF NOT EXISTS blockchain_data_balances (
|
||||
chain_id UNSIGNED BIGINT NOT NULL,
|
||||
account BLOB NOT NULL,
|
||||
block_number BLOB NOT NULL,
|
||||
balance BLOB NOT NULL,
|
||||
PRIMARY KEY (chain_id, account, block_number),
|
||||
CONSTRAINT unique_balance_per_chain_per_account_per_block_number UNIQUE (chain_id, account, block_number) ON CONFLICT REPLACE
|
||||
) WITHOUT ROWID;
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_blockchain_data_balances_chain_id_account_block_number ON blockchain_data_balances (chain_id, account, block_number);
|
||||
|
||||
-- store transaction counts
|
||||
CREATE TABLE IF NOT EXISTS blockchain_data_transaction_counts (
|
||||
chain_id UNSIGNED BIGINT NOT NULL,
|
||||
account BLOB NOT NULL,
|
||||
block_number BLOB NOT NULL,
|
||||
transaction_count BIGINT NOT NULL,
|
||||
PRIMARY KEY (chain_id, account, block_number),
|
||||
CONSTRAINT unique_transaction_count_per_chain_per_account_per_block_number UNIQUE (chain_id, account, block_number) ON CONFLICT REPLACE
|
||||
) WITHOUT ROWID;
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_blockchain_data_transaction_count_chain_id_account_block_number ON blockchain_data_transaction_counts (chain_id, account, block_number);
|
Loading…
x
Reference in New Issue
Block a user