asd
This commit is contained in:
parent
fb150f3d16
commit
953ad55f19
|
@ -0,0 +1,170 @@
|
|||
package chain
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"math/big"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
)
|
||||
|
||||
type CachedClient struct {
|
||||
*ClientWithFallback
|
||||
db *DB
|
||||
}
|
||||
|
||||
func NewCachedClient(ethClients []*EthClient, chainID uint64, db *sql.DB) *CachedClient {
|
||||
return &CachedClient{
|
||||
ClientWithFallback: NewClient(ethClients, chainID),
|
||||
db: NewDB(db),
|
||||
}
|
||||
}
|
||||
|
||||
func (c *CachedClient) HeaderByHash(ctx context.Context, hash common.Hash) (*types.Header, error) {
|
||||
header, err := c.db.GetBlockHeaderByHash(c.NetworkID(), hash)
|
||||
if err == nil {
|
||||
return header, nil
|
||||
} else if err != sql.ErrNoRows {
|
||||
// Soft error, we can continue
|
||||
log.Error("Failed to get header from cache", "error", err)
|
||||
}
|
||||
|
||||
header, err = c.ClientWithFallback.HeaderByHash(ctx, hash)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = c.db.PutBlockHeader(c.NetworkID(), header)
|
||||
if err != nil {
|
||||
// Soft error, we can continue
|
||||
log.Error("Failed to put header into cache", "error", err)
|
||||
}
|
||||
|
||||
return header, nil
|
||||
}
|
||||
|
||||
func (c *CachedClient) BlockByHash(ctx context.Context, hash common.Hash) (*types.Block, error) {
|
||||
block, err := c.db.GetBlockByHash(c.NetworkID(), hash)
|
||||
if err == nil {
|
||||
return block, nil
|
||||
} else if err != sql.ErrNoRows {
|
||||
// Soft error, we can continue
|
||||
log.Error("Failed to get block from cache", "error", err)
|
||||
}
|
||||
|
||||
block, err = c.ClientWithFallback.BlockByHash(ctx, hash)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if block != nil {
|
||||
err = c.db.PutBlock(c.NetworkID(), block)
|
||||
if err != nil {
|
||||
// Soft error, we can continue
|
||||
log.Error("Failed to put block into cache", "error", err)
|
||||
}
|
||||
err = c.db.PutTransactions(c.NetworkID(), block.Transactions())
|
||||
if err != nil {
|
||||
// Soft error, we can continue
|
||||
log.Error("Failed to put transactions into cache", "error", err)
|
||||
}
|
||||
}
|
||||
|
||||
return block, nil
|
||||
}
|
||||
|
||||
func (c *CachedClient) BlockByNumber(ctx context.Context, number *big.Int) (*types.Block, error) {
|
||||
block, err := c.db.GetBlockByNumber(c.NetworkID(), number)
|
||||
if err == nil {
|
||||
return block, nil
|
||||
} else if err != sql.ErrNoRows {
|
||||
// Soft error, we can continue
|
||||
log.Error("Failed to get block from cache", "error", err)
|
||||
}
|
||||
|
||||
block, err = c.ClientWithFallback.BlockByNumber(ctx, number)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = c.db.PutBlock(c.NetworkID(), block)
|
||||
if err != nil {
|
||||
// Soft error, we can continue
|
||||
log.Error("Failed to put block into cache", "error", err)
|
||||
}
|
||||
|
||||
return block, nil
|
||||
}
|
||||
|
||||
func (c *CachedClient) HeaderByNumber(ctx context.Context, number *big.Int) (*types.Header, error) {
|
||||
header, err := c.db.GetBlockHeaderByNumber(c.NetworkID(), number)
|
||||
if err == nil {
|
||||
return header, nil
|
||||
} else if err != sql.ErrNoRows {
|
||||
// Soft error, we can continue
|
||||
log.Error("Failed to get header from cache", "error", err)
|
||||
}
|
||||
|
||||
header, err = c.ClientWithFallback.HeaderByNumber(ctx, number)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = c.db.PutBlockHeader(c.NetworkID(), header)
|
||||
if err != nil {
|
||||
// Soft error, we can continue
|
||||
log.Error("Failed to put header into cache", "error", err)
|
||||
}
|
||||
|
||||
return header, nil
|
||||
}
|
||||
|
||||
func (c *CachedClient) TransactionByHash(ctx context.Context, hash common.Hash) (*types.Transaction, bool, error) {
|
||||
transaction, err := c.db.GetTransactionByHash(c.NetworkID(), hash)
|
||||
if err == nil {
|
||||
return transaction, false, nil
|
||||
} else if err != sql.ErrNoRows {
|
||||
// Soft error, we can continue
|
||||
log.Error("Failed to get transaction from cache", "error", err)
|
||||
}
|
||||
|
||||
transaction, pending, err := c.ClientWithFallback.TransactionByHash(ctx, hash)
|
||||
if err != nil {
|
||||
return nil, pending, err
|
||||
}
|
||||
|
||||
if !pending {
|
||||
err = c.db.PutTransactions(c.NetworkID(), types.Transactions{transaction})
|
||||
if err != nil {
|
||||
// Soft error, we can continue
|
||||
log.Error("Failed to put transaction into cache", "error", err)
|
||||
}
|
||||
}
|
||||
|
||||
return transaction, pending, nil
|
||||
}
|
||||
|
||||
func (c *CachedClient) TransactionReceipt(ctx context.Context, hash common.Hash) (*types.Receipt, error) {
|
||||
receipt, err := c.db.GetTransactionReceipt(c.NetworkID(), hash)
|
||||
if err == nil {
|
||||
return receipt, nil
|
||||
} else if err != sql.ErrNoRows {
|
||||
// Soft error, we can continue
|
||||
log.Error("Failed to get transaction receipt from cache", "error", err)
|
||||
}
|
||||
|
||||
receipt, err = c.ClientWithFallback.TransactionReceipt(ctx, hash)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = c.db.PutTransactionReceipt(c.NetworkID(), receipt)
|
||||
if err != nil {
|
||||
// Soft error, we can continue
|
||||
log.Error("Failed to put transaction receipt into cache", "error", err)
|
||||
}
|
||||
|
||||
return receipt, nil
|
||||
}
|
|
@ -0,0 +1,56 @@
|
|||
package chain
|
||||
|
||||
import (
|
||||
"math/big"
|
||||
"testing"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/status-im/status-go/t/helpers"
|
||||
|
||||
"github.com/status-im/status-go/t/helpers"
|
||||
"github.com/status-im/status-go/walletdatabase"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func setupDBTest(t *testing.T) (*DB, func()) {
|
||||
db, err := helpers.SetupTestMemorySQLDB(walletdatabase.DbInitializer{})
|
||||
require.NoError(t, err)
|
||||
return NewDB(db), func() {
|
||||
require.NoError(t, db.Close())
|
||||
}
|
||||
}
|
||||
|
||||
setupCachedClient(t *testing.T) (*CachedClient, func()) {
|
||||
db, closeDB := setupDBTest(t)
|
||||
|
||||
|
||||
|
||||
db, err := helpers.SetupTestMemorySQLDB(walletdatabase.DbInitializer{})
|
||||
require.NoError(t, err)
|
||||
return NewDB(db), func() {
|
||||
require.NoError(t, db.Close())
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetTransactionByHash(t *testing.T) {
|
||||
db, cleanup := setupDBTest(t)
|
||||
defer cleanup()
|
||||
|
||||
chainID := uint64(1)
|
||||
txHash := common.HexToHash("0x123456789abcdef")
|
||||
tx := types.NewTransaction(0, common.HexToAddress("0x1"), big.NewInt(1), 100000, big.NewInt(1), nil)
|
||||
receipt := &types.Receipt{
|
||||
TxHash: txHash,
|
||||
GasUsed: 100000,
|
||||
Status: types.ReceiptStatusSuccessful,
|
||||
}
|
||||
|
||||
err := db.PutTransaction(chainID, tx)
|
||||
require.NoError(t, err)
|
||||
|
||||
retrievedTx, err := db.GetTransactionByHash(chainID, txHash)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, tx, retrievedTx)
|
||||
}
|
|
@ -0,0 +1,94 @@
|
|||
package chain
|
||||
|
||||
import (
|
||||
"math/big"
|
||||
"math/rand"
|
||||
|
||||
crypto_rand "crypto/rand"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
)
|
||||
|
||||
func getRandomTransaction() *types.Transaction {
|
||||
nonce := rand.Uint64()
|
||||
gasLimit := rand.Uint64()
|
||||
gasPrice := rand.Uint64()
|
||||
to := common.Address{}
|
||||
crypto_rand.Read(to[:])
|
||||
value := rand.Uint64()
|
||||
data := make([]byte, 32*rand.Intn(10))
|
||||
crypto_rand.Read(data)
|
||||
|
||||
tx := types.NewTransaction(nonce, to, big.NewInt(int64(value)), gasLimit, big.NewInt(int64(gasPrice)), data)
|
||||
|
||||
return tx
|
||||
}
|
||||
|
||||
func getRandomBlockHeader() *types.Header {
|
||||
header := &types.Header{
|
||||
Number: big.NewInt(rand.Int63()),
|
||||
Time: rand.Uint64(),
|
||||
Difficulty: big.NewInt(rand.Int63()),
|
||||
ParentHash: common.Hash{},
|
||||
Nonce: types.BlockNonce{},
|
||||
MixDigest: common.Hash{},
|
||||
}
|
||||
crypto_rand.Read(header.ParentHash[:])
|
||||
crypto_rand.Read(header.Nonce[:])
|
||||
crypto_rand.Read(header.MixDigest[:])
|
||||
|
||||
return header
|
||||
}
|
||||
|
||||
func getRandomLog() *types.Log {
|
||||
log := &types.Log{
|
||||
Address: common.Address{},
|
||||
Topics: []common.Hash{},
|
||||
Data: []byte{},
|
||||
BlockNumber: rand.Uint64(),
|
||||
TxHash: common.Hash{},
|
||||
TxIndex: uint(rand.Uint64()),
|
||||
}
|
||||
crypto_rand.Read(log.Address[:])
|
||||
crypto_rand.Read(log.TxHash[:])
|
||||
for i := 0; i < rand.Intn(10); i++ {
|
||||
hash := common.Hash{}
|
||||
crypto_rand.Read(hash[:])
|
||||
log.Topics = append(log.Topics, hash)
|
||||
}
|
||||
crypto_rand.Read(log.Data)
|
||||
|
||||
return log
|
||||
}
|
||||
|
||||
func getRandomReceipt() *types.Receipt {
|
||||
receipt := &types.Receipt{
|
||||
Status: rand.Uint64(),
|
||||
CumulativeGasUsed: rand.Uint64(),
|
||||
Bloom: types.Bloom{},
|
||||
Logs: []*types.Log{},
|
||||
}
|
||||
crypto_rand.Read(receipt.Bloom[:])
|
||||
for i := 0; i < rand.Intn(10); i++ {
|
||||
receipt.Logs = append(receipt.Logs, getRandomLog())
|
||||
}
|
||||
|
||||
return receipt
|
||||
}
|
||||
|
||||
func getRandomBlock() *types.Block {
|
||||
header := getRandomBlockHeader()
|
||||
|
||||
txs := []*types.Transaction{}
|
||||
for i := 0; i < rand.Intn(10); i++ {
|
||||
txs = append(txs, getRandomTransaction())
|
||||
}
|
||||
|
||||
receipts := []*types.Receipt{}
|
||||
for i := 0; i < rand.Intn(10); i++ {
|
||||
receipts = append(receipts, getRandomReceipt())
|
||||
}
|
||||
|
||||
return types.NewBlock(header, txs, nil, receipts, nil)
|
||||
}
|
|
@ -161,25 +161,6 @@ var propagateErrors = []error{
|
|||
bind.ErrNoCode,
|
||||
}
|
||||
|
||||
func NewSimpleClient(ethClient EthClient, chainID uint64) *ClientWithFallback {
|
||||
cbConfig := circuitbreaker.Config{
|
||||
Timeout: 20000,
|
||||
MaxConcurrentRequests: 100,
|
||||
SleepWindow: 300000,
|
||||
ErrorPercentThreshold: 25,
|
||||
}
|
||||
|
||||
isConnected := &atomic.Bool{}
|
||||
isConnected.Store(true)
|
||||
return &ClientWithFallback{
|
||||
ChainID: chainID,
|
||||
ethClients: []*EthClient{ðClient},
|
||||
isConnected: isConnected,
|
||||
LastCheckedAt: time.Now().Unix(),
|
||||
circuitbreaker: circuitbreaker.NewCircuitBreaker(cbConfig),
|
||||
}
|
||||
}
|
||||
|
||||
func NewClient(ethClients []*EthClient, chainID uint64) *ClientWithFallback {
|
||||
cbConfig := circuitbreaker.Config{
|
||||
Timeout: 20000,
|
||||
|
|
|
@ -0,0 +1,177 @@
|
|||
package chain
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"math/big"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
)
|
||||
|
||||
type DB struct {
|
||||
db *sql.DB
|
||||
}
|
||||
|
||||
func NewDB(db *sql.DB) *DB {
|
||||
return &DB{db: db}
|
||||
}
|
||||
|
||||
func (b *DB) GetBlockByNumber(chainID uint64, blockNumber *big.Int) (*types.Block, error) {
|
||||
row := b.db.QueryRow("SELECT block_json FROM blockchain_data_blocks WHERE chain_id = ? AND block_number = ?", chainID, blockNumber)
|
||||
var block types.Block
|
||||
err := row.Scan(&block)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &block, nil
|
||||
}
|
||||
|
||||
func (b *DB) GetBlockByHash(chainID uint64, blockHash common.Hash) (*types.Block, error) {
|
||||
row := b.db.QueryRow("SELECT block_json FROM blockchain_data_blocks WHERE chain_id = ? AND block_hash = ?", chainID, blockHash)
|
||||
var block types.Block
|
||||
err := row.Scan(&block)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &block, nil
|
||||
}
|
||||
|
||||
func (b *DB) GetBlockHeaderByNumber(chainID uint64, blockNumber *big.Int) (*types.Header, error) {
|
||||
row := b.db.QueryRow("SELECT block_json FROM blockchain_data_blocks WHERE chain_id = ? AND block_number = ?", chainID, blockNumber)
|
||||
var blockHeader types.Header
|
||||
err := row.Scan(&blockHeader)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &blockHeader, nil
|
||||
}
|
||||
|
||||
func (b *DB) GetBlockHeaderByHash(chainID uint64, blockHash common.Hash) (*types.Header, error) {
|
||||
row := b.db.QueryRow("SELECT block_json FROM blockchain_data_blocks WHERE chain_id = ? AND block_hash = ?", chainID, blockHash)
|
||||
var blockHeader types.Header
|
||||
err := row.Scan(&blockHeader)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &blockHeader, nil
|
||||
}
|
||||
|
||||
func (b *DB) PutBlock(chainID uint64, block *types.Block) error {
|
||||
_, err := b.db.Exec("INSERT INTO blockchain_data_blocks (chain_id, block_number, block_hash, block_header_json, block_json) VALUES (?, ?, ?, ?, ?)", chainID, block.Number(), block.Hash(), block.Header(), block)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return b.PutTransactions(chainID, block.Transactions())
|
||||
}
|
||||
|
||||
func (b *DB) PutBlockHeader(chainID uint64, blockHeader *types.Header) error {
|
||||
_, err := b.db.Exec("INSERT INTO blockchain_data_blocks (chain_id, block_number, block_hash, block_header_json) VALUES (?, ?, ?, ?)", chainID, blockHeader.Number, blockHeader.Hash(), blockHeader)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *DB) GetTransactionsByBlockHash(chainID uint64, blockHash common.Hash) (types.Transactions, error) {
|
||||
rows, err := t.db.Query("SELECT transaction_json FROM blockchain_data_transactions WHERE chain_id = ? AND block_hash = ?", chainID, blockHash)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var transactions types.Transactions
|
||||
for rows.Next() {
|
||||
var transaction types.Transaction
|
||||
err := rows.Scan(&transaction)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
transactions = append(transactions, &transaction)
|
||||
}
|
||||
|
||||
return transactions, nil
|
||||
}
|
||||
|
||||
func (t *DB) GetTransactionsByBlockNumber(chainID uint64, blockNumber *big.Int) (types.Transactions, error) {
|
||||
rows, err := t.db.Query("SELECT transaction_json FROM blockchain_data_transactions WHERE chain_id = ? AND block_number = ?", chainID, blockNumber)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var transactions types.Transactions
|
||||
for rows.Next() {
|
||||
var transaction types.Transaction
|
||||
err := rows.Scan(&transaction)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
transactions = append(transactions, &transaction)
|
||||
}
|
||||
|
||||
return transactions, nil
|
||||
}
|
||||
|
||||
func (t *DB) GetTransactionByHash(chainID uint64, transactionHash common.Hash) (*types.Transaction, error) {
|
||||
row := t.db.QueryRow("SELECT transaction_json FROM blockchain_data_transactions WHERE chain_id = ? AND transaction_hash = ?", chainID, transactionHash)
|
||||
var transaction types.Transaction
|
||||
err := row.Scan(&transaction)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &transaction, nil
|
||||
}
|
||||
|
||||
func (t *DB) PutTransactions(chainID uint64, transactions types.Transactions) error {
|
||||
tx, err := t.db.Begin()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer tx.Rollback()
|
||||
|
||||
stmt, err := tx.Prepare("INSERT INTO blockchain_data_transactions (chain_id, transaction_hash, transaction_json) VALUES (?, ?, ?)")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer stmt.Close()
|
||||
|
||||
for _, transaction := range transactions {
|
||||
_, err = stmt.Exec(chainID, transaction.Hash(), transaction)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return tx.Commit()
|
||||
}
|
||||
|
||||
func (t *DB) GetTransactionReceipt(chainID uint64, transactionHash common.Hash) (*types.Receipt, error) {
|
||||
row := t.db.QueryRow("SELECT receipt_json FROM blockchain_data_transactions_receipts WHERE chain_id = ? AND transaction_hash = ?", chainID, transactionHash)
|
||||
var receipt types.Receipt
|
||||
err := row.Scan(&receipt)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &receipt, nil
|
||||
}
|
||||
|
||||
func (t *DB) PutTransactionReceipt(chainID uint64, receipt *types.Receipt) error {
|
||||
tx, err := t.db.Begin()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer tx.Rollback()
|
||||
|
||||
stmt, err := tx.Prepare("INSERT INTO blockchain_data_transactions_receipts (chain_id, transaction_hash, receipt_json) VALUES (?, ?, ?)")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer stmt.Close()
|
||||
|
||||
_, err = stmt.Exec(chainID, receipt.TxHash, receipt)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return tx.Commit()
|
||||
}
|
|
@ -0,0 +1,153 @@
|
|||
package chain
|
||||
|
||||
import (
|
||||
"math/big"
|
||||
"testing"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/status-im/status-go/t/helpers"
|
||||
"github.com/status-im/status-go/walletdatabase"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func setupDBTest(t *testing.T) (*DB, func()) {
|
||||
db, err := helpers.SetupTestMemorySQLDB(walletdatabase.DbInitializer{})
|
||||
require.NoError(t, err)
|
||||
return NewDB(db), func() {
|
||||
require.NoError(t, db.Close())
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetBlockByNumber(t *testing.T) {
|
||||
db, cleanup := setupDBTest(t)
|
||||
defer cleanup()
|
||||
|
||||
chainID := uint64(1)
|
||||
blockNumber := big.NewInt(123)
|
||||
block := types.NewBlock(&types.Header{Number: blockNumber}, nil, nil, nil, nil)
|
||||
|
||||
err := db.PutBlock(chainID, block)
|
||||
require.NoError(t, err)
|
||||
|
||||
retrievedBlock, err := db.GetBlockByNumber(chainID, blockNumber)
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, retrievedBlock)
|
||||
assert.Equal(t, block.Hash(), retrievedBlock.Hash())
|
||||
}
|
||||
|
||||
func TestGetBlockByHash(t *testing.T) {
|
||||
db, cleanup := setupDBTest(t)
|
||||
defer cleanup()
|
||||
|
||||
chainID := uint64(1)
|
||||
blockNumber := big.NewInt(123)
|
||||
block := types.NewBlock(&types.Header{Number: blockNumber}, nil, nil, nil, nil)
|
||||
|
||||
err := db.PutBlock(chainID, block)
|
||||
require.NoError(t, err)
|
||||
|
||||
retrievedBlock, err := db.GetBlockByHash(chainID, block.Hash())
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, retrievedBlock)
|
||||
assert.Equal(t, block.Number(), retrievedBlock.Number())
|
||||
}
|
||||
|
||||
func TestGetBlockHeaderByNumber(t *testing.T) {
|
||||
db, cleanup := setupDBTest(t)
|
||||
defer cleanup()
|
||||
|
||||
chainID := uint64(1)
|
||||
blockNumber := big.NewInt(123)
|
||||
header := &types.Header{Number: blockNumber}
|
||||
block := types.NewBlock(header, nil, nil, nil, nil)
|
||||
|
||||
err := db.PutBlock(chainID, block)
|
||||
require.NoError(t, err)
|
||||
|
||||
retrievedHeader, err := db.GetBlockHeaderByNumber(chainID, blockNumber)
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, retrievedHeader)
|
||||
assert.Equal(t, header.Hash(), retrievedHeader.Hash())
|
||||
}
|
||||
|
||||
func TestGetBlockHeaderByHash(t *testing.T) {
|
||||
db, cleanup := setupDBTest(t)
|
||||
defer cleanup()
|
||||
|
||||
chainID := uint64(1)
|
||||
blockNumber := big.NewInt(123)
|
||||
header := &types.Header{Number: blockNumber}
|
||||
block := types.NewBlock(header, nil, nil, nil, nil)
|
||||
|
||||
err := db.PutBlock(chainID, block)
|
||||
require.NoError(t, err)
|
||||
|
||||
retrievedHeader, err := db.GetBlockHeaderByHash(chainID, block.Hash())
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, retrievedHeader)
|
||||
assert.Equal(t, header.Number, retrievedHeader.Number)
|
||||
}
|
||||
|
||||
func TestPutAndGetTransactions(t *testing.T) {
|
||||
db, cleanup := setupDBTest(t)
|
||||
defer cleanup()
|
||||
|
||||
chainID := uint64(1)
|
||||
blockNumber := big.NewInt(123)
|
||||
tx1 := types.NewTransaction(0, common.Address{}, big.NewInt(100), 21000, big.NewInt(1), nil)
|
||||
tx2 := types.NewTransaction(1, common.Address{}, big.NewInt(200), 21000, big.NewInt(1), nil)
|
||||
txs := types.Transactions{tx1, tx2}
|
||||
|
||||
block := types.NewBlock(&types.Header{Number: blockNumber}, txs, nil, nil, nil)
|
||||
|
||||
err := db.PutBlock(chainID, block)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Test GetTransactionsByBlockHash
|
||||
retrievedTxs, err := db.GetTransactionsByBlockHash(chainID, block.Hash())
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, retrievedTxs, 2)
|
||||
assert.Equal(t, tx1.Hash(), retrievedTxs[0].Hash())
|
||||
assert.Equal(t, tx2.Hash(), retrievedTxs[1].Hash())
|
||||
|
||||
// Test GetTransactionsByBlockNumber
|
||||
retrievedTxs, err = db.GetTransactionsByBlockNumber(chainID, blockNumber)
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, retrievedTxs, 2)
|
||||
assert.Equal(t, tx1.Hash(), retrievedTxs[0].Hash())
|
||||
assert.Equal(t, tx2.Hash(), retrievedTxs[1].Hash())
|
||||
|
||||
// Test GetTransactionByHash
|
||||
retrievedTx, err := db.GetTransactionByHash(chainID, tx1.Hash())
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, retrievedTx)
|
||||
assert.Equal(t, tx1.Hash(), retrievedTx.Hash())
|
||||
}
|
||||
|
||||
func TestPutAndGetTransactionReceipt(t *testing.T) {
|
||||
db, cleanup := setupDBTest(t)
|
||||
defer cleanup()
|
||||
|
||||
chainID := uint64(1)
|
||||
tx := types.NewTransaction(0, common.Address{}, big.NewInt(100), 21000, big.NewInt(1), nil)
|
||||
receipt := &types.Receipt{
|
||||
TxHash: tx.Hash(),
|
||||
GasUsed: 21000,
|
||||
Status: types.ReceiptStatusSuccessful,
|
||||
BlockNumber: big.NewInt(123),
|
||||
}
|
||||
|
||||
err := db.PutTransactionReceipt(chainID, receipt)
|
||||
require.NoError(t, err)
|
||||
|
||||
retrievedReceipt, err := db.GetTransactionReceipt(chainID, tx.Hash())
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, retrievedReceipt)
|
||||
assert.Equal(t, receipt.TxHash, retrievedReceipt.TxHash)
|
||||
assert.Equal(t, receipt.GasUsed, retrievedReceipt.GasUsed)
|
||||
assert.Equal(t, receipt.Status, retrievedReceipt.Status)
|
||||
}
|
||||
|
||||
// Add more test functions as needed...
|
|
@ -104,6 +104,8 @@ type Client struct {
|
|||
|
||||
walletNotifier func(chainID uint64, message string)
|
||||
providerConfigs []params.ProviderConfig
|
||||
|
||||
db *sql.DB
|
||||
}
|
||||
|
||||
// Is initialized in a build-tag-dependent module
|
||||
|
@ -136,6 +138,7 @@ func NewClient(client *gethrpc.Client, upstreamChainID uint64, upstream params.U
|
|||
limiterPerProvider: make(map[string]*chain.RPCRpsLimiter),
|
||||
log: log,
|
||||
providerConfigs: providerConfigs,
|
||||
db: db,
|
||||
}
|
||||
|
||||
var opts []gethrpc.ClientOption
|
||||
|
@ -165,7 +168,10 @@ func NewClient(client *gethrpc.Client, upstreamChainID uint64, upstream params.U
|
|||
// Include the chain-id in the rpc client
|
||||
rpcName := fmt.Sprintf("%s-chain-id-%d", hostPortUpstream, upstreamChainID)
|
||||
|
||||
c.upstream = chain.NewSimpleClient(*chain.NewEthClient(ethclient.NewClient(upstreamClient), limiter, upstreamClient, rpcName), upstreamChainID)
|
||||
ethClients := []*chain.EthClient{
|
||||
chain.NewEthClient(ethclient.NewClient(upstreamClient), limiter, upstreamClient, rpcName),
|
||||
}
|
||||
c.upstream = chain.NewCachedClient(ethClients, upstreamChainID, db)
|
||||
}
|
||||
|
||||
c.router = newRouter(c.upstreamEnabled)
|
||||
|
@ -233,7 +239,7 @@ func (c *Client) getClientUsingCache(chainID uint64) (chain.ClientInterface, err
|
|||
return nil, fmt.Errorf("could not find any RPC URL for chain: %d", chainID)
|
||||
}
|
||||
|
||||
client := chain.NewClient(ethClients, chainID)
|
||||
client := chain.NewCachedClient(ethClients, chainID, c.db)
|
||||
client.WalletNotifier = c.walletNotifier
|
||||
c.rpcClients[chainID] = client
|
||||
return client, nil
|
||||
|
@ -371,7 +377,12 @@ func (c *Client) UpdateUpstreamURL(url string) error {
|
|||
if err != nil {
|
||||
hostPortUpstream = "upstream"
|
||||
}
|
||||
c.upstream = chain.NewSimpleClient(*chain.NewEthClient(ethclient.NewClient(rpcClient), rpsLimiter, rpcClient, hostPortUpstream), c.UpstreamChainID)
|
||||
|
||||
ethClients := []*chain.EthClient{
|
||||
chain.NewEthClient(chain.NewEthClient(ethclient.NewClient(rpcClient), rpsLimiter, rpcClient, hostPortUpstream)),
|
||||
}
|
||||
c.upstream = chain.NewCachedClient(ethClients, c.UpstreamChainID, c.db)
|
||||
|
||||
c.upstreamURL = url
|
||||
c.Unlock()
|
||||
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
-- store raw block headers
|
||||
CREATE TABLE IF NOT EXISTS blockchain_data_blocks (
|
||||
chain_id UNSIGNED BIGINT NOT NULL,
|
||||
block_number BLOB NOT NULL,
|
||||
block_hash BLOB NOT NULL,
|
||||
block_header_json JSON NOT NULL,
|
||||
block_json JSON,
|
||||
CONSTRAINT unique_block_header_per_chain_per_block_number UNIQUE (chain_id,block_number) ON CONFLICT REPLACE,
|
||||
CONSTRAINT unique_block_header_per_chain_per_block_hash UNIQUE (chain_id,block_hash) ON CONFLICT REPLACE
|
||||
) WITHOUT ROWID;
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_blockchain_data_block_headers_chain_id_block_number ON blockchain_data_block_headers (chain_id, block_number);
|
||||
CREATE INDEX IF NOT EXISTS idx_blockchain_data_block_headers_chain_id_block_hash ON blockchain_data_block_headers (chain_id, block_hash);
|
||||
|
||||
-- store raw transactions
|
||||
CREATE TABLE IF NOT EXISTS blockchain_data_transactions (
|
||||
chain_id UNSIGNED BIGINT NOT NULL,
|
||||
block_hash BLOB NOT NULL,
|
||||
transaction_hash BLOB NOT NULL,
|
||||
transaction_json JSON NOT NULL,
|
||||
receipt_json JSON,
|
||||
CONSTRAINT unique_transaction_per_chain_per_transaction_hash UNIQUE (chain_id, transaction_hash) ON CONFLICT REPLACE,
|
||||
FOREIGN KEY(chain_id, block_hash) REFERENCES blockchain_data_block_headers(chain_id, block_hash)
|
||||
ON DELETE CASCADE
|
||||
) WITHOUT ROWID;
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_blockchain_data_transactions_chain_id_transaction_hash ON blockchain_data_transactions (chain_id, transaction_hash);
|
||||
CREATE INDEX IF NOT EXISTS idx_blockchain_data_transactions_chain_id_block_hash ON blockchain_data_transactions (chain_id, block_hash);
|
Loading…
Reference in New Issue