diff --git a/cmd/serve.go b/cmd/serve.go
index ec662cfc..0bd3e29b 100644
--- a/cmd/serve.go
+++ b/cmd/serve.go
@@ -25,7 +25,6 @@ import (
"sync"
"time"
-
"github.com/cerc-io/ipld-eth-server/v4/pkg/log"
"github.com/ethereum/go-ethereum/rpc"
"github.com/mailgun/groupcache/v2"
diff --git a/pkg/eth/api.go b/pkg/eth/api.go
index 0e923cca..96196df9 100644
--- a/pkg/eth/api.go
+++ b/pkg/eth/api.go
@@ -720,7 +720,7 @@ func (pea *PublicEthAPI) localGetLogs(crit filters.FilterCriteria) ([]*types.Log
// If we have a blockHash to filter on, fire off single retrieval query
if crit.BlockHash != nil {
- filteredLogs, err := pea.B.Retriever.RetrieveFilteredLog(tx, filter, 0, crit.BlockHash)
+ filteredLogs, err := pea.B.Retriever.RetrieveFilteredLogs(tx, filter, 0, crit.BlockHash)
if err != nil {
return nil, err
}
@@ -748,7 +748,7 @@ func (pea *PublicEthAPI) localGetLogs(crit filters.FilterCriteria) ([]*types.Log
end := endingBlock.Int64()
var logs []*types.Log
for i := start; i <= end; i++ {
- filteredLogs, err := pea.B.Retriever.RetrieveFilteredLog(tx, filter, i, nil)
+ filteredLogs, err := pea.B.Retriever.RetrieveFilteredLogs(tx, filter, i, nil)
if err != nil {
return nil, err
}
diff --git a/pkg/eth/backend.go b/pkg/eth/backend.go
index cecd29a0..03688d0f 100644
--- a/pkg/eth/backend.go
+++ b/pkg/eth/backend.go
@@ -28,6 +28,7 @@ import (
validator "github.com/cerc-io/eth-ipfs-state-validator/v4/pkg"
ipfsethdb "github.com/cerc-io/ipfs-ethdb/v4/postgres"
"github.com/cerc-io/ipld-eth-server/v4/pkg/log"
+ "github.com/cerc-io/ipld-eth-server/v4/pkg/shared"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/consensus"
@@ -48,8 +49,6 @@ import (
sdtypes "github.com/ethereum/go-ethereum/statediff/types"
"github.com/ethereum/go-ethereum/trie"
"github.com/jmoiron/sqlx"
-
- "github.com/cerc-io/ipld-eth-server/v4/pkg/shared"
)
var (
@@ -105,8 +104,7 @@ type Backend struct {
DB *sqlx.DB
// postgres db interfaces
- Retriever *CIDRetriever
- IPLDRetriever *IPLDRetriever
+ Retriever *Retriever
// ethereum interfaces
EthDB ethdb.Database
@@ -131,7 +129,7 @@ func NewEthBackend(db *sqlx.DB, c *Config) (*Backend, error) {
groupName = StateDBGroupCacheName
}
- r := NewCIDRetriever(db)
+ r := NewRetriever(db)
ethDB := ipfsethdb.NewDatabase(db, ipfsethdb.CacheConfig{
Name: groupName,
Size: gcc.StateDB.CacheSizeInMB * 1024 * 1024,
@@ -143,7 +141,6 @@ func NewEthBackend(db *sqlx.DB, c *Config) (*Backend, error) {
return &Backend{
DB: db,
Retriever: r,
- IPLDRetriever: NewIPLDRetriever(db),
EthDB: ethDB,
StateDatabase: state.NewDatabase(ethDB),
Config: c,
@@ -204,7 +201,7 @@ func (b *Backend) HeaderByHash(ctx context.Context, hash common.Hash) (*types.He
}
}()
- _, headerRLP, err := b.IPLDRetriever.RetrieveHeaderByHash(tx, hash)
+ _, headerRLP, err := b.Retriever.RetrieveHeaderByHash(tx, hash)
if err != nil {
return nil, err
}
@@ -407,7 +404,7 @@ func (b *Backend) BlockByHash(ctx context.Context, hash common.Hash) (*types.Blo
// GetHeaderByBlockHash retrieves header for a provided block hash
func (b *Backend) GetHeaderByBlockHash(tx *sqlx.Tx, hash common.Hash) (*types.Header, error) {
- _, headerRLP, err := b.IPLDRetriever.RetrieveHeaderByHash(tx, hash)
+ _, headerRLP, err := b.Retriever.RetrieveHeaderByHash(tx, hash)
if err != nil {
return nil, err
}
@@ -418,7 +415,7 @@ func (b *Backend) GetHeaderByBlockHash(tx *sqlx.Tx, hash common.Hash) (*types.He
// GetUnclesByBlockHash retrieves uncles for a provided block hash
func (b *Backend) GetUnclesByBlockHash(tx *sqlx.Tx, hash common.Hash) ([]*types.Header, error) {
- _, uncleBytes, err := b.IPLDRetriever.RetrieveUnclesByBlockHash(tx, hash)
+ _, uncleBytes, err := b.Retriever.RetrieveUnclesByBlockHash(tx, hash)
if err != nil {
return nil, err
}
@@ -439,7 +436,7 @@ func (b *Backend) GetUnclesByBlockHash(tx *sqlx.Tx, hash common.Hash) ([]*types.
// GetUnclesByBlockHashAndNumber retrieves uncles for a provided block hash and number
func (b *Backend) GetUnclesByBlockHashAndNumber(tx *sqlx.Tx, hash common.Hash, number uint64) ([]*types.Header, error) {
- _, uncleBytes, err := b.IPLDRetriever.RetrieveUncles(tx, hash, number)
+ _, uncleBytes, err := b.Retriever.RetrieveUncles(tx, hash, number)
if err != nil {
return nil, err
}
@@ -460,7 +457,7 @@ func (b *Backend) GetUnclesByBlockHashAndNumber(tx *sqlx.Tx, hash common.Hash, n
// GetTransactionsByBlockHash retrieves transactions for a provided block hash
func (b *Backend) GetTransactionsByBlockHash(tx *sqlx.Tx, hash common.Hash) (types.Transactions, error) {
- _, transactionBytes, err := b.IPLDRetriever.RetrieveTransactionsByBlockHash(tx, hash)
+ _, transactionBytes, err := b.Retriever.RetrieveTransactionsByBlockHash(tx, hash)
if err != nil {
return nil, err
}
@@ -480,7 +477,7 @@ func (b *Backend) GetTransactionsByBlockHash(tx *sqlx.Tx, hash common.Hash) (typ
// GetTransactionsByBlockHashAndNumber retrieves transactions for a provided block hash and number
func (b *Backend) GetTransactionsByBlockHashAndNumber(tx *sqlx.Tx, hash common.Hash, number uint64) (types.Transactions, error) {
- _, transactionBytes, err := b.IPLDRetriever.RetrieveTransactions(tx, hash, number)
+ _, transactionBytes, err := b.Retriever.RetrieveTransactions(tx, hash, number)
if err != nil {
return nil, err
}
@@ -500,7 +497,7 @@ func (b *Backend) GetTransactionsByBlockHashAndNumber(tx *sqlx.Tx, hash common.H
// GetReceiptsByBlockHash retrieves receipts for a provided block hash
func (b *Backend) GetReceiptsByBlockHash(tx *sqlx.Tx, hash common.Hash) (types.Receipts, error) {
- _, receiptBytes, txs, err := b.IPLDRetriever.RetrieveReceiptsByBlockHash(tx, hash)
+ _, receiptBytes, txs, err := b.Retriever.RetrieveReceiptsByBlockHash(tx, hash)
if err != nil {
return nil, err
}
@@ -518,7 +515,7 @@ func (b *Backend) GetReceiptsByBlockHash(tx *sqlx.Tx, hash common.Hash) (types.R
// GetReceiptsByBlockHashAndNumber retrieves receipts for a provided block hash and number
func (b *Backend) GetReceiptsByBlockHashAndNumber(tx *sqlx.Tx, hash common.Hash, number uint64) (types.Receipts, error) {
- _, receiptBytes, txs, err := b.IPLDRetriever.RetrieveReceipts(tx, hash, number)
+ _, receiptBytes, txs, err := b.Retriever.RetrieveReceipts(tx, hash, number)
if err != nil {
return nil, err
}
@@ -607,7 +604,7 @@ func (b *Backend) GetLogs(ctx context.Context, hash common.Hash, number uint64)
}
}()
- _, receiptBytes, txs, err := b.IPLDRetriever.RetrieveReceipts(tx, hash, number)
+ _, receiptBytes, txs, err := b.Retriever.RetrieveReceipts(tx, hash, number)
if err != nil {
return nil, err
}
@@ -695,8 +692,8 @@ func (b *Backend) GetCanonicalHeader(number uint64) (string, []byte, error) {
func (b *Backend) GetEVM(ctx context.Context, msg core.Message, state *state.StateDB, header *types.Header) (*vm.EVM, func() error, error) {
vmError := func() error { return nil }
txContext := core.NewEVMTxContext(msg)
- context := core.NewEVMBlockContext(header, b, nil)
- return vm.NewEVM(context, txContext, state, b.Config.ChainConfig, b.Config.VMConfig), vmError, nil
+ blockContext := core.NewEVMBlockContext(header, b, nil)
+ return vm.NewEVM(blockContext, txContext, state, b.Config.ChainConfig, b.Config.VMConfig), vmError, nil
}
// GetAccountByNumberOrHash returns the account object for the provided address at the block corresponding to the provided number or hash
@@ -748,7 +745,7 @@ func (b *Backend) GetAccountByHash(ctx context.Context, address common.Address,
return nil, err
}
- _, accountRlp, err := b.IPLDRetriever.RetrieveAccountByAddressAndBlockHash(address, hash)
+ _, accountRlp, err := b.Retriever.RetrieveAccountByAddressAndBlockHash(address, hash)
if err != nil {
return nil, err
}
@@ -879,7 +876,7 @@ func (b *Backend) GetStorageByHash(ctx context.Context, address common.Address,
return nil, err
}
- _, _, storageRlp, err := b.IPLDRetriever.RetrieveStorageAtByAddressAndStorageSlotAndBlockHash(address, key, hash)
+ _, _, storageRlp, err := b.Retriever.RetrieveStorageAtByAddressAndStorageSlotAndBlockHash(address, key, hash)
return storageRlp, err
}
diff --git a/pkg/eth/cid_retriever.go b/pkg/eth/cid_retriever.go
deleted file mode 100644
index 7548f4c8..00000000
--- a/pkg/eth/cid_retriever.go
+++ /dev/null
@@ -1,376 +0,0 @@
-// VulcanizeDB
-// Copyright © 2019 Vulcanize
-
-// This program is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Affero General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Affero General Public License for more details.
-
-// You should have received a copy of the GNU Affero General Public License
-// along with this program. If not, see .
-
-package eth
-
-import (
- "fmt"
- "math/big"
- "strconv"
-
- "github.com/cerc-io/ipld-eth-server/v4/pkg/log"
- "github.com/ethereum/go-ethereum/common"
- "github.com/ethereum/go-ethereum/statediff/indexer/models"
- "github.com/jmoiron/sqlx"
- "github.com/lib/pq"
- "gorm.io/driver/postgres"
- "gorm.io/gorm"
-)
-
-// CIDRetriever satisfies the CIDRetriever interface for ethereum
-type CIDRetriever struct {
- db *sqlx.DB
- gormDB *gorm.DB
-}
-
-type IPLDModelRecord struct {
- models.IPLDModel
-}
-
-// TableName overrides the table name used by IPLD
-func (IPLDModelRecord) TableName() string {
- return "ipld.blocks"
-}
-
-type HeaderCIDRecord struct {
- CID string `gorm:"column:cid"`
- BlockHash string `gorm:"primaryKey"`
- BlockNumber string `gorm:"primaryKey"`
- ParentHash string
- Timestamp uint64
- StateRoot string
- TotalDifficulty string `gorm:"column:td"`
- TxRoot string
- RctRoot string `gorm:"column:receipt_root"`
- UncleRoot string
- Bloom []byte
- MhKey string
-
- // gorm doesn't check if foreign key exists in database.
- // It is required to eager load relations using preload.
- TransactionCIDs []TransactionCIDRecord `gorm:"foreignKey:HeaderID,BlockNumber;references:BlockHash,BlockNumber"`
- IPLD IPLDModelRecord `gorm:"foreignKey:MhKey,BlockNumber;references:Key,BlockNumber"`
-}
-
-// TableName overrides the table name used by HeaderCIDRecord
-func (HeaderCIDRecord) TableName() string {
- return "eth.header_cids"
-}
-
-type TransactionCIDRecord struct {
- CID string `gorm:"column:cid"`
- TxHash string `gorm:"primaryKey"`
- BlockNumber string `gorm:"primaryKey"`
- HeaderID string `gorm:"column:header_id"`
- Index int64
- Src string
- Dst string
- MhKey string
- IPLD IPLDModelRecord `gorm:"foreignKey:MhKey,BlockNumber;references:Key,BlockNumber"`
-}
-
-// TableName overrides the table name used by TransactionCIDRecord
-func (TransactionCIDRecord) TableName() string {
- return "eth.transaction_cids"
-}
-
-// NewCIDRetriever returns a pointer to a new CIDRetriever which supports the CIDRetriever interface
-func NewCIDRetriever(db *sqlx.DB) *CIDRetriever {
- gormDB, err := gorm.Open(postgres.New(postgres.Config{
- Conn: db,
- }), &gorm.Config{})
-
- if err != nil {
- log.Error(err)
- return nil
- }
-
- return &CIDRetriever{
- db: db,
- gormDB: gormDB,
- }
-}
-
-// RetrieveFirstBlockNumber is used to retrieve the first block number in the db
-func (ecr *CIDRetriever) RetrieveFirstBlockNumber() (int64, error) {
- var blockNumber int64
- err := ecr.db.Get(&blockNumber, "SELECT block_number FROM eth.header_cids ORDER BY block_number ASC LIMIT 1")
- return blockNumber, err
-}
-
-// RetrieveLastBlockNumber is used to retrieve the latest block number in the db
-func (ecr *CIDRetriever) RetrieveLastBlockNumber() (int64, error) {
- var blockNumber int64
- err := ecr.db.Get(&blockNumber, "SELECT block_number FROM eth.header_cids ORDER BY block_number DESC LIMIT 1")
- return blockNumber, err
-}
-
-func topicFilterCondition(id *int, topics [][]string, args []interface{}, pgStr string, first bool) (string, []interface{}) {
- for i, topicSet := range topics {
- if len(topicSet) == 0 {
- continue
- }
-
- if !first {
- pgStr += " AND"
- } else {
- first = false
- }
- pgStr += fmt.Sprintf(` eth.log_cids.topic%d = ANY ($%d)`, i, *id)
- args = append(args, pq.Array(topicSet))
- *id++
- }
- return pgStr, args
-}
-
-func logFilterCondition(id *int, pgStr string, args []interface{}, rctFilter ReceiptFilter) (string, []interface{}) {
- if len(rctFilter.LogAddresses) > 0 {
- pgStr += fmt.Sprintf(` AND eth.log_cids.address = ANY ($%d)`, *id)
- args = append(args, pq.Array(rctFilter.LogAddresses))
- *id++
- }
-
- // Filter on topics if there are any
- if hasTopics(rctFilter.Topics) {
- pgStr, args = topicFilterCondition(id, rctFilter.Topics, args, pgStr, false)
- }
-
- return pgStr, args
-}
-
-func receiptFilterConditions(id *int, pgStr string, args []interface{}, rctFilter ReceiptFilter, txHashes []string) (string, []interface{}) {
- rctCond := " AND (receipt_cids.tx_id = ANY ( "
- logQuery := "SELECT rct_id FROM eth.log_cids WHERE"
- if len(rctFilter.LogAddresses) > 0 {
- // Filter on log contract addresses if there are any
- pgStr += fmt.Sprintf(`%s %s eth.log_cids.address = ANY ($%d)`, rctCond, logQuery, *id)
- args = append(args, pq.Array(rctFilter.LogAddresses))
- *id++
-
- // Filter on topics if there are any
- if hasTopics(rctFilter.Topics) {
- pgStr, args = topicFilterCondition(id, rctFilter.Topics, args, pgStr, false)
- }
-
- pgStr += ")"
-
- // Filter on txHashes if there are any, and we are matching txs
- if rctFilter.MatchTxs && len(txHashes) > 0 {
- pgStr += fmt.Sprintf(` OR receipt_cids.tx_id = ANY($%d)`, *id)
- args = append(args, pq.Array(txHashes))
- }
- pgStr += ")"
- } else { // If there are no contract addresses to filter on
- // Filter on topics if there are any
- if hasTopics(rctFilter.Topics) {
- pgStr += rctCond + logQuery
- pgStr, args = topicFilterCondition(id, rctFilter.Topics, args, pgStr, true)
- pgStr += ")"
- // Filter on txHashes if there are any, and we are matching txs
- if rctFilter.MatchTxs && len(txHashes) > 0 {
- pgStr += fmt.Sprintf(` OR receipt_cids.tx_id = ANY($%d)`, *id)
- args = append(args, pq.Array(txHashes))
- }
- pgStr += ")"
- } else if rctFilter.MatchTxs && len(txHashes) > 0 {
- // If there are no contract addresses or topics to filter on,
- // Filter on txHashes if there are any, and we are matching txs
- pgStr += fmt.Sprintf(` AND receipt_cids.tx_id = ANY($%d)`, *id)
- args = append(args, pq.Array(txHashes))
- }
- }
-
- return pgStr, args
-}
-
-// RetrieveFilteredGQLLogs retrieves and returns all the log CIDs provided blockHash that conform to the provided
-// filter parameters.
-func (ecr *CIDRetriever) RetrieveFilteredGQLLogs(tx *sqlx.Tx, rctFilter ReceiptFilter, blockHash *common.Hash, blockNumber *big.Int) ([]LogResult, error) {
- log.Debug("retrieving log cids for receipt ids with block hash", blockHash.String())
- args := make([]interface{}, 0, 4)
- id := 1
- pgStr := `SELECT CAST(eth.log_cids.block_number as Text), eth.log_cids.header_id as block_hash,
- eth.log_cids.cid, eth.log_cids.index, eth.log_cids.rct_id, eth.log_cids.address,
- eth.log_cids.topic0, eth.log_cids.topic1, eth.log_cids.topic2, eth.log_cids.topic3, eth.log_cids.log_data,
- data, eth.receipt_cids.cid, eth.receipt_cids.post_status, eth.receipt_cids.tx_id AS tx_hash
- FROM eth.log_cids, eth.receipt_cids, ipld.blocks
- WHERE eth.log_cids.rct_id = receipt_cids.tx_id
- AND eth.log_cids.header_id = receipt_cids.header_id
- AND eth.log_cids.block_number = receipt_cids.block_number
- AND log_cids.cid = blocks.key
- AND log_cids.block_number = blocks.block_number
- AND receipt_cids.header_id = $1`
-
- args = append(args, blockHash.String())
- id++
-
- if blockNumber != nil {
- pgStr += ` AND receipt_cids.block_number = $2`
- id++
- args = append(args, blockNumber.Int64())
- }
-
- pgStr, args = logFilterCondition(&id, pgStr, args, rctFilter)
- pgStr += ` ORDER BY log_cids.index`
-
- logCIDs := make([]LogResult, 0)
- err := tx.Select(&logCIDs, pgStr, args...)
- if err != nil {
- return nil, err
- }
-
- return logCIDs, nil
-}
-
-// RetrieveFilteredLog retrieves and returns all the log CIDs provided blockHeight or blockHash that conform to the provided
-// filter parameters.
-func (ecr *CIDRetriever) RetrieveFilteredLog(tx *sqlx.Tx, rctFilter ReceiptFilter, blockNumber int64, blockHash *common.Hash) ([]LogResult, error) {
- log.Debug("retrieving log cids for receipt ids")
- args := make([]interface{}, 0, 4)
- pgStr := `SELECT CAST(eth.log_cids.block_number as Text), eth.log_cids.cid, eth.log_cids.index, eth.log_cids.rct_id,
- eth.log_cids.address, eth.log_cids.topic0, eth.log_cids.topic1, eth.log_cids.topic2, eth.log_cids.topic3,
- eth.log_cids.log_data, eth.transaction_cids.tx_hash, eth.transaction_cids.index as txn_index,
- eth.receipt_cids.cid as cid, eth.receipt_cids.post_status, header_cids.block_hash
- FROM eth.log_cids, eth.receipt_cids, eth.transaction_cids, eth.header_cids
- WHERE eth.log_cids.rct_id = receipt_cids.tx_id
- AND eth.log_cids.header_id = eth.receipt_cids.header_id
- AND eth.log_cids.block_number = eth.receipt_cids.block_number
- AND receipt_cids.tx_id = transaction_cids.tx_hash
- AND receipt_cids.header_id = transaction_cids.header_id
- AND receipt_cids.block_number = transaction_cids.block_number
- AND transaction_cids.header_id = header_cids.block_hash
- AND transaction_cids.block_number = header_cids.block_number`
- id := 1
- if blockNumber > 0 {
- pgStr += fmt.Sprintf(` AND header_cids.block_number = $%d`, id)
- args = append(args, blockNumber)
- id++
- }
- if blockHash != nil {
- pgStr += fmt.Sprintf(` AND header_cids.block_hash = $%d`, id)
- args = append(args, blockHash.String())
- id++
- }
-
- pgStr, args = logFilterCondition(&id, pgStr, args, rctFilter)
- pgStr += ` ORDER BY log_cids.index`
-
- logCIDs := make([]LogResult, 0)
- err := tx.Select(&logCIDs, pgStr, args...)
- if err != nil {
- return nil, err
- }
-
- return logCIDs, nil
-}
-
-func hasTopics(topics [][]string) bool {
- for _, topicSet := range topics {
- if len(topicSet) > 0 {
- return true
- }
- }
- return false
-}
-
-// RetrieveBlockNumberByHash returns the block number for the given block hash
-func (ecr *CIDRetriever) RetrieveBlockNumberByHash(tx *sqlx.Tx, blockHash common.Hash) (uint64, error) {
- log.Debug("retrieving block number for block hash ", blockHash.String())
- pgStr := `SELECT CAST(block_number as TEXT) FROM eth.header_cids WHERE block_hash = $1`
- var blockNumberStr string
- if err := tx.Get(&blockNumberStr, pgStr, blockHash.String()); err != nil {
- return 0, err
- }
- return strconv.ParseUint(blockNumberStr, 10, 64)
-}
-
-// RetrieveHeaderAndTxCIDsByBlockNumber retrieves header CIDs and their associated tx CIDs by block number
-func (ecr *CIDRetriever) RetrieveHeaderAndTxCIDsByBlockNumber(blockNumber int64) ([]HeaderCIDRecord, error) {
- log.Debug("retrieving header cids and tx cids for block number ", blockNumber)
-
- var headerCIDs []HeaderCIDRecord
-
- // https://github.com/go-gorm/gorm/issues/4083#issuecomment-778883283
- // Will use join for TransactionCIDs once preload for 1:N is supported.
- err := ecr.gormDB.Preload("TransactionCIDs", func(tx *gorm.DB) *gorm.DB {
- return tx.Select("cid", "tx_hash", "index", "src", "dst", "header_id", "block_number")
- }).Joins("IPLD").Find(&headerCIDs, "header_cids.block_number = ?", blockNumber).Error
-
- if err != nil {
- log.Error("header cid retrieval error")
- return nil, err
- }
-
- return headerCIDs, nil
-}
-
-// RetrieveHeaderAndTxCIDsByBlockHash retrieves header CID and their associated tx CIDs by block hash (and optionally block number)
-func (ecr *CIDRetriever) RetrieveHeaderAndTxCIDsByBlockHash(blockHash common.Hash, blockNumber *big.Int) (HeaderCIDRecord, error) {
- log.Debug("retrieving header cid and tx cids for block hash ", blockHash.String())
-
- var headerCIDs []HeaderCIDRecord
-
- conditions := map[string]interface{}{"block_hash": blockHash.String()}
- if blockNumber != nil {
- conditions["header_cids.block_number"] = blockNumber.Int64()
- }
-
- // https://github.com/go-gorm/gorm/issues/4083#issuecomment-778883283
- // Will use join for TransactionCIDs once preload for 1:N is supported.
- err := ecr.gormDB.Preload("TransactionCIDs", func(tx *gorm.DB) *gorm.DB {
- return tx.Select("cid", "tx_hash", "index", "src", "dst", "header_id", "block_number")
- }).Joins("IPLD").Find(&headerCIDs, conditions).Error
-
- if err != nil {
- log.Error("header cid retrieval error")
- return HeaderCIDRecord{}, err
- }
-
- if len(headerCIDs) == 0 {
- return HeaderCIDRecord{}, errHeaderHashNotFound
- } else if len(headerCIDs) > 1 {
- return HeaderCIDRecord{}, errMultipleHeadersForHash
- }
-
- return headerCIDs[0], nil
-}
-
-// RetrieveTxCIDByHash returns the tx for the given tx hash (and optionally block number)
-func (ecr *CIDRetriever) RetrieveTxCIDByHash(txHash string, blockNumber *big.Int) (TransactionCIDRecord, error) {
- log.Debug("retrieving tx cid for tx hash ", txHash)
-
- var txCIDs []TransactionCIDRecord
-
- var err error
- if blockNumber != nil {
- err = ecr.gormDB.Joins("IPLD").Find(&txCIDs, "tx_hash = ? AND transaction_cids.header_id = (SELECT canonical_header_hash(transaction_cids.block_number)) AND transaction_cids.block_number = ?", txHash, blockNumber.Int64()).Error
- } else {
- err = ecr.gormDB.Joins("IPLD").Find(&txCIDs, "tx_hash = ? AND transaction_cids.header_id = (SELECT canonical_header_hash(transaction_cids.block_number))", txHash).Error
- }
- if err != nil {
- log.Error("tx retrieval error")
- return TransactionCIDRecord{}, err
- }
-
- if len(txCIDs) == 0 {
- return TransactionCIDRecord{}, errTxHashNotFound
- } else if len(txCIDs) > 1 {
- // a transaction can be part of a only one canonical block
- return TransactionCIDRecord{}, errTxHashInMultipleBlocks
- }
-
- return txCIDs[0], nil
-}
diff --git a/pkg/eth/retriever.go b/pkg/eth/retriever.go
new file mode 100644
index 00000000..e9a04622
--- /dev/null
+++ b/pkg/eth/retriever.go
@@ -0,0 +1,555 @@
+// VulcanizeDB
+// Copyright © 2019 Vulcanize
+
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Affero General Public License for more details.
+
+// You should have received a copy of the GNU Affero General Public License
+// along with this program. If not, see .
+
+package eth
+
+import (
+ "fmt"
+ "math/big"
+ "strconv"
+
+ "github.com/cerc-io/ipld-eth-server/v4/pkg/log"
+ "github.com/cerc-io/ipld-eth-server/v4/pkg/shared"
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/crypto"
+ "github.com/ethereum/go-ethereum/rlp"
+ "github.com/ethereum/go-ethereum/statediff/indexer/models"
+ "github.com/ethereum/go-ethereum/statediff/trie_helpers"
+ sdtypes "github.com/ethereum/go-ethereum/statediff/types"
+ "github.com/jmoiron/sqlx"
+ "github.com/lib/pq"
+ "gorm.io/driver/postgres"
+ "gorm.io/gorm"
+)
+
+// Retriever is used for fetching
+type Retriever struct {
+ db *sqlx.DB
+ gormDB *gorm.DB
+}
+
+type IPLDModelRecord struct {
+ models.IPLDModel
+}
+
+// TableName overrides the table name used by IPLD
+func (IPLDModelRecord) TableName() string {
+ return "ipld.blocks"
+}
+
+type HeaderCIDRecord struct {
+ CID string `gorm:"column:cid"`
+ BlockHash string `gorm:"primaryKey"`
+ BlockNumber string `gorm:"primaryKey"`
+ ParentHash string
+ Timestamp uint64
+ StateRoot string
+ TotalDifficulty string `gorm:"column:td"`
+ TxRoot string
+ RctRoot string `gorm:"column:receipt_root"`
+ UncleRoot string
+ Bloom []byte
+ MhKey string
+
+ // gorm doesn't check if foreign key exists in database.
+ // It is required to eager load relations using preload.
+ TransactionCIDs []TransactionCIDRecord `gorm:"foreignKey:HeaderID,BlockNumber;references:BlockHash,BlockNumber"`
+ IPLD IPLDModelRecord `gorm:"foreignKey:MhKey,BlockNumber;references:Key,BlockNumber"`
+}
+
+// TableName overrides the table name used by HeaderCIDRecord
+func (HeaderCIDRecord) TableName() string {
+ return "eth.header_cids"
+}
+
+type TransactionCIDRecord struct {
+ CID string `gorm:"column:cid"`
+ TxHash string `gorm:"primaryKey"`
+ BlockNumber string `gorm:"primaryKey"`
+ HeaderID string `gorm:"column:header_id"`
+ Index int64
+ Src string
+ Dst string
+ MhKey string
+ IPLD IPLDModelRecord `gorm:"foreignKey:MhKey,BlockNumber;references:Key,BlockNumber"`
+}
+
+// TableName overrides the table name used by TransactionCIDRecord
+func (TransactionCIDRecord) TableName() string {
+ return "eth.transaction_cids"
+}
+
+// NewRetriever returns a pointer to a new Retriever which supports the Retriever interface
+func NewRetriever(db *sqlx.DB) *Retriever {
+ gormDB, err := gorm.Open(postgres.New(postgres.Config{
+ Conn: db,
+ }), &gorm.Config{})
+
+ if err != nil {
+ log.Error(err)
+ return nil
+ }
+
+ return &Retriever{
+ db: db,
+ gormDB: gormDB,
+ }
+}
+
+// RetrieveFirstBlockNumber is used to retrieve the first block number in the db
+func (r *Retriever) RetrieveFirstBlockNumber() (int64, error) {
+ var blockNumber int64
+ err := r.db.Get(&blockNumber, "SELECT block_number FROM eth.header_cids ORDER BY block_number ASC LIMIT 1")
+ return blockNumber, err
+}
+
+// RetrieveLastBlockNumber is used to retrieve the latest block number in the db
+func (r *Retriever) RetrieveLastBlockNumber() (int64, error) {
+ var blockNumber int64
+ err := r.db.Get(&blockNumber, "SELECT block_number FROM eth.header_cids ORDER BY block_number DESC LIMIT 1")
+ return blockNumber, err
+}
+
+func topicFilterCondition(id *int, topics [][]string, args []interface{}, pgStr string, first bool) (string, []interface{}) {
+ for i, topicSet := range topics {
+ if len(topicSet) == 0 {
+ continue
+ }
+
+ if !first {
+ pgStr += " AND"
+ } else {
+ first = false
+ }
+ pgStr += fmt.Sprintf(` eth.log_cids.topic%d = ANY ($%d)`, i, *id)
+ args = append(args, pq.Array(topicSet))
+ *id++
+ }
+ return pgStr, args
+}
+
+func logFilterCondition(id *int, pgStr string, args []interface{}, rctFilter ReceiptFilter) (string, []interface{}) {
+ if len(rctFilter.LogAddresses) > 0 {
+ pgStr += fmt.Sprintf(` AND eth.log_cids.address = ANY ($%d)`, *id)
+ args = append(args, pq.Array(rctFilter.LogAddresses))
+ *id++
+ }
+
+ // Filter on topics if there are any
+ if hasTopics(rctFilter.Topics) {
+ pgStr, args = topicFilterCondition(id, rctFilter.Topics, args, pgStr, false)
+ }
+
+ return pgStr, args
+}
+
+func receiptFilterConditions(id *int, pgStr string, args []interface{}, rctFilter ReceiptFilter, txHashes []string) (string, []interface{}) {
+ rctCond := " AND (receipt_cids.tx_id = ANY ( "
+ logQuery := "SELECT rct_id FROM eth.log_cids WHERE"
+ if len(rctFilter.LogAddresses) > 0 {
+ // Filter on log contract addresses if there are any
+ pgStr += fmt.Sprintf(`%s %s eth.log_cids.address = ANY ($%d)`, rctCond, logQuery, *id)
+ args = append(args, pq.Array(rctFilter.LogAddresses))
+ *id++
+
+ // Filter on topics if there are any
+ if hasTopics(rctFilter.Topics) {
+ pgStr, args = topicFilterCondition(id, rctFilter.Topics, args, pgStr, false)
+ }
+
+ pgStr += ")"
+
+ // Filter on txHashes if there are any, and we are matching txs
+ if rctFilter.MatchTxs && len(txHashes) > 0 {
+ pgStr += fmt.Sprintf(` OR receipt_cids.tx_id = ANY($%d)`, *id)
+ args = append(args, pq.Array(txHashes))
+ }
+ pgStr += ")"
+ } else { // If there are no contract addresses to filter on
+ // Filter on topics if there are any
+ if hasTopics(rctFilter.Topics) {
+ pgStr += rctCond + logQuery
+ pgStr, args = topicFilterCondition(id, rctFilter.Topics, args, pgStr, true)
+ pgStr += ")"
+ // Filter on txHashes if there are any, and we are matching txs
+ if rctFilter.MatchTxs && len(txHashes) > 0 {
+ pgStr += fmt.Sprintf(` OR receipt_cids.tx_id = ANY($%d)`, *id)
+ args = append(args, pq.Array(txHashes))
+ }
+ pgStr += ")"
+ } else if rctFilter.MatchTxs && len(txHashes) > 0 {
+ // If there are no contract addresses or topics to filter on,
+ // Filter on txHashes if there are any, and we are matching txs
+ pgStr += fmt.Sprintf(` AND receipt_cids.tx_id = ANY($%d)`, *id)
+ args = append(args, pq.Array(txHashes))
+ }
+ }
+
+ return pgStr, args
+}
+
+// RetrieveFilteredGQLLogs retrieves and returns all the log CIDs provided blockHash that conform to the provided
+// filter parameters.
+func (r *Retriever) RetrieveFilteredGQLLogs(tx *sqlx.Tx, rctFilter ReceiptFilter, blockHash *common.Hash, blockNumber *big.Int) ([]LogResult, error) {
+ log.Debug("retrieving log cids for receipt ids with block hash", blockHash.String())
+ args := make([]interface{}, 0, 4)
+ id := 1
+ pgStr := RetrieveFilteredGQLLogs
+ args = append(args, blockHash.String())
+ id++
+
+ if blockNumber != nil {
+ pgStr += ` AND receipt_cids.block_number = $2`
+ id++
+ args = append(args, blockNumber.Int64())
+ }
+
+ pgStr, args = logFilterCondition(&id, pgStr, args, rctFilter)
+ pgStr += ` ORDER BY log_cids.index`
+
+ logCIDs := make([]LogResult, 0)
+ err := tx.Select(&logCIDs, pgStr, args...)
+ if err != nil {
+ return nil, err
+ }
+
+ return logCIDs, nil
+}
+
+// RetrieveFilteredLogs retrieves and returns all the log CIDs provided blockHeight or blockHash that conform to the provided
+// filter parameters.
+func (r *Retriever) RetrieveFilteredLogs(tx *sqlx.Tx, rctFilter ReceiptFilter, blockNumber int64, blockHash *common.Hash) ([]LogResult, error) {
+ log.Debug("retrieving log cids for receipt ids")
+ args := make([]interface{}, 0, 4)
+ pgStr := RetrieveFilteredLogs
+ id := 1
+ if blockNumber > 0 {
+ pgStr += fmt.Sprintf(` AND header_cids.block_number = $%d`, id)
+ args = append(args, blockNumber)
+ id++
+ }
+ if blockHash != nil {
+ pgStr += fmt.Sprintf(` AND header_cids.block_hash = $%d`, id)
+ args = append(args, blockHash.String())
+ id++
+ }
+
+ pgStr, args = logFilterCondition(&id, pgStr, args, rctFilter)
+ pgStr += ` ORDER BY log_cids.index`
+
+ logCIDs := make([]LogResult, 0)
+ err := tx.Select(&logCIDs, pgStr, args...)
+ if err != nil {
+ return nil, err
+ }
+
+ return logCIDs, nil
+}
+
+func hasTopics(topics [][]string) bool {
+ for _, topicSet := range topics {
+ if len(topicSet) > 0 {
+ return true
+ }
+ }
+ return false
+}
+
+// RetrieveBlockNumberByHash returns the block number for the given block hash
+func (r *Retriever) RetrieveBlockNumberByHash(tx *sqlx.Tx, blockHash common.Hash) (uint64, error) {
+ log.Debug("retrieving block number for block hash ", blockHash.String())
+ pgStr := `SELECT CAST(block_number as TEXT) FROM eth.header_cids WHERE block_hash = $1`
+ var blockNumberStr string
+ if err := tx.Get(&blockNumberStr, pgStr, blockHash.String()); err != nil {
+ return 0, err
+ }
+ return strconv.ParseUint(blockNumberStr, 10, 64)
+}
+
+// RetrieveHeaderAndTxCIDsByBlockNumber retrieves header CIDs and their associated tx CIDs by block number
+func (r *Retriever) RetrieveHeaderAndTxCIDsByBlockNumber(blockNumber int64) ([]HeaderCIDRecord, error) {
+ log.Debug("retrieving header cids and tx cids for block number ", blockNumber)
+
+ var headerCIDs []HeaderCIDRecord
+
+ // https://github.com/go-gorm/gorm/issues/4083#issuecomment-778883283
+ // Will use join for TransactionCIDs once preload for 1:N is supported.
+ err := r.gormDB.Preload("TransactionCIDs", func(tx *gorm.DB) *gorm.DB {
+ return tx.Select("cid", "tx_hash", "index", "src", "dst", "header_id", "block_number")
+ }).Joins("IPLD").Find(&headerCIDs, "header_cids.block_number = ?", blockNumber).Error
+
+ if err != nil {
+ log.Error("header cid retrieval error")
+ return nil, err
+ }
+
+ return headerCIDs, nil
+}
+
+// RetrieveHeaderAndTxCIDsByBlockHash retrieves header CID and their associated tx CIDs by block hash (and optionally block number)
+func (r *Retriever) RetrieveHeaderAndTxCIDsByBlockHash(blockHash common.Hash, blockNumber *big.Int) (HeaderCIDRecord, error) {
+ log.Debug("retrieving header cid and tx cids for block hash ", blockHash.String())
+
+ var headerCIDs []HeaderCIDRecord
+
+ conditions := map[string]interface{}{"block_hash": blockHash.String()}
+ if blockNumber != nil {
+ conditions["header_cids.block_number"] = blockNumber.Int64()
+ }
+
+ // https://github.com/go-gorm/gorm/issues/4083#issuecomment-778883283
+ // Will use join for TransactionCIDs once preload for 1:N is supported.
+ err := r.gormDB.Preload("TransactionCIDs", func(tx *gorm.DB) *gorm.DB {
+ return tx.Select("cid", "tx_hash", "index", "src", "dst", "header_id", "block_number")
+ }).Joins("IPLD").Find(&headerCIDs, conditions).Error
+
+ if err != nil {
+ log.Error("header cid retrieval error")
+ return HeaderCIDRecord{}, err
+ }
+
+ if len(headerCIDs) == 0 {
+ return HeaderCIDRecord{}, errHeaderHashNotFound
+ } else if len(headerCIDs) > 1 {
+ return HeaderCIDRecord{}, errMultipleHeadersForHash
+ }
+
+ return headerCIDs[0], nil
+}
+
+// RetrieveTxCIDByHash returns the tx for the given tx hash (and optionally block number)
+func (r *Retriever) RetrieveTxCIDByHash(txHash string, blockNumber *big.Int) (TransactionCIDRecord, error) {
+ log.Debug("retrieving tx cid for tx hash ", txHash)
+
+ var txCIDs []TransactionCIDRecord
+
+ var err error
+ if blockNumber != nil {
+ err = r.gormDB.Joins("IPLD").Find(&txCIDs, "tx_hash = ? AND transaction_cids.header_id = (SELECT canonical_header_hash(transaction_cids.block_number)) AND transaction_cids.block_number = ?", txHash, blockNumber.Int64()).Error
+ } else {
+ err = r.gormDB.Joins("IPLD").Find(&txCIDs, "tx_hash = ? AND transaction_cids.header_id = (SELECT canonical_header_hash(transaction_cids.block_number))", txHash).Error
+ }
+ if err != nil {
+ log.Error("tx retrieval error")
+ return TransactionCIDRecord{}, err
+ }
+
+ if len(txCIDs) == 0 {
+ return TransactionCIDRecord{}, errTxHashNotFound
+ } else if len(txCIDs) > 1 {
+ // a transaction can be part of a only one canonical block
+ return TransactionCIDRecord{}, errTxHashInMultipleBlocks
+ }
+
+ return txCIDs[0], nil
+}
+
+var EmptyNodeValue = make([]byte, common.HashLength)
+
+// RetrieveHeaderByHash returns the cid and rlp bytes for the header corresponding to the provided block hash
+func (r *Retriever) RetrieveHeaderByHash(tx *sqlx.Tx, hash common.Hash) (string, []byte, error) {
+ headerResult := new(ipldResult)
+ return headerResult.CID, headerResult.Data, tx.Get(headerResult, RetrieveHeaderByHashPgStr, hash.Hex())
+}
+
+// RetrieveUncles returns the cids and rlp bytes for the uncles corresponding to the provided block hash, number (of non-omner root block)
+func (r *Retriever) RetrieveUncles(tx *sqlx.Tx, hash common.Hash, number uint64) ([]string, [][]byte, error) {
+ uncleResults := make([]ipldResult, 0)
+ if err := tx.Select(&uncleResults, RetrieveUnclesPgStr, hash.Hex(), number); err != nil {
+ return nil, nil, err
+ }
+ cids := make([]string, len(uncleResults))
+ uncles := make([][]byte, len(uncleResults))
+ for i, res := range uncleResults {
+ cids[i] = res.CID
+ uncles[i] = res.Data
+ }
+ return cids, uncles, nil
+}
+
+// RetrieveUnclesByBlockHash returns the cids and rlp bytes for the uncles corresponding to the provided block hash (of non-omner root block)
+func (r *Retriever) RetrieveUnclesByBlockHash(tx *sqlx.Tx, hash common.Hash) ([]string, [][]byte, error) {
+ uncleResults := make([]ipldResult, 0)
+ if err := tx.Select(&uncleResults, RetrieveUnclesByBlockHashPgStr, hash.Hex()); err != nil {
+ return nil, nil, err
+ }
+ cids := make([]string, len(uncleResults))
+ uncles := make([][]byte, len(uncleResults))
+ for i, res := range uncleResults {
+ cids[i] = res.CID
+ uncles[i] = res.Data
+ }
+ return cids, uncles, nil
+}
+
+// RetrieveTransactions returns the cids and rlp bytes for the transactions corresponding to the provided block hash, number
+func (r *Retriever) RetrieveTransactions(tx *sqlx.Tx, hash common.Hash, number uint64) ([]string, [][]byte, error) {
+ txResults := make([]ipldResult, 0)
+ if err := tx.Select(&txResults, RetrieveTransactionsPgStr, hash.Hex(), number); err != nil {
+ return nil, nil, err
+ }
+ cids := make([]string, len(txResults))
+ txs := make([][]byte, len(txResults))
+ for i, res := range txResults {
+ cids[i] = res.CID
+ txs[i] = res.Data
+ }
+ return cids, txs, nil
+}
+
+// RetrieveTransactionsByBlockHash returns the cids and rlp bytes for the transactions corresponding to the provided block hash
+func (r *Retriever) RetrieveTransactionsByBlockHash(tx *sqlx.Tx, hash common.Hash) ([]string, [][]byte, error) {
+ txResults := make([]ipldResult, 0)
+ if err := tx.Select(&txResults, RetrieveTransactionsByBlockHashPgStr, hash.Hex()); err != nil {
+ return nil, nil, err
+ }
+ cids := make([]string, len(txResults))
+ txs := make([][]byte, len(txResults))
+ for i, res := range txResults {
+ cids[i] = res.CID
+ txs[i] = res.Data
+ }
+ return cids, txs, nil
+}
+
+// DecodeLeafNode decodes the leaf node data
+func DecodeLeafNode(node []byte) ([]byte, error) {
+ var nodeElements []interface{}
+ if err := rlp.DecodeBytes(node, &nodeElements); err != nil {
+ return nil, err
+ }
+ ty, err := trie_helpers.CheckKeyType(nodeElements)
+ if err != nil {
+ return nil, err
+ }
+
+ if ty != sdtypes.Leaf {
+ return nil, fmt.Errorf("expected leaf node but found %s", ty)
+ }
+ return nodeElements[1].([]byte), nil
+}
+
+// RetrieveReceipts returns the cids and rlp bytes for the receipts corresponding to the provided block hash, number.
+// cid returned corresponds to the leaf node data which contains the receipt.
+func (r *Retriever) RetrieveReceipts(tx *sqlx.Tx, hash common.Hash, number uint64) ([]string, [][]byte, []common.Hash, error) {
+ rctResults := make([]rctIpldResult, 0)
+ if err := tx.Select(&rctResults, RetrieveReceiptsPgStr, hash.Hex(), number); err != nil {
+ return nil, nil, nil, err
+ }
+ cids := make([]string, len(rctResults))
+ rcts := make([][]byte, len(rctResults))
+ txs := make([]common.Hash, len(rctResults))
+
+ for i, res := range rctResults {
+ cids[i] = res.LeafCID
+ nodeVal, err := DecodeLeafNode(res.Data)
+ if err != nil {
+ return nil, nil, nil, err
+ }
+ rcts[i] = nodeVal
+ txs[i] = common.HexToHash(res.TxHash)
+ }
+
+ return cids, rcts, txs, nil
+}
+
+// RetrieveReceiptsByBlockHash returns the cids and rlp bytes for the receipts corresponding to the provided block hash.
+// cid returned corresponds to the leaf node data which contains the receipt.
+func (r *Retriever) RetrieveReceiptsByBlockHash(tx *sqlx.Tx, hash common.Hash) ([]string, [][]byte, []common.Hash, error) {
+ rctResults := make([]rctIpldResult, 0)
+ if err := tx.Select(&rctResults, RetrieveReceiptsByBlockHashPgStr, hash.Hex()); err != nil {
+ return nil, nil, nil, err
+ }
+ cids := make([]string, len(rctResults))
+ rcts := make([][]byte, len(rctResults))
+ txs := make([]common.Hash, len(rctResults))
+
+ for i, res := range rctResults {
+ cids[i] = res.LeafCID
+ nodeVal, err := DecodeLeafNode(res.Data)
+ if err != nil {
+ return nil, nil, nil, err
+ }
+ rcts[i] = nodeVal
+ txs[i] = common.HexToHash(res.TxHash)
+ }
+
+ return cids, rcts, txs, nil
+}
+
+// RetrieveAccountByAddressAndBlockHash returns the cid and rlp bytes for the account corresponding to the provided address and block hash
+// TODO: ensure this handles deleted accounts appropriately
+func (r *Retriever) RetrieveAccountByAddressAndBlockHash(address common.Address, hash common.Hash) (string, []byte, error) {
+ accountResult := new(nodeInfo)
+ leafKey := crypto.Keccak256Hash(address.Bytes())
+ if err := r.db.Get(accountResult, RetrieveAccountByLeafKeyAndBlockHashPgStr, leafKey.Hex(), hash.Hex()); err != nil {
+ return "", nil, err
+ }
+
+ if accountResult.NodeType == sdtypes.Removed.Int() {
+ return "", EmptyNodeValue, nil
+ }
+
+ blockNumber, err := strconv.ParseUint(accountResult.BlockNumber, 10, 64)
+ if err != nil {
+ return "", nil, err
+ }
+ accountResult.Data, err = shared.FetchIPLD(r.db, accountResult.CID, blockNumber)
+ if err != nil {
+ return "", nil, err
+ }
+
+ var i []interface{}
+ if err := rlp.DecodeBytes(accountResult.Data, &i); err != nil {
+ return "", nil, fmt.Errorf("error decoding state leaf node rlp: %s", err.Error())
+ }
+ if len(i) != 2 {
+ return "", nil, fmt.Errorf("eth Retriever expected state leaf node rlp to decode into two elements")
+ }
+ return accountResult.CID, i[1].([]byte), nil
+}
+
+// RetrieveStorageAtByAddressAndStorageSlotAndBlockHash returns the cid and rlp bytes for the storage value corresponding to the provided address, storage slot, and block hash
+func (r *Retriever) RetrieveStorageAtByAddressAndStorageSlotAndBlockHash(address common.Address, key, hash common.Hash) (string, []byte, []byte, error) {
+ storageResult := new(nodeInfo)
+ stateLeafKey := crypto.Keccak256Hash(address.Bytes())
+ storageHash := crypto.Keccak256Hash(key.Bytes())
+ if err := r.db.Get(storageResult, RetrieveStorageLeafByAddressHashAndLeafKeyAndBlockHashPgStr, stateLeafKey.Hex(), storageHash.Hex(), hash.Hex()); err != nil {
+ return "", nil, nil, err
+ }
+ if storageResult.StateLeafRemoved || storageResult.NodeType == sdtypes.Removed.Int() {
+ return "", EmptyNodeValue, EmptyNodeValue, nil
+ }
+
+ blockNumber, err := strconv.ParseUint(storageResult.BlockNumber, 10, 64)
+ if err != nil {
+ return "", nil, nil, err
+ }
+ storageResult.Data, err = shared.FetchIPLD(r.db, storageResult.CID, blockNumber)
+ if err != nil {
+ return "", nil, nil, err
+ }
+
+ var i []interface{}
+ if err := rlp.DecodeBytes(storageResult.Data, &i); err != nil {
+ err = fmt.Errorf("error decoding storage leaf node rlp: %s", err.Error())
+ return "", nil, nil, err
+ }
+ if len(i) != 2 {
+ return "", nil, nil, fmt.Errorf("eth Retriever expected storage leaf node rlp to decode into two elements")
+ }
+ return storageResult.CID, storageResult.Data, i[1].([]byte), nil
+}
diff --git a/pkg/eth/cid_retriever_test.go b/pkg/eth/retriever_test.go
similarity index 98%
rename from pkg/eth/cid_retriever_test.go
rename to pkg/eth/retriever_test.go
index 2941da0d..b2bb27dd 100644
--- a/pkg/eth/cid_retriever_test.go
+++ b/pkg/eth/retriever_test.go
@@ -33,13 +33,13 @@ var _ = Describe("Retriever", func() {
var (
db *sqlx.DB
diffIndexer interfaces.StateDiffIndexer
- retriever *eth.CIDRetriever
+ retriever *eth.Retriever
)
BeforeEach(func() {
db = shared.SetupDB()
diffIndexer = shared.SetupTestStateDiffIndexer(ctx, params.TestChainConfig, test_helpers.Genesis.Hash())
- retriever = eth.NewCIDRetriever(db)
+ retriever = eth.NewRetriever(db)
})
AfterEach(func() {
shared.TearDownDB(db)
diff --git a/pkg/eth/test_helpers/test_data.go b/pkg/eth/test_helpers/test_data.go
index 2e5d40b9..37553009 100644
--- a/pkg/eth/test_helpers/test_data.go
+++ b/pkg/eth/test_helpers/test_data.go
@@ -17,28 +17,22 @@
package test_helpers
import (
- "bytes"
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"math/big"
+ "github.com/cerc-io/ipld-eth-server/v4/pkg/eth"
"github.com/cerc-io/ipld-eth-server/v4/pkg/log"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/rlp"
- "github.com/ethereum/go-ethereum/statediff/indexer/ipld"
"github.com/ethereum/go-ethereum/statediff/indexer/models"
- "github.com/ethereum/go-ethereum/statediff/indexer/shared"
testhelpers "github.com/ethereum/go-ethereum/statediff/test_helpers"
sdtypes "github.com/ethereum/go-ethereum/statediff/types"
"github.com/ethereum/go-ethereum/trie"
- blocks "github.com/ipfs/go-block-format"
- "github.com/multiformats/go-multihash"
-
- "github.com/cerc-io/ipld-eth-server/v4/pkg/eth"
)
// Test variables
@@ -75,10 +69,8 @@ var (
Extra: []byte{},
},
}
- ReceiptsRlp, _ = rlp.EncodeToBytes(MockReceipts)
- MockBlock = createNewBlock(&MockHeader, MockTransactions, MockUncles, MockReceipts, new(trie.Trie))
- MockHeaderRlp, _ = rlp.EncodeToBytes(MockBlock.Header())
- MockChildHeader = types.Header{
+ MockBlock = createNewBlock(&MockHeader, MockTransactions, MockUncles, MockReceipts, new(trie.Trie))
+ MockChildHeader = types.Header{
Time: 0,
Number: new(big.Int).Add(BlockNumber, common.Big1),
Root: common.HexToHash("0x0"),
@@ -89,7 +81,6 @@ var (
ParentHash: MockBlock.Header().Hash(),
}
MockChild = types.NewBlock(&MockChildHeader, MockTransactions, MockUncles, MockReceipts, new(trie.Trie))
- MockChildRlp, _ = rlp.EncodeToBytes(MockChild.Header())
Address = common.HexToAddress("0xaE9BEa628c4Ce503DcFD7E305CaB4e29E7476592")
AnotherAddress = common.HexToAddress("0xaE9BEa628c4Ce503DcFD7E305CaB4e29E7476593")
AnotherAddress1 = common.HexToAddress("0xaE9BEa628c4Ce503DcFD7E305CaB4e29E7476594")
@@ -157,41 +148,10 @@ var (
Index: 5,
}
- Tx1 = GetTxnRlp(0, MockTransactions)
- Tx2 = GetTxnRlp(1, MockTransactions)
- Tx3 = GetTxnRlp(2, MockTransactions)
- Tx4 = GetTxnRlp(3, MockTransactions)
-
- rctCIDs, rctIPLDData, _ = GetRctLeafNodeData(MockReceipts)
- HeaderCID, _ = ipld.RawdataToCid(ipld.MEthHeader, MockHeaderRlp, multihash.KECCAK_256)
- HeaderMhKey = shared.MultihashKeyFromCID(HeaderCID)
- Trx1CID, _ = ipld.RawdataToCid(ipld.MEthTx, Tx1, multihash.KECCAK_256)
- Trx1MhKey = shared.MultihashKeyFromCID(Trx1CID)
- Trx2CID, _ = ipld.RawdataToCid(ipld.MEthTx, Tx2, multihash.KECCAK_256)
- Trx2MhKey = shared.MultihashKeyFromCID(Trx2CID)
- Trx3CID, _ = ipld.RawdataToCid(ipld.MEthTx, Tx3, multihash.KECCAK_256)
- Trx3MhKey = shared.MultihashKeyFromCID(Trx3CID)
- Trx4CID, _ = ipld.RawdataToCid(ipld.MEthTx, Tx4, multihash.KECCAK_256)
- Trx4MhKey = shared.MultihashKeyFromCID(Trx4CID)
- Rct1CID = rctCIDs[0]
- Rct1MhKey = shared.MultihashKeyFromCID(Rct1CID)
- Rct2CID = rctCIDs[1]
- Rct2MhKey = shared.MultihashKeyFromCID(Rct2CID)
- Rct3CID = rctCIDs[2]
- Rct3MhKey = shared.MultihashKeyFromCID(Rct3CID)
- Rct4CID = rctCIDs[3]
- Rct4MhKey = shared.MultihashKeyFromCID(Rct4CID)
- State1CID, _ = ipld.RawdataToCid(ipld.MEthStateTrie, ContractLeafNode, multihash.KECCAK_256)
- State1MhKey = shared.MultihashKeyFromCID(State1CID)
- State2CID, _ = ipld.RawdataToCid(ipld.MEthStateTrie, AccountLeafNode, multihash.KECCAK_256)
- State2MhKey = shared.MultihashKeyFromCID(State2CID)
- StorageCID, _ = ipld.RawdataToCid(ipld.MEthStorageTrie, StorageLeafNode, multihash.KECCAK_256)
- StorageMhKey = shared.MultihashKeyFromCID(StorageCID)
- Rct1IPLD = rctIPLDData[0]
- Rct2IPLD = rctIPLDData[1]
- Rct3IPLD = rctIPLDData[2]
- Rct4IPLD = rctIPLDData[3]
- MockTrxMeta = []models.TxModel{
+ rctCIDs, _, _ = GetRctLeafNodeData(MockReceipts)
+ Rct1CID = rctCIDs[0]
+ Rct4CID = rctCIDs[3]
+ MockTrxMeta = []models.TxModel{
{
CID: "", // This is empty until we go to publish to ipfs
MhKey: "",
@@ -229,48 +189,6 @@ var (
Data: []byte{},
},
}
- MockTrxMetaPostPublsh = []models.TxModel{
- {
- BlockNumber: "1",
- CID: Trx1CID.String(), // This is empty until we go to publish to ipfs
- MhKey: Trx1MhKey,
- Src: SenderAddr.Hex(),
- Dst: Address.String(),
- Index: 0,
- TxHash: MockTransactions[0].Hash().String(),
- Data: []byte{},
- },
- {
- BlockNumber: "1",
- CID: Trx2CID.String(),
- MhKey: Trx2MhKey,
- Src: SenderAddr.Hex(),
- Dst: AnotherAddress.String(),
- Index: 1,
- TxHash: MockTransactions[1].Hash().String(),
- Data: []byte{},
- },
- {
- BlockNumber: "1",
- CID: Trx3CID.String(),
- MhKey: Trx3MhKey,
- Src: SenderAddr.Hex(),
- Dst: "",
- Index: 2,
- TxHash: MockTransactions[2].Hash().String(),
- Data: MockContractByteCode,
- },
- {
- BlockNumber: "1",
- CID: Trx4CID.String(),
- MhKey: Trx4MhKey,
- Src: SenderAddr.Hex(),
- Dst: AnotherAddress1.String(),
- Index: 3,
- TxHash: MockTransactions[3].Hash().String(),
- Data: []byte{},
- },
- }
MockRctMeta = []models.ReceiptModel{
{
LeafCID: "",
@@ -298,41 +216,6 @@ var (
},
}
- MockRctMetaPostPublish = []models.ReceiptModel{
- {
- BlockNumber: "1",
- HeaderID: MockBlock.Hash().String(),
- LeafCID: Rct1CID.String(),
- LeafMhKey: Rct1MhKey,
- Contract: "",
- ContractHash: "",
- },
- {
- BlockNumber: "1",
- HeaderID: MockBlock.Hash().String(),
- LeafCID: Rct2CID.String(),
- LeafMhKey: Rct2MhKey,
- Contract: "",
- ContractHash: "",
- },
- {
- BlockNumber: "1",
- HeaderID: MockBlock.Hash().String(),
- LeafCID: Rct3CID.String(),
- LeafMhKey: Rct3MhKey,
- Contract: ContractAddress.String(),
- ContractHash: ContractHash,
- },
- {
- BlockNumber: "1",
- HeaderID: MockBlock.Hash().String(),
- LeafCID: Rct4CID.String(),
- LeafMhKey: Rct4MhKey,
- Contract: "",
- ContractHash: "",
- },
- }
-
// statediff data
storageLocation = common.HexToHash("0")
StorageLeafKey = crypto.Keccak256Hash(storageLocation[:]).Bytes()
@@ -401,24 +284,6 @@ var (
StorageNodes: []sdtypes.StorageNode{},
},
}
- MockStateMetaPostPublish = []models.StateNodeModel{
- {
- BlockNumber: "1",
- CID: State1CID.String(),
- MhKey: State1MhKey,
- Path: []byte{'\x06'},
- NodeType: 2,
- StateKey: common.BytesToHash(ContractLeafKey).Hex(),
- },
- {
- BlockNumber: "1",
- CID: State2CID.String(),
- MhKey: State2MhKey,
- Path: []byte{'\x0c'},
- NodeType: 2,
- StateKey: common.BytesToHash(AccountLeafKey).Hex(),
- },
- }
MockStorageNodes = map[string][]sdtypes.StorageNode{
contractPath: {
{
@@ -439,149 +304,6 @@ var (
StorageNodes: MockStorageNodes,
StateNodes: MockStateNodes,
}
- MockConvertedPayloadForChild = eth.ConvertedPayload{
- TotalDifficulty: MockChild.Difficulty(),
- Block: MockChild,
- Receipts: MockReceipts,
- TxMetaData: MockTrxMeta,
- ReceiptMetaData: MockRctMeta,
- StorageNodes: MockStorageNodes,
- StateNodes: MockStateNodes,
- }
-
- Reward = shared.CalcEthBlockReward(MockBlock.Header(), MockBlock.Uncles(), MockBlock.Transactions(), MockReceipts)
- MockCIDWrapper = ð.CIDWrapper{
- BlockNumber: new(big.Int).Set(BlockNumber),
- Header: models.HeaderModel{
- BlockNumber: "1",
- BlockHash: MockBlock.Hash().String(),
- ParentHash: "0x0000000000000000000000000000000000000000000000000000000000000000",
- CID: HeaderCID.String(),
- MhKey: HeaderMhKey,
- TotalDifficulty: MockBlock.Difficulty().String(),
- Reward: Reward.String(),
- StateRoot: MockBlock.Root().String(),
- RctRoot: MockBlock.ReceiptHash().String(),
- TxRoot: MockBlock.TxHash().String(),
- UncleRoot: MockBlock.UncleHash().String(),
- Bloom: MockBlock.Bloom().Bytes(),
- Timestamp: MockBlock.Time(),
- TimesValidated: 1,
- Coinbase: "0x0000000000000000000000000000000000000000",
- },
- Transactions: MockTrxMetaPostPublsh,
- Receipts: MockRctMetaPostPublish,
- Uncles: []models.UncleModel{},
- StateNodes: MockStateMetaPostPublish,
- StorageNodes: []models.StorageNodeWithStateKeyModel{
- {
- BlockNumber: "1",
- Path: []byte{},
- CID: StorageCID.String(),
- MhKey: StorageMhKey,
- NodeType: 2,
- StateKey: common.BytesToHash(ContractLeafKey).Hex(),
- StorageKey: common.BytesToHash(StorageLeafKey).Hex(),
- },
- },
- }
-
- HeaderIPLD, _ = blocks.NewBlockWithCid(MockHeaderRlp, HeaderCID)
- Trx1IPLD, _ = blocks.NewBlockWithCid(Tx1, Trx1CID)
- Trx2IPLD, _ = blocks.NewBlockWithCid(Tx2, Trx2CID)
- Trx3IPLD, _ = blocks.NewBlockWithCid(Tx3, Trx3CID)
- Trx4IPLD, _ = blocks.NewBlockWithCid(Tx4, Trx4CID)
- State1IPLD, _ = blocks.NewBlockWithCid(ContractLeafNode, State1CID)
- State2IPLD, _ = blocks.NewBlockWithCid(AccountLeafNode, State2CID)
- StorageIPLD, _ = blocks.NewBlockWithCid(StorageLeafNode, StorageCID)
-
- MockIPLDs = eth.IPLDs{
- BlockNumber: new(big.Int).Set(BlockNumber),
- Header: models.IPLDModel{
- BlockNumber: BlockNumber.String(),
- Data: HeaderIPLD.RawData(),
- Key: HeaderIPLD.Cid().String(),
- },
- Transactions: []models.IPLDModel{
- {
- BlockNumber: BlockNumber.String(),
- Data: Trx1IPLD.RawData(),
- Key: Trx1IPLD.Cid().String(),
- },
- {
- BlockNumber: BlockNumber.String(),
- Data: Trx2IPLD.RawData(),
- Key: Trx2IPLD.Cid().String(),
- },
- {
- BlockNumber: BlockNumber.String(),
- Data: Trx3IPLD.RawData(),
- Key: Trx3IPLD.Cid().String(),
- },
- {
- BlockNumber: BlockNumber.String(),
- Data: Trx4IPLD.RawData(),
- Key: Trx4IPLD.Cid().String(),
- },
- },
- Receipts: []models.IPLDModel{
- {
- BlockNumber: BlockNumber.String(),
- Data: Rct1IPLD,
- Key: Rct1CID.String(),
- },
- {
- BlockNumber: BlockNumber.String(),
- Data: Rct2IPLD,
- Key: Rct2CID.String(),
- },
- {
- BlockNumber: BlockNumber.String(),
- Data: Rct3IPLD,
- Key: Rct3CID.String(),
- },
- {
- BlockNumber: BlockNumber.String(),
- Data: Rct4IPLD,
- Key: Rct4CID.String(),
- },
- },
- StateNodes: []eth.StateNode{
- {
- StateLeafKey: common.BytesToHash(ContractLeafKey),
- Type: sdtypes.Leaf,
- IPLD: models.IPLDModel{
- BlockNumber: BlockNumber.String(),
- Data: State1IPLD.RawData(),
- Key: State1IPLD.Cid().String(),
- },
- Path: []byte{'\x06'},
- },
- {
- StateLeafKey: common.BytesToHash(AccountLeafKey),
- Type: sdtypes.Leaf,
- IPLD: models.IPLDModel{
- BlockNumber: BlockNumber.String(),
- Data: State2IPLD.RawData(),
- Key: State2IPLD.Cid().String(),
- },
- Path: []byte{'\x0c'},
- },
- },
- StorageNodes: []eth.StorageNode{
- {
- StateLeafKey: common.BytesToHash(ContractLeafKey),
- StorageLeafKey: common.BytesToHash(StorageLeafKey),
- Type: sdtypes.Leaf,
- IPLD: models.IPLDModel{
- BlockNumber: BlockNumber.String(),
- Data: StorageIPLD.RawData(),
- Key: StorageIPLD.Cid().String(),
- },
- Path: []byte{},
- },
- },
- }
LondonBlockNum = new(big.Int).Add(BlockNumber, big.NewInt(2))
MockLondonHeader = types.Header{
@@ -741,21 +463,3 @@ func createLegacyTransactionsAndReceipts() (types.Transactions, types.Receipts,
return types.Transactions{signedTrx1, signedTrx2, signedTrx3, signedTrx4}, types.Receipts{mockReceipt1, mockReceipt2, mockReceipt3, mockReceipt4}, SenderAddr
}
-
-func GetTxnRlp(num int, txs types.Transactions) []byte {
- buf := new(bytes.Buffer)
- txs.EncodeIndex(num, buf)
- tx := make([]byte, buf.Len())
- copy(tx, buf.Bytes())
- buf.Reset()
- return tx
-}
-
-func GetRctRlp(num int, rcts types.Receipts) []byte {
- buf := new(bytes.Buffer)
- rcts.EncodeIndex(num, buf)
- rct := make([]byte, buf.Len())
- copy(rct, buf.Bytes())
- buf.Reset()
- return rct
-}
diff --git a/pkg/graphql/graphql.go b/pkg/graphql/graphql.go
index afcbd0f3..1f73b606 100644
--- a/pkg/graphql/graphql.go
+++ b/pkg/graphql/graphql.go
@@ -1009,7 +1009,7 @@ func (r *Resolver) GetStorageAt(ctx context.Context, args struct {
Contract common.Address
Slot common.Hash
}) (*StorageResult, error) {
- cid, ipldBlock, rlpValue, err := r.backend.IPLDRetriever.RetrieveStorageAtByAddressAndStorageSlotAndBlockHash(args.Contract, args.Slot, args.BlockHash)
+ cid, ipldBlock, rlpValue, err := r.backend.Retriever.RetrieveStorageAtByAddressAndStorageSlotAndBlockHash(args.Contract, args.Slot, args.BlockHash)
if err != nil {
if err == sql.ErrNoRows {
diff --git a/test_config/test_config.go b/test_config/test_config.go
index 982f9b44..de67b704 100644
--- a/test_config/test_config.go
+++ b/test_config/test_config.go
@@ -18,6 +18,7 @@ package test_config
import (
"errors"
+
"github.com/cerc-io/ipld-eth-server/v4/pkg/log"
"github.com/ethereum/go-ethereum/statediff/indexer/database/sql/postgres"
"github.com/spf13/viper"