Update to geth 1.11.5-statediff-v5 #238
@ -25,15 +25,14 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/mailgun/groupcache/v2"
|
|
||||||
|
|
||||||
"github.com/cerc-io/ipld-eth-server/v4/pkg/log"
|
"github.com/cerc-io/ipld-eth-server/v4/pkg/log"
|
||||||
"github.com/ethereum/go-ethereum/rpc"
|
"github.com/ethereum/go-ethereum/rpc"
|
||||||
|
"github.com/mailgun/groupcache/v2"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
"github.com/vulcanize/gap-filler/pkg/mux"
|
"github.com/vulcanize/gap-filler/pkg/mux"
|
||||||
|
|
||||||
"github.com/cerc-io/ipld-eth-server/v4/pkg/eth"
|
|
||||||
"github.com/cerc-io/ipld-eth-server/v4/pkg/graphql"
|
"github.com/cerc-io/ipld-eth-server/v4/pkg/graphql"
|
||||||
srpc "github.com/cerc-io/ipld-eth-server/v4/pkg/rpc"
|
srpc "github.com/cerc-io/ipld-eth-server/v4/pkg/rpc"
|
||||||
s "github.com/cerc-io/ipld-eth-server/v4/pkg/serve"
|
s "github.com/cerc-io/ipld-eth-server/v4/pkg/serve"
|
||||||
@ -59,7 +58,6 @@ var serveCmd = &cobra.Command{
|
|||||||
func serve() {
|
func serve() {
|
||||||
logWithCommand.Infof("running ipld-eth-server version: %s", v.VersionWithMeta)
|
logWithCommand.Infof("running ipld-eth-server version: %s", v.VersionWithMeta)
|
||||||
|
|
||||||
var forwardPayloadChan chan eth.ConvertedPayload
|
|
||||||
wg := new(sync.WaitGroup)
|
wg := new(sync.WaitGroup)
|
||||||
logWithCommand.Debug("loading server configuration variables")
|
logWithCommand.Debug("loading server configuration variables")
|
||||||
serverConfig, err := s.NewConfig()
|
serverConfig, err := s.NewConfig()
|
||||||
@ -74,8 +72,7 @@ func serve() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
logWithCommand.Info("starting up server servers")
|
logWithCommand.Info("starting up server servers")
|
||||||
forwardPayloadChan = make(chan eth.ConvertedPayload, s.PayloadChanBufferSize)
|
server.Serve(wg)
|
||||||
server.Serve(wg, forwardPayloadChan)
|
|
||||||
if err := startServers(server, serverConfig); err != nil {
|
if err := startServers(server, serverConfig); err != nil {
|
||||||
logWithCommand.Fatal(err)
|
logWithCommand.Fatal(err)
|
||||||
}
|
}
|
||||||
|
@ -110,7 +110,6 @@ type Backend struct {
|
|||||||
|
|
||||||
// postgres db interfaces
|
// postgres db interfaces
|
||||||
Retriever *CIDRetriever
|
Retriever *CIDRetriever
|
||||||
Fetcher *IPLDFetcher
|
|
||||||
IPLDRetriever *IPLDRetriever
|
IPLDRetriever *IPLDRetriever
|
||||||
|
|
||||||
// ethereum interfaces
|
// ethereum interfaces
|
||||||
@ -148,7 +147,6 @@ func NewEthBackend(db *sqlx.DB, c *Config) (*Backend, error) {
|
|||||||
return &Backend{
|
return &Backend{
|
||||||
DB: db,
|
DB: db,
|
||||||
Retriever: r,
|
Retriever: r,
|
||||||
Fetcher: NewIPLDFetcher(db),
|
|
||||||
IPLDRetriever: NewIPLDRetriever(db),
|
IPLDRetriever: NewIPLDRetriever(db),
|
||||||
EthDB: ethDB,
|
EthDB: ethDB,
|
||||||
StateDatabase: state.NewDatabase(ethDB),
|
StateDatabase: state.NewDatabase(ethDB),
|
||||||
|
@ -19,27 +19,16 @@ package eth
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"math/big"
|
"math/big"
|
||||||
"strconv"
|
|
||||||
|
|
||||||
"github.com/cerc-io/ipld-eth-server/v4/pkg/log"
|
"github.com/cerc-io/ipld-eth-server/v4/pkg/log"
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
|
||||||
"github.com/ethereum/go-ethereum/statediff/indexer/models"
|
"github.com/ethereum/go-ethereum/statediff/indexer/models"
|
||||||
"github.com/jmoiron/sqlx"
|
"github.com/jmoiron/sqlx"
|
||||||
"github.com/lib/pq"
|
"github.com/lib/pq"
|
||||||
"gorm.io/driver/postgres"
|
"gorm.io/driver/postgres"
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
|
|
||||||
"github.com/cerc-io/ipld-eth-server/v4/pkg/shared"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Retriever interface for substituting mocks in tests
|
|
||||||
type Retriever interface {
|
|
||||||
RetrieveFirstBlockNumber() (int64, error)
|
|
||||||
RetrieveLastBlockNumber() (int64, error)
|
|
||||||
Retrieve(filter SubscriptionSettings, blockNumber int64) ([]CIDWrapper, bool, error)
|
|
||||||
}
|
|
||||||
|
|
||||||
// CIDRetriever satisfies the CIDRetriever interface for ethereum
|
// CIDRetriever satisfies the CIDRetriever interface for ethereum
|
||||||
type CIDRetriever struct {
|
type CIDRetriever struct {
|
||||||
db *sqlx.DB
|
db *sqlx.DB
|
||||||
@ -128,158 +117,6 @@ func (ecr *CIDRetriever) RetrieveLastBlockNumber() (int64, error) {
|
|||||||
return blockNumber, err
|
return blockNumber, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Retrieve is used to retrieve all of the CIDs which conform to the passed StreamFilters
|
|
||||||
func (ecr *CIDRetriever) Retrieve(filter SubscriptionSettings, blockNumber int64) ([]CIDWrapper, bool, error) {
|
|
||||||
log.Debug("retrieving cids")
|
|
||||||
|
|
||||||
// Begin new db tx
|
|
||||||
tx, err := ecr.db.Beginx()
|
|
||||||
if err != nil {
|
|
||||||
return nil, true, err
|
|
||||||
}
|
|
||||||
defer func() {
|
|
||||||
if p := recover(); p != nil {
|
|
||||||
shared.Rollback(tx)
|
|
||||||
panic(p)
|
|
||||||
} else if err != nil {
|
|
||||||
shared.Rollback(tx)
|
|
||||||
} else {
|
|
||||||
err = tx.Commit()
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
// Retrieve cached header CIDs at this block height
|
|
||||||
var headers []models.HeaderModel
|
|
||||||
headers, err = ecr.RetrieveHeaderCIDs(tx, blockNumber)
|
|
||||||
if err != nil {
|
|
||||||
log.Error("header cid retrieval error", err)
|
|
||||||
return nil, true, err
|
|
||||||
}
|
|
||||||
cws := make([]CIDWrapper, len(headers))
|
|
||||||
empty := true
|
|
||||||
for i, header := range headers {
|
|
||||||
cw := new(CIDWrapper)
|
|
||||||
cw.BlockNumber = big.NewInt(blockNumber)
|
|
||||||
if !filter.HeaderFilter.Off {
|
|
||||||
cw.Header = header
|
|
||||||
empty = false
|
|
||||||
if filter.HeaderFilter.Uncles {
|
|
||||||
// Retrieve uncle cids for this header id
|
|
||||||
var uncleCIDs []models.UncleModel
|
|
||||||
uncleCIDs, err = ecr.RetrieveUncleCIDsByHeaderID(tx, header.BlockHash)
|
|
||||||
if err != nil {
|
|
||||||
log.Error("uncle cid retrieval error")
|
|
||||||
return nil, true, err
|
|
||||||
}
|
|
||||||
cw.Uncles = uncleCIDs
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Retrieve cached trx CIDs
|
|
||||||
if !filter.TxFilter.Off {
|
|
||||||
cw.Transactions, err = ecr.RetrieveTxCIDs(tx, filter.TxFilter, header.BlockHash)
|
|
||||||
if err != nil {
|
|
||||||
log.Error("transaction cid retrieval error")
|
|
||||||
return nil, true, err
|
|
||||||
}
|
|
||||||
if len(cw.Transactions) > 0 {
|
|
||||||
empty = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
trxHashes := make([]string, len(cw.Transactions))
|
|
||||||
for j, t := range cw.Transactions {
|
|
||||||
trxHashes[j] = t.TxHash
|
|
||||||
}
|
|
||||||
// Retrieve cached receipt CIDs
|
|
||||||
if !filter.ReceiptFilter.Off {
|
|
||||||
cw.Receipts, err = ecr.RetrieveRctCIDs(tx, filter.ReceiptFilter, 0, header.BlockHash, trxHashes)
|
|
||||||
if err != nil {
|
|
||||||
log.Error("receipt cid retrieval error")
|
|
||||||
return nil, true, err
|
|
||||||
}
|
|
||||||
if len(cw.Receipts) > 0 {
|
|
||||||
empty = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Retrieve cached state CIDs
|
|
||||||
if !filter.StateFilter.Off {
|
|
||||||
cw.StateNodes, err = ecr.RetrieveStateCIDs(tx, filter.StateFilter, header.BlockHash)
|
|
||||||
if err != nil {
|
|
||||||
log.Error("state cid retrieval error")
|
|
||||||
return nil, true, err
|
|
||||||
}
|
|
||||||
if len(cw.StateNodes) > 0 {
|
|
||||||
empty = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Retrieve cached storage CIDs
|
|
||||||
if !filter.StorageFilter.Off {
|
|
||||||
cw.StorageNodes, err = ecr.RetrieveStorageCIDs(tx, filter.StorageFilter, header.BlockHash)
|
|
||||||
if err != nil {
|
|
||||||
log.Error("storage cid retrieval error")
|
|
||||||
return nil, true, err
|
|
||||||
}
|
|
||||||
if len(cw.StorageNodes) > 0 {
|
|
||||||
empty = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
cws[i] = *cw
|
|
||||||
}
|
|
||||||
|
|
||||||
return cws, empty, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// RetrieveHeaderCIDs retrieves and returns all of the header cids at the provided blockheight
|
|
||||||
func (ecr *CIDRetriever) RetrieveHeaderCIDs(tx *sqlx.Tx, blockNumber int64) ([]models.HeaderModel, error) {
|
|
||||||
log.Debug("retrieving header cids for block ", blockNumber)
|
|
||||||
headers := make([]models.HeaderModel, 0)
|
|
||||||
pgStr := `SELECT CAST(block_number as Text), block_hash, parent_hash, cid, mh_key, CAST(td as Text), node_id,
|
|
||||||
CAST(reward as Text), state_root, uncle_root,tx_root, receipt_root, bloom, timestamp, times_validated, coinbase
|
|
||||||
FROM eth.header_cids
|
|
||||||
WHERE block_number = $1`
|
|
||||||
return headers, tx.Select(&headers, pgStr, blockNumber)
|
|
||||||
}
|
|
||||||
|
|
||||||
// RetrieveUncleCIDsByHeaderID retrieves and returns all of the uncle cids for the provided header
|
|
||||||
func (ecr *CIDRetriever) RetrieveUncleCIDsByHeaderID(tx *sqlx.Tx, headerID string) ([]models.UncleModel, error) {
|
|
||||||
log.Debug("retrieving uncle cids for block id ", headerID)
|
|
||||||
headers := make([]models.UncleModel, 0)
|
|
||||||
pgStr := `SELECT CAST(block_number as Text), header_id, block_hash, parent_hash, cid, mh_key, CAST(reward as text)
|
|
||||||
FROM eth.uncle_cids
|
|
||||||
WHERE header_id = $1`
|
|
||||||
return headers, tx.Select(&headers, pgStr, headerID)
|
|
||||||
}
|
|
||||||
|
|
||||||
// RetrieveTxCIDs retrieves and returns all of the trx cids at the provided blockheight that conform to the provided filter parameters
|
|
||||||
// also returns the ids for the returned transaction cids
|
|
||||||
func (ecr *CIDRetriever) RetrieveTxCIDs(tx *sqlx.Tx, txFilter TxFilter, headerID string) ([]models.TxModel, error) {
|
|
||||||
log.Debug("retrieving transaction cids for header id ", headerID)
|
|
||||||
args := make([]interface{}, 0, 3)
|
|
||||||
results := make([]models.TxModel, 0)
|
|
||||||
id := 1
|
|
||||||
pgStr := fmt.Sprintf(`SELECT CAST(transaction_cids.block_number as Text), transaction_cids.tx_hash,
|
|
||||||
transaction_cids.header_id, transaction_cids.cid, transaction_cids.mh_key, transaction_cids.dst,
|
|
||||||
transaction_cids.src, transaction_cids.index, transaction_cids.tx_data, transaction_cids.tx_type
|
|
||||||
FROM eth.transaction_cids
|
|
||||||
INNER JOIN eth.header_cids ON (
|
|
||||||
transaction_cids.header_id = header_cids.block_hash
|
|
||||||
AND transaction_cids.block_number = header_cids.block_number
|
|
||||||
)
|
|
||||||
WHERE header_cids.block_hash = $%d`, id)
|
|
||||||
args = append(args, headerID)
|
|
||||||
id++
|
|
||||||
if len(txFilter.Dst) > 0 {
|
|
||||||
pgStr += fmt.Sprintf(` AND transaction_cids.dst = ANY($%d::VARCHAR(66)[])`, id)
|
|
||||||
args = append(args, pq.Array(txFilter.Dst))
|
|
||||||
id++
|
|
||||||
}
|
|
||||||
if len(txFilter.Src) > 0 {
|
|
||||||
pgStr += fmt.Sprintf(` AND transaction_cids.src = ANY($%d::VARCHAR(66)[])`, id)
|
|
||||||
args = append(args, pq.Array(txFilter.Src))
|
|
||||||
}
|
|
||||||
pgStr += ` ORDER BY transaction_cids.index`
|
|
||||||
return results, tx.Select(&results, pgStr, args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func topicFilterCondition(id *int, topics [][]string, args []interface{}, pgStr string, first bool) (string, []interface{}) {
|
func topicFilterCondition(id *int, topics [][]string, args []interface{}, pgStr string, first bool) (string, []interface{}) {
|
||||||
for i, topicSet := range topics {
|
for i, topicSet := range topics {
|
||||||
if len(topicSet) == 0 {
|
if len(topicSet) == 0 {
|
||||||
@ -439,38 +276,6 @@ func (ecr *CIDRetriever) RetrieveFilteredLog(tx *sqlx.Tx, rctFilter ReceiptFilte
|
|||||||
return logCIDs, nil
|
return logCIDs, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// RetrieveRctCIDs retrieves and returns all of the rct cids at the provided blockheight or block hash that conform to the provided
|
|
||||||
// filter parameters and correspond to the provided tx ids
|
|
||||||
func (ecr *CIDRetriever) RetrieveRctCIDs(tx *sqlx.Tx, rctFilter ReceiptFilter, blockNumber int64, blockHash string, txHashes []string) ([]models.ReceiptModel, error) {
|
|
||||||
log.Debug("retrieving receipt cids for block ", blockNumber)
|
|
||||||
args := make([]interface{}, 0, 5)
|
|
||||||
pgStr := `SELECT CAST(receipt_cids.block_number as Text), receipt_cids.header_id, receipt_cids.tx_id,
|
|
||||||
receipt_cids.leaf_cid, receipt_cids.leaf_mh_key, receipt_cids.contract, receipt_cids.contract_hash
|
|
||||||
FROM eth.receipt_cids, eth.transaction_cids, eth.header_cids
|
|
||||||
WHERE 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 != "" {
|
|
||||||
pgStr += fmt.Sprintf(` AND header_cids.block_hash = $%d`, id)
|
|
||||||
args = append(args, blockHash)
|
|
||||||
id++
|
|
||||||
}
|
|
||||||
|
|
||||||
pgStr, args = receiptFilterConditions(&id, pgStr, args, rctFilter, txHashes)
|
|
||||||
|
|
||||||
pgStr += ` ORDER BY transaction_cids.index`
|
|
||||||
receiptCIDs := make([]models.ReceiptModel, 0)
|
|
||||||
return receiptCIDs, tx.Select(&receiptCIDs, pgStr, args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func hasTopics(topics [][]string) bool {
|
func hasTopics(topics [][]string) bool {
|
||||||
for _, topicSet := range topics {
|
for _, topicSet := range topics {
|
||||||
if len(topicSet) > 0 {
|
if len(topicSet) > 0 {
|
||||||
@ -480,179 +285,6 @@ func hasTopics(topics [][]string) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// RetrieveStateCIDs retrieves and returns all of the state node cids at the provided header ID that conform to the provided filter parameters
|
|
||||||
func (ecr *CIDRetriever) RetrieveStateCIDs(tx *sqlx.Tx, stateFilter StateFilter, headerID string) ([]models.StateNodeModel, error) {
|
|
||||||
log.Debug("retrieving state cids for header id ", headerID)
|
|
||||||
args := make([]interface{}, 0, 2)
|
|
||||||
pgStr := `SELECT CAST(state_cids.block_number as Text), state_cids.header_id,
|
|
||||||
state_cids.state_leaf_key, state_cids.node_type, state_cids.cid, state_cids.mh_key, state_cids.state_path
|
|
||||||
FROM eth.state_cids
|
|
||||||
INNER JOIN eth.header_cids ON (
|
|
||||||
state_cids.header_id = header_cids.block_hash
|
|
||||||
AND state_cids.block_number = header_cids.block_number
|
|
||||||
)
|
|
||||||
WHERE header_cids.block_hash = $1`
|
|
||||||
args = append(args, headerID)
|
|
||||||
addrLen := len(stateFilter.Addresses)
|
|
||||||
if addrLen > 0 {
|
|
||||||
keys := make([]string, addrLen)
|
|
||||||
for i, addr := range stateFilter.Addresses {
|
|
||||||
keys[i] = crypto.Keccak256Hash(common.HexToAddress(addr).Bytes()).String()
|
|
||||||
}
|
|
||||||
pgStr += ` AND state_cids.state_leaf_key = ANY($2::VARCHAR(66)[])`
|
|
||||||
args = append(args, pq.Array(keys))
|
|
||||||
}
|
|
||||||
if !stateFilter.IntermediateNodes {
|
|
||||||
pgStr += ` AND state_cids.node_type = 2`
|
|
||||||
}
|
|
||||||
stateNodeCIDs := make([]models.StateNodeModel, 0)
|
|
||||||
return stateNodeCIDs, tx.Select(&stateNodeCIDs, pgStr, args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// RetrieveStorageCIDs retrieves and returns all of the storage node cids at the provided header id that conform to the provided filter parameters
|
|
||||||
func (ecr *CIDRetriever) RetrieveStorageCIDs(tx *sqlx.Tx, storageFilter StorageFilter, headerID string) ([]models.StorageNodeWithStateKeyModel, error) {
|
|
||||||
log.Debug("retrieving storage cids for header id ", headerID)
|
|
||||||
args := make([]interface{}, 0, 3)
|
|
||||||
pgStr := `SELECT CAST(storage_cids.block_number as Text), storage_cids.header_id, storage_cids.storage_leaf_key,
|
|
||||||
storage_cids.node_type, storage_cids.cid, storage_cids.mh_key, storage_cids.storage_path, storage_cids.state_path,
|
|
||||||
state_cids.state_leaf_key
|
|
||||||
FROM eth.storage_cids, eth.state_cids, eth.header_cids
|
|
||||||
WHERE storage_cids.header_id = state_cids.header_id
|
|
||||||
AND storage_cids.state_path = state_cids.state_path
|
|
||||||
AND storage_cids.block_number = state_cids.block_number
|
|
||||||
AND state_cids.header_id = header_cids.block_hash
|
|
||||||
AND state_cids.block_number = header_cids.block_number
|
|
||||||
AND header_cids.block_hash = $1`
|
|
||||||
args = append(args, headerID)
|
|
||||||
id := 2
|
|
||||||
addrLen := len(storageFilter.Addresses)
|
|
||||||
if addrLen > 0 {
|
|
||||||
keys := make([]string, addrLen)
|
|
||||||
for i, addr := range storageFilter.Addresses {
|
|
||||||
keys[i] = crypto.Keccak256Hash(common.HexToAddress(addr).Bytes()).String()
|
|
||||||
}
|
|
||||||
pgStr += fmt.Sprintf(` AND state_cids.state_leaf_key = ANY($%d::VARCHAR(66)[])`, id)
|
|
||||||
args = append(args, pq.Array(keys))
|
|
||||||
id++
|
|
||||||
}
|
|
||||||
if len(storageFilter.StorageKeys) > 0 {
|
|
||||||
pgStr += fmt.Sprintf(` AND storage_cids.storage_leaf_key = ANY($%d::VARCHAR(66)[])`, id)
|
|
||||||
args = append(args, pq.Array(storageFilter.StorageKeys))
|
|
||||||
}
|
|
||||||
if !storageFilter.IntermediateNodes {
|
|
||||||
pgStr += ` AND storage_cids.node_type = 2`
|
|
||||||
}
|
|
||||||
storageNodeCIDs := make([]models.StorageNodeWithStateKeyModel, 0)
|
|
||||||
return storageNodeCIDs, tx.Select(&storageNodeCIDs, pgStr, args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// RetrieveBlockByHash returns all of the CIDs needed to compose an entire block, for a given block hash
|
|
||||||
func (ecr *CIDRetriever) RetrieveBlockByHash(blockHash common.Hash) (models.HeaderModel, []models.UncleModel, []models.TxModel, []models.ReceiptModel, error) {
|
|
||||||
log.Debug("retrieving block cids for block hash ", blockHash.String())
|
|
||||||
|
|
||||||
// Begin new db tx
|
|
||||||
tx, err := ecr.db.Beginx()
|
|
||||||
if err != nil {
|
|
||||||
return models.HeaderModel{}, nil, nil, nil, err
|
|
||||||
}
|
|
||||||
defer func() {
|
|
||||||
if p := recover(); p != nil {
|
|
||||||
shared.Rollback(tx)
|
|
||||||
panic(p)
|
|
||||||
} else if err != nil {
|
|
||||||
shared.Rollback(tx)
|
|
||||||
} else {
|
|
||||||
err = tx.Commit()
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
var headerCID models.HeaderModel
|
|
||||||
headerCID, err = ecr.RetrieveHeaderCIDByHash(tx, blockHash)
|
|
||||||
if err != nil {
|
|
||||||
log.Error("header cid retrieval error")
|
|
||||||
return models.HeaderModel{}, nil, nil, nil, err
|
|
||||||
}
|
|
||||||
blockNumber, err := strconv.ParseInt(headerCID.BlockNumber, 10, 64)
|
|
||||||
if err != nil {
|
|
||||||
return models.HeaderModel{}, nil, nil, nil, err
|
|
||||||
}
|
|
||||||
var uncleCIDs []models.UncleModel
|
|
||||||
uncleCIDs, err = ecr.RetrieveUncleCIDsByHeaderID(tx, headerCID.BlockHash)
|
|
||||||
if err != nil {
|
|
||||||
log.Error("uncle cid retrieval error")
|
|
||||||
return models.HeaderModel{}, nil, nil, nil, err
|
|
||||||
}
|
|
||||||
var txCIDs []models.TxModel
|
|
||||||
txCIDs, err = ecr.RetrieveTxCIDsByHeaderID(tx, headerCID.BlockHash, blockNumber)
|
|
||||||
if err != nil {
|
|
||||||
log.Error("tx cid retrieval error")
|
|
||||||
return models.HeaderModel{}, nil, nil, nil, err
|
|
||||||
}
|
|
||||||
txHashes := make([]string, len(txCIDs))
|
|
||||||
for i, txCID := range txCIDs {
|
|
||||||
txHashes[i] = txCID.TxHash
|
|
||||||
}
|
|
||||||
var rctCIDs []models.ReceiptModel
|
|
||||||
rctCIDs, err = ecr.RetrieveReceiptCIDsByByHeaderIDAndTxIDs(tx, headerCID.BlockHash, txHashes, blockNumber)
|
|
||||||
if err != nil {
|
|
||||||
log.Error("rct cid retrieval error")
|
|
||||||
}
|
|
||||||
return headerCID, uncleCIDs, txCIDs, rctCIDs, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// RetrieveBlockByNumber returns all of the CIDs needed to compose an entire block, for a given block number
|
|
||||||
func (ecr *CIDRetriever) RetrieveBlockByNumber(blockNumber int64) (models.HeaderModel, []models.UncleModel, []models.TxModel, []models.ReceiptModel, error) {
|
|
||||||
log.Debug("retrieving block cids for block number ", blockNumber)
|
|
||||||
|
|
||||||
// Begin new db tx
|
|
||||||
tx, err := ecr.db.Beginx()
|
|
||||||
if err != nil {
|
|
||||||
return models.HeaderModel{}, nil, nil, nil, err
|
|
||||||
}
|
|
||||||
defer func() {
|
|
||||||
if p := recover(); p != nil {
|
|
||||||
shared.Rollback(tx)
|
|
||||||
panic(p)
|
|
||||||
} else if err != nil {
|
|
||||||
shared.Rollback(tx)
|
|
||||||
} else {
|
|
||||||
err = tx.Commit()
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
var headerCID []models.HeaderModel
|
|
||||||
headerCID, err = ecr.RetrieveHeaderCIDs(tx, blockNumber)
|
|
||||||
if err != nil {
|
|
||||||
log.Error("header cid retrieval error")
|
|
||||||
return models.HeaderModel{}, nil, nil, nil, err
|
|
||||||
}
|
|
||||||
if len(headerCID) < 1 {
|
|
||||||
return models.HeaderModel{}, nil, nil, nil, fmt.Errorf("header cid retrieval error, no header CIDs found at block %d", blockNumber)
|
|
||||||
}
|
|
||||||
var uncleCIDs []models.UncleModel
|
|
||||||
uncleCIDs, err = ecr.RetrieveUncleCIDsByHeaderID(tx, headerCID[0].BlockHash)
|
|
||||||
if err != nil {
|
|
||||||
log.Error("uncle cid retrieval error")
|
|
||||||
return models.HeaderModel{}, nil, nil, nil, err
|
|
||||||
}
|
|
||||||
var txCIDs []models.TxModel
|
|
||||||
txCIDs, err = ecr.RetrieveTxCIDsByHeaderID(tx, headerCID[0].BlockHash, blockNumber)
|
|
||||||
if err != nil {
|
|
||||||
log.Error("tx cid retrieval error")
|
|
||||||
return models.HeaderModel{}, nil, nil, nil, err
|
|
||||||
}
|
|
||||||
txHashes := make([]string, len(txCIDs))
|
|
||||||
for i, txCID := range txCIDs {
|
|
||||||
txHashes[i] = txCID.TxHash
|
|
||||||
}
|
|
||||||
var rctCIDs []models.ReceiptModel
|
|
||||||
rctCIDs, err = ecr.RetrieveReceiptCIDsByByHeaderIDAndTxIDs(tx, headerCID[0].BlockHash, txHashes, blockNumber)
|
|
||||||
if err != nil {
|
|
||||||
log.Error("rct cid retrieval error")
|
|
||||||
}
|
|
||||||
return headerCID[0], uncleCIDs, txCIDs, rctCIDs, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// RetrieveHeaderCIDByHash returns the header for the given block hash
|
// RetrieveHeaderCIDByHash returns the header for the given block hash
|
||||||
func (ecr *CIDRetriever) RetrieveHeaderCIDByHash(tx *sqlx.Tx, blockHash common.Hash) (models.HeaderModel, error) {
|
func (ecr *CIDRetriever) RetrieveHeaderCIDByHash(tx *sqlx.Tx, blockHash common.Hash) (models.HeaderModel, error) {
|
||||||
log.Debug("retrieving header cids for block hash ", blockHash.String())
|
log.Debug("retrieving header cids for block hash ", blockHash.String())
|
||||||
@ -663,35 +295,6 @@ func (ecr *CIDRetriever) RetrieveHeaderCIDByHash(tx *sqlx.Tx, blockHash common.H
|
|||||||
return headerCID, tx.Get(&headerCID, pgStr, blockHash.String())
|
return headerCID, tx.Get(&headerCID, pgStr, blockHash.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
// RetrieveTxCIDsByHeaderID retrieves all tx CIDs for the given header id
|
|
||||||
func (ecr *CIDRetriever) RetrieveTxCIDsByHeaderID(tx *sqlx.Tx, headerID string, blockNumber int64) ([]models.TxModel, error) {
|
|
||||||
log.Debug("retrieving tx cids for block id ", headerID)
|
|
||||||
pgStr := `SELECT CAST(block_number as Text), header_id, index, tx_hash, cid, mh_key,
|
|
||||||
dst, src, tx_data, tx_type, value
|
|
||||||
FROM eth.transaction_cids
|
|
||||||
WHERE header_id = $1 AND block_number = $2
|
|
||||||
ORDER BY index`
|
|
||||||
var txCIDs []models.TxModel
|
|
||||||
return txCIDs, tx.Select(&txCIDs, pgStr, headerID, blockNumber)
|
|
||||||
}
|
|
||||||
|
|
||||||
// RetrieveReceiptCIDsByByHeaderIDAndTxIDs retrieves receipt CIDs by their associated tx IDs for the given header id
|
|
||||||
func (ecr *CIDRetriever) RetrieveReceiptCIDsByByHeaderIDAndTxIDs(tx *sqlx.Tx, headerID string, txHashes []string, blockNumber int64) ([]models.ReceiptModel, error) {
|
|
||||||
log.Debugf("retrieving receipt cids for tx hashes %v", txHashes)
|
|
||||||
pgStr := `SELECT CAST(receipt_cids.block_number as Text), receipt_cids.header_id, receipt_cids.tx_id, receipt_cids.leaf_cid,
|
|
||||||
receipt_cids.leaf_mh_key, receipt_cids.contract, receipt_cids.contract_hash
|
|
||||||
FROM eth.receipt_cids, eth.transaction_cids
|
|
||||||
WHERE tx_id = ANY($2)
|
|
||||||
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 = $1
|
|
||||||
AND transaction_cids.block_number = $3
|
|
||||||
ORDER BY transaction_cids.index`
|
|
||||||
var rctCIDs []models.ReceiptModel
|
|
||||||
return rctCIDs, tx.Select(&rctCIDs, pgStr, headerID, pq.Array(txHashes), blockNumber)
|
|
||||||
}
|
|
||||||
|
|
||||||
// RetrieveHeaderAndTxCIDsByBlockNumber retrieves header CIDs and their associated tx CIDs by block number
|
// RetrieveHeaderAndTxCIDsByBlockNumber retrieves header CIDs and their associated tx CIDs by block number
|
||||||
func (ecr *CIDRetriever) RetrieveHeaderAndTxCIDsByBlockNumber(blockNumber int64) ([]HeaderCIDRecord, error) {
|
func (ecr *CIDRetriever) RetrieveHeaderAndTxCIDsByBlockNumber(blockNumber int64) ([]HeaderCIDRecord, error) {
|
||||||
log.Debug("retrieving header cids and tx cids for block number ", blockNumber)
|
log.Debug("retrieving header cids and tx cids for block number ", blockNumber)
|
||||||
|
@ -17,197 +17,18 @@
|
|||||||
package eth_test
|
package eth_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"math/big"
|
|
||||||
|
|
||||||
"github.com/cerc-io/ipld-eth-server/v4/pkg/eth"
|
"github.com/cerc-io/ipld-eth-server/v4/pkg/eth"
|
||||||
"github.com/cerc-io/ipld-eth-server/v4/pkg/eth/test_helpers"
|
"github.com/cerc-io/ipld-eth-server/v4/pkg/eth/test_helpers"
|
||||||
"github.com/cerc-io/ipld-eth-server/v4/pkg/shared"
|
"github.com/cerc-io/ipld-eth-server/v4/pkg/shared"
|
||||||
"github.com/ethereum/go-ethereum/common"
|
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
"github.com/ethereum/go-ethereum/statediff/indexer/interfaces"
|
"github.com/ethereum/go-ethereum/statediff/indexer/interfaces"
|
||||||
"github.com/ethereum/go-ethereum/statediff/indexer/models"
|
|
||||||
"github.com/ethereum/go-ethereum/trie"
|
"github.com/ethereum/go-ethereum/trie"
|
||||||
"github.com/jmoiron/sqlx"
|
"github.com/jmoiron/sqlx"
|
||||||
. "github.com/onsi/ginkgo"
|
. "github.com/onsi/ginkgo"
|
||||||
. "github.com/onsi/gomega"
|
. "github.com/onsi/gomega"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
|
||||||
openFilter = eth.SubscriptionSettings{
|
|
||||||
Start: big.NewInt(0),
|
|
||||||
End: big.NewInt(1),
|
|
||||||
HeaderFilter: eth.HeaderFilter{},
|
|
||||||
TxFilter: eth.TxFilter{},
|
|
||||||
ReceiptFilter: eth.ReceiptFilter{},
|
|
||||||
StateFilter: eth.StateFilter{},
|
|
||||||
StorageFilter: eth.StorageFilter{},
|
|
||||||
}
|
|
||||||
rctAddressFilter = eth.SubscriptionSettings{
|
|
||||||
Start: big.NewInt(0),
|
|
||||||
End: big.NewInt(1),
|
|
||||||
HeaderFilter: eth.HeaderFilter{
|
|
||||||
Off: true,
|
|
||||||
},
|
|
||||||
TxFilter: eth.TxFilter{
|
|
||||||
Off: true,
|
|
||||||
},
|
|
||||||
ReceiptFilter: eth.ReceiptFilter{
|
|
||||||
LogAddresses: []string{test_helpers.Address.String()},
|
|
||||||
},
|
|
||||||
StateFilter: eth.StateFilter{
|
|
||||||
Off: true,
|
|
||||||
},
|
|
||||||
StorageFilter: eth.StorageFilter{
|
|
||||||
Off: true,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
rctTopicsFilter = eth.SubscriptionSettings{
|
|
||||||
Start: big.NewInt(0),
|
|
||||||
End: big.NewInt(1),
|
|
||||||
HeaderFilter: eth.HeaderFilter{
|
|
||||||
Off: true,
|
|
||||||
},
|
|
||||||
TxFilter: eth.TxFilter{
|
|
||||||
Off: true,
|
|
||||||
},
|
|
||||||
ReceiptFilter: eth.ReceiptFilter{
|
|
||||||
Topics: [][]string{{"0x0000000000000000000000000000000000000000000000000000000000000004"}},
|
|
||||||
},
|
|
||||||
StateFilter: eth.StateFilter{
|
|
||||||
Off: true,
|
|
||||||
},
|
|
||||||
StorageFilter: eth.StorageFilter{
|
|
||||||
Off: true,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
rctTopicsAndAddressFilter = eth.SubscriptionSettings{
|
|
||||||
Start: big.NewInt(0),
|
|
||||||
End: big.NewInt(1),
|
|
||||||
HeaderFilter: eth.HeaderFilter{
|
|
||||||
Off: true,
|
|
||||||
},
|
|
||||||
TxFilter: eth.TxFilter{
|
|
||||||
Off: true,
|
|
||||||
},
|
|
||||||
ReceiptFilter: eth.ReceiptFilter{
|
|
||||||
Topics: [][]string{
|
|
||||||
{"0x0000000000000000000000000000000000000000000000000000000000000004"},
|
|
||||||
{"0x0000000000000000000000000000000000000000000000000000000000000006"},
|
|
||||||
},
|
|
||||||
LogAddresses: []string{test_helpers.Address.String()},
|
|
||||||
},
|
|
||||||
StateFilter: eth.StateFilter{
|
|
||||||
Off: true,
|
|
||||||
},
|
|
||||||
StorageFilter: eth.StorageFilter{
|
|
||||||
Off: true,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
rctTopicsAndAddressFilterFail = eth.SubscriptionSettings{
|
|
||||||
Start: big.NewInt(0),
|
|
||||||
End: big.NewInt(1),
|
|
||||||
HeaderFilter: eth.HeaderFilter{
|
|
||||||
Off: true,
|
|
||||||
},
|
|
||||||
TxFilter: eth.TxFilter{
|
|
||||||
Off: true,
|
|
||||||
},
|
|
||||||
ReceiptFilter: eth.ReceiptFilter{
|
|
||||||
Topics: [][]string{
|
|
||||||
{"0x0000000000000000000000000000000000000000000000000000000000000004"},
|
|
||||||
{"0x0000000000000000000000000000000000000000000000000000000000000007"}, // This topic won't match on the mocks.Address.String() contract receipt
|
|
||||||
},
|
|
||||||
LogAddresses: []string{test_helpers.Address.String()},
|
|
||||||
},
|
|
||||||
StateFilter: eth.StateFilter{
|
|
||||||
Off: true,
|
|
||||||
},
|
|
||||||
StorageFilter: eth.StorageFilter{
|
|
||||||
Off: true,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
rctAddressesAndTopicFilter = eth.SubscriptionSettings{
|
|
||||||
Start: big.NewInt(0),
|
|
||||||
End: big.NewInt(1),
|
|
||||||
HeaderFilter: eth.HeaderFilter{
|
|
||||||
Off: true,
|
|
||||||
},
|
|
||||||
TxFilter: eth.TxFilter{
|
|
||||||
Off: true,
|
|
||||||
},
|
|
||||||
ReceiptFilter: eth.ReceiptFilter{
|
|
||||||
Topics: [][]string{{"0x0000000000000000000000000000000000000000000000000000000000000005"}},
|
|
||||||
LogAddresses: []string{test_helpers.Address.String(), test_helpers.AnotherAddress.String()},
|
|
||||||
},
|
|
||||||
StateFilter: eth.StateFilter{
|
|
||||||
Off: true,
|
|
||||||
},
|
|
||||||
StorageFilter: eth.StorageFilter{
|
|
||||||
Off: true,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
rctsForAllCollectedTrxs = eth.SubscriptionSettings{
|
|
||||||
Start: big.NewInt(0),
|
|
||||||
End: big.NewInt(1),
|
|
||||||
HeaderFilter: eth.HeaderFilter{
|
|
||||||
Off: true,
|
|
||||||
},
|
|
||||||
TxFilter: eth.TxFilter{}, // Trx filter open so we will collect all trxs, therefore we will also collect all corresponding rcts despite rct filter
|
|
||||||
ReceiptFilter: eth.ReceiptFilter{
|
|
||||||
MatchTxs: true,
|
|
||||||
Topics: [][]string{{"0x0000000000000000000000000000000000000000000000000000000000000006"}}, // Topic0 isn't one of the topic0s we have
|
|
||||||
LogAddresses: []string{"0x0000000000000000000000000000000000000002"}, // Contract isn't one of the contracts we have
|
|
||||||
},
|
|
||||||
StateFilter: eth.StateFilter{
|
|
||||||
Off: true,
|
|
||||||
},
|
|
||||||
StorageFilter: eth.StorageFilter{
|
|
||||||
Off: true,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
rctsForSelectCollectedTrxs = eth.SubscriptionSettings{
|
|
||||||
Start: big.NewInt(0),
|
|
||||||
End: big.NewInt(1),
|
|
||||||
HeaderFilter: eth.HeaderFilter{
|
|
||||||
Off: true,
|
|
||||||
},
|
|
||||||
TxFilter: eth.TxFilter{
|
|
||||||
Dst: []string{test_helpers.AnotherAddress.String()}, // We only filter for one of the trxs so we will only get the one corresponding receipt
|
|
||||||
},
|
|
||||||
ReceiptFilter: eth.ReceiptFilter{
|
|
||||||
MatchTxs: true,
|
|
||||||
Topics: [][]string{{"0x0000000000000000000000000000000000000000000000000000000000000006"}}, // Topic0 isn't one of the topic0s we have
|
|
||||||
LogAddresses: []string{"0x0000000000000000000000000000000000000002"}, // Contract isn't one of the contracts we have
|
|
||||||
},
|
|
||||||
StateFilter: eth.StateFilter{
|
|
||||||
Off: true,
|
|
||||||
},
|
|
||||||
StorageFilter: eth.StorageFilter{
|
|
||||||
Off: true,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
stateFilter = eth.SubscriptionSettings{
|
|
||||||
Start: big.NewInt(0),
|
|
||||||
End: big.NewInt(1),
|
|
||||||
HeaderFilter: eth.HeaderFilter{
|
|
||||||
Off: true,
|
|
||||||
},
|
|
||||||
TxFilter: eth.TxFilter{
|
|
||||||
Off: true,
|
|
||||||
},
|
|
||||||
ReceiptFilter: eth.ReceiptFilter{
|
|
||||||
Off: true,
|
|
||||||
},
|
|
||||||
StateFilter: eth.StateFilter{
|
|
||||||
Addresses: []string{test_helpers.AccountAddresss.Hex()},
|
|
||||||
},
|
|
||||||
StorageFilter: eth.StorageFilter{
|
|
||||||
Off: true,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
var _ = Describe("Retriever", func() {
|
var _ = Describe("Retriever", func() {
|
||||||
var (
|
var (
|
||||||
db *sqlx.DB
|
db *sqlx.DB
|
||||||
@ -236,196 +57,6 @@ var _ = Describe("Retriever", func() {
|
|||||||
err = tx.Submit(err)
|
err = tx.Submit(err)
|
||||||
Expect(err).ToNot(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
})
|
})
|
||||||
It("Retrieves all CIDs for the given blocknumber when provided an open filter", func() {
|
|
||||||
type rctCIDAndMHKeyResult struct {
|
|
||||||
LeafCID string `db:"leaf_cid"`
|
|
||||||
LeafMhKey string `db:"leaf_mh_key"`
|
|
||||||
}
|
|
||||||
expectedRctCIDsAndLeafNodes := make([]rctCIDAndMHKeyResult, 0)
|
|
||||||
pgStr := `SELECT receipt_cids.leaf_cid, receipt_cids.leaf_mh_key FROM eth.receipt_cids, eth.transaction_cids, eth.header_cids
|
|
||||||
WHERE receipt_cids.tx_id = transaction_cids.tx_hash
|
|
||||||
AND transaction_cids.header_id = header_cids.block_hash
|
|
||||||
AND header_cids.block_number = $1
|
|
||||||
ORDER BY transaction_cids.index`
|
|
||||||
err := db.Select(&expectedRctCIDsAndLeafNodes, pgStr, test_helpers.BlockNumber.Uint64())
|
|
||||||
Expect(err).ToNot(HaveOccurred())
|
|
||||||
cids, empty, err := retriever.Retrieve(openFilter, 1)
|
|
||||||
Expect(err).ToNot(HaveOccurred())
|
|
||||||
Expect(empty).ToNot(BeTrue())
|
|
||||||
Expect(len(cids)).To(Equal(1))
|
|
||||||
Expect(cids[0].BlockNumber).To(Equal(test_helpers.MockCIDWrapper.BlockNumber))
|
|
||||||
|
|
||||||
expectedHeaderCID := test_helpers.MockCIDWrapper.Header
|
|
||||||
expectedHeaderCID.BlockHash = cids[0].Header.BlockHash
|
|
||||||
expectedHeaderCID.NodeID = cids[0].Header.NodeID
|
|
||||||
Expect(cids[0].Header).To(Equal(expectedHeaderCID))
|
|
||||||
Expect(len(cids[0].Transactions)).To(Equal(4))
|
|
||||||
Expect(eth.TxModelsContainsCID(cids[0].Transactions, test_helpers.MockCIDWrapper.Transactions[0].CID)).To(BeTrue())
|
|
||||||
Expect(eth.TxModelsContainsCID(cids[0].Transactions, test_helpers.MockCIDWrapper.Transactions[1].CID)).To(BeTrue())
|
|
||||||
Expect(eth.TxModelsContainsCID(cids[0].Transactions, test_helpers.MockCIDWrapper.Transactions[2].CID)).To(BeTrue())
|
|
||||||
Expect(len(cids[0].Receipts)).To(Equal(4))
|
|
||||||
Expect(eth.ReceiptModelsContainsCID(cids[0].Receipts, expectedRctCIDsAndLeafNodes[0].LeafCID)).To(BeTrue())
|
|
||||||
Expect(eth.ReceiptModelsContainsCID(cids[0].Receipts, expectedRctCIDsAndLeafNodes[1].LeafCID)).To(BeTrue())
|
|
||||||
Expect(eth.ReceiptModelsContainsCID(cids[0].Receipts, expectedRctCIDsAndLeafNodes[2].LeafCID)).To(BeTrue())
|
|
||||||
Expect(len(cids[0].StateNodes)).To(Equal(2))
|
|
||||||
|
|
||||||
for _, stateNode := range cids[0].StateNodes {
|
|
||||||
if stateNode.CID == test_helpers.State1CID.String() {
|
|
||||||
Expect(stateNode.StateKey).To(Equal(common.BytesToHash(test_helpers.ContractLeafKey).Hex()))
|
|
||||||
Expect(stateNode.NodeType).To(Equal(2))
|
|
||||||
Expect(stateNode.Path).To(Equal([]byte{'\x06'}))
|
|
||||||
}
|
|
||||||
if stateNode.CID == test_helpers.State2CID.String() {
|
|
||||||
Expect(stateNode.StateKey).To(Equal(common.BytesToHash(test_helpers.AccountLeafKey).Hex()))
|
|
||||||
Expect(stateNode.NodeType).To(Equal(2))
|
|
||||||
Expect(stateNode.Path).To(Equal([]byte{'\x0c'}))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Expect(len(cids[0].StorageNodes)).To(Equal(1))
|
|
||||||
expectedStorageNodeCIDs := test_helpers.MockCIDWrapper.StorageNodes
|
|
||||||
expectedStorageNodeCIDs[0].HeaderID = cids[0].StorageNodes[0].HeaderID
|
|
||||||
expectedStorageNodeCIDs[0].StatePath = cids[0].StorageNodes[0].StatePath
|
|
||||||
Expect(cids[0].StorageNodes).To(Equal(expectedStorageNodeCIDs))
|
|
||||||
})
|
|
||||||
|
|
||||||
It("Applies filters from the provided config.Subscription", func() {
|
|
||||||
type rctCIDAndMHKeyResult struct {
|
|
||||||
LeafCID string `db:"leaf_cid"`
|
|
||||||
LeafMhKey string `db:"leaf_mh_key"`
|
|
||||||
}
|
|
||||||
expectedRctCIDsAndLeafNodes := make([]rctCIDAndMHKeyResult, 0)
|
|
||||||
pgStr := `SELECT receipt_cids.leaf_cid, receipt_cids.leaf_mh_key FROM eth.receipt_cids, eth.transaction_cids, eth.header_cids
|
|
||||||
WHERE receipt_cids.tx_id = transaction_cids.tx_hash
|
|
||||||
AND transaction_cids.header_id = header_cids.block_hash
|
|
||||||
AND header_cids.block_number = $1
|
|
||||||
ORDER BY transaction_cids.index`
|
|
||||||
err := db.Select(&expectedRctCIDsAndLeafNodes, pgStr, test_helpers.BlockNumber.Uint64())
|
|
||||||
Expect(err).ToNot(HaveOccurred())
|
|
||||||
cids1, empty, err := retriever.Retrieve(rctAddressFilter, 1)
|
|
||||||
Expect(err).ToNot(HaveOccurred())
|
|
||||||
Expect(empty).ToNot(BeTrue())
|
|
||||||
Expect(len(cids1)).To(Equal(1))
|
|
||||||
Expect(cids1[0].BlockNumber).To(Equal(test_helpers.MockCIDWrapper.BlockNumber))
|
|
||||||
Expect(cids1[0].Header).To(Equal(models.HeaderModel{}))
|
|
||||||
Expect(len(cids1[0].Transactions)).To(Equal(0))
|
|
||||||
Expect(len(cids1[0].StateNodes)).To(Equal(0))
|
|
||||||
Expect(len(cids1[0].StorageNodes)).To(Equal(0))
|
|
||||||
Expect(len(cids1[0].Receipts)).To(Equal(1))
|
|
||||||
expectedReceiptCID := test_helpers.MockCIDWrapper.Receipts[0]
|
|
||||||
expectedReceiptCID.TxID = cids1[0].Receipts[0].TxID
|
|
||||||
expectedReceiptCID.LeafCID = expectedRctCIDsAndLeafNodes[0].LeafCID
|
|
||||||
expectedReceiptCID.LeafMhKey = expectedRctCIDsAndLeafNodes[0].LeafMhKey
|
|
||||||
Expect(cids1[0].Receipts[0]).To(Equal(expectedReceiptCID))
|
|
||||||
|
|
||||||
cids2, empty, err := retriever.Retrieve(rctTopicsFilter, 1)
|
|
||||||
Expect(err).ToNot(HaveOccurred())
|
|
||||||
Expect(empty).ToNot(BeTrue())
|
|
||||||
Expect(len(cids2)).To(Equal(1))
|
|
||||||
Expect(cids2[0].BlockNumber).To(Equal(test_helpers.MockCIDWrapper.BlockNumber))
|
|
||||||
Expect(cids2[0].Header).To(Equal(models.HeaderModel{}))
|
|
||||||
Expect(len(cids2[0].Transactions)).To(Equal(0))
|
|
||||||
Expect(len(cids2[0].StateNodes)).To(Equal(0))
|
|
||||||
Expect(len(cids2[0].StorageNodes)).To(Equal(0))
|
|
||||||
Expect(len(cids2[0].Receipts)).To(Equal(1))
|
|
||||||
expectedReceiptCID = test_helpers.MockCIDWrapper.Receipts[0]
|
|
||||||
expectedReceiptCID.TxID = cids2[0].Receipts[0].TxID
|
|
||||||
expectedReceiptCID.LeafCID = expectedRctCIDsAndLeafNodes[0].LeafCID
|
|
||||||
expectedReceiptCID.LeafMhKey = expectedRctCIDsAndLeafNodes[0].LeafMhKey
|
|
||||||
Expect(cids2[0].Receipts[0]).To(Equal(expectedReceiptCID))
|
|
||||||
|
|
||||||
cids3, empty, err := retriever.Retrieve(rctTopicsAndAddressFilter, 1)
|
|
||||||
Expect(err).ToNot(HaveOccurred())
|
|
||||||
Expect(empty).ToNot(BeTrue())
|
|
||||||
Expect(len(cids3)).To(Equal(1))
|
|
||||||
Expect(cids3[0].BlockNumber).To(Equal(test_helpers.MockCIDWrapper.BlockNumber))
|
|
||||||
Expect(cids3[0].Header).To(Equal(models.HeaderModel{}))
|
|
||||||
Expect(len(cids3[0].Transactions)).To(Equal(0))
|
|
||||||
Expect(len(cids3[0].StateNodes)).To(Equal(0))
|
|
||||||
Expect(len(cids3[0].StorageNodes)).To(Equal(0))
|
|
||||||
Expect(len(cids3[0].Receipts)).To(Equal(1))
|
|
||||||
expectedReceiptCID = test_helpers.MockCIDWrapper.Receipts[0]
|
|
||||||
expectedReceiptCID.TxID = cids3[0].Receipts[0].TxID
|
|
||||||
expectedReceiptCID.LeafCID = expectedRctCIDsAndLeafNodes[0].LeafCID
|
|
||||||
expectedReceiptCID.LeafMhKey = expectedRctCIDsAndLeafNodes[0].LeafMhKey
|
|
||||||
Expect(cids3[0].Receipts[0]).To(Equal(expectedReceiptCID))
|
|
||||||
|
|
||||||
cids4, empty, err := retriever.Retrieve(rctAddressesAndTopicFilter, 1)
|
|
||||||
Expect(err).ToNot(HaveOccurred())
|
|
||||||
Expect(empty).ToNot(BeTrue())
|
|
||||||
Expect(len(cids4)).To(Equal(1))
|
|
||||||
Expect(cids4[0].BlockNumber).To(Equal(test_helpers.MockCIDWrapper.BlockNumber))
|
|
||||||
Expect(cids4[0].Header).To(Equal(models.HeaderModel{}))
|
|
||||||
Expect(len(cids4[0].Transactions)).To(Equal(0))
|
|
||||||
Expect(len(cids4[0].StateNodes)).To(Equal(0))
|
|
||||||
Expect(len(cids4[0].StorageNodes)).To(Equal(0))
|
|
||||||
Expect(len(cids4[0].Receipts)).To(Equal(1))
|
|
||||||
expectedReceiptCID = test_helpers.MockCIDWrapper.Receipts[1]
|
|
||||||
expectedReceiptCID.TxID = cids4[0].Receipts[0].TxID
|
|
||||||
expectedReceiptCID.LeafCID = expectedRctCIDsAndLeafNodes[1].LeafCID
|
|
||||||
expectedReceiptCID.LeafMhKey = expectedRctCIDsAndLeafNodes[1].LeafMhKey
|
|
||||||
Expect(cids4[0].Receipts[0]).To(Equal(expectedReceiptCID))
|
|
||||||
|
|
||||||
cids5, empty, err := retriever.Retrieve(rctsForAllCollectedTrxs, 1)
|
|
||||||
Expect(err).ToNot(HaveOccurred())
|
|
||||||
Expect(empty).ToNot(BeTrue())
|
|
||||||
Expect(len(cids5)).To(Equal(1))
|
|
||||||
Expect(cids5[0].BlockNumber).To(Equal(test_helpers.MockCIDWrapper.BlockNumber))
|
|
||||||
Expect(cids5[0].Header).To(Equal(models.HeaderModel{}))
|
|
||||||
Expect(len(cids5[0].Transactions)).To(Equal(4))
|
|
||||||
Expect(eth.TxModelsContainsCID(cids5[0].Transactions, test_helpers.Trx1CID.String())).To(BeTrue())
|
|
||||||
Expect(eth.TxModelsContainsCID(cids5[0].Transactions, test_helpers.Trx2CID.String())).To(BeTrue())
|
|
||||||
Expect(eth.TxModelsContainsCID(cids5[0].Transactions, test_helpers.Trx3CID.String())).To(BeTrue())
|
|
||||||
Expect(len(cids5[0].StateNodes)).To(Equal(0))
|
|
||||||
Expect(len(cids5[0].StorageNodes)).To(Equal(0))
|
|
||||||
Expect(len(cids5[0].Receipts)).To(Equal(4))
|
|
||||||
Expect(eth.ReceiptModelsContainsCID(cids5[0].Receipts, expectedRctCIDsAndLeafNodes[0].LeafCID)).To(BeTrue())
|
|
||||||
Expect(eth.ReceiptModelsContainsCID(cids5[0].Receipts, expectedRctCIDsAndLeafNodes[1].LeafCID)).To(BeTrue())
|
|
||||||
Expect(eth.ReceiptModelsContainsCID(cids5[0].Receipts, expectedRctCIDsAndLeafNodes[2].LeafCID)).To(BeTrue())
|
|
||||||
|
|
||||||
cids6, empty, err := retriever.Retrieve(rctsForSelectCollectedTrxs, 1)
|
|
||||||
Expect(err).ToNot(HaveOccurred())
|
|
||||||
Expect(empty).ToNot(BeTrue())
|
|
||||||
Expect(len(cids6)).To(Equal(1))
|
|
||||||
Expect(cids6[0].BlockNumber).To(Equal(test_helpers.MockCIDWrapper.BlockNumber))
|
|
||||||
Expect(cids6[0].Header).To(Equal(models.HeaderModel{}))
|
|
||||||
Expect(len(cids6[0].Transactions)).To(Equal(1))
|
|
||||||
expectedTxCID := test_helpers.MockCIDWrapper.Transactions[1]
|
|
||||||
expectedTxCID.TxHash = cids6[0].Transactions[0].TxHash
|
|
||||||
expectedTxCID.HeaderID = cids6[0].Transactions[0].HeaderID
|
|
||||||
Expect(cids6[0].Transactions[0]).To(Equal(expectedTxCID))
|
|
||||||
Expect(len(cids6[0].StateNodes)).To(Equal(0))
|
|
||||||
Expect(len(cids6[0].StorageNodes)).To(Equal(0))
|
|
||||||
Expect(len(cids6[0].Receipts)).To(Equal(1))
|
|
||||||
expectedReceiptCID = test_helpers.MockCIDWrapper.Receipts[1]
|
|
||||||
expectedReceiptCID.TxID = cids6[0].Receipts[0].TxID
|
|
||||||
expectedReceiptCID.LeafCID = expectedRctCIDsAndLeafNodes[1].LeafCID
|
|
||||||
expectedReceiptCID.LeafMhKey = expectedRctCIDsAndLeafNodes[1].LeafMhKey
|
|
||||||
Expect(cids6[0].Receipts[0]).To(Equal(expectedReceiptCID))
|
|
||||||
|
|
||||||
cids7, empty, err := retriever.Retrieve(stateFilter, 1)
|
|
||||||
Expect(err).ToNot(HaveOccurred())
|
|
||||||
Expect(empty).ToNot(BeTrue())
|
|
||||||
Expect(len(cids7)).To(Equal(1))
|
|
||||||
Expect(cids7[0].BlockNumber).To(Equal(test_helpers.MockCIDWrapper.BlockNumber))
|
|
||||||
Expect(cids7[0].Header).To(Equal(models.HeaderModel{}))
|
|
||||||
Expect(len(cids7[0].Transactions)).To(Equal(0))
|
|
||||||
Expect(len(cids7[0].Receipts)).To(Equal(0))
|
|
||||||
Expect(len(cids7[0].StorageNodes)).To(Equal(0))
|
|
||||||
Expect(len(cids7[0].StateNodes)).To(Equal(1))
|
|
||||||
Expect(cids7[0].StateNodes[0]).To(Equal(models.StateNodeModel{
|
|
||||||
BlockNumber: "1",
|
|
||||||
HeaderID: cids7[0].StateNodes[0].HeaderID,
|
|
||||||
NodeType: 2,
|
|
||||||
StateKey: common.BytesToHash(test_helpers.AccountLeafKey).Hex(),
|
|
||||||
CID: test_helpers.State2CID.String(),
|
|
||||||
MhKey: test_helpers.State2MhKey,
|
|
||||||
Path: []byte{'\x0c'},
|
|
||||||
}))
|
|
||||||
|
|
||||||
_, empty, err = retriever.Retrieve(rctTopicsAndAddressFilterFail, 1)
|
|
||||||
Expect(err).ToNot(HaveOccurred())
|
|
||||||
Expect(empty).To(BeTrue())
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
|
|
||||||
Describe("RetrieveFirstBlockNumber", func() {
|
Describe("RetrieveFirstBlockNumber", func() {
|
||||||
|
@ -28,24 +28,9 @@ import (
|
|||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
"github.com/ethereum/go-ethereum/rlp"
|
"github.com/ethereum/go-ethereum/rlp"
|
||||||
"github.com/lib/pq"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
RetrieveHeadersByHashesPgStr = `SELECT cid, data
|
|
||||||
FROM eth.header_cids
|
|
||||||
INNER JOIN public.blocks ON (
|
|
||||||
header_cids.mh_key = blocks.key
|
|
||||||
AND header_cids.block_number = blocks.block_number
|
|
||||||
)
|
|
||||||
WHERE block_hash = ANY($1::VARCHAR(66)[])`
|
|
||||||
RetrieveHeadersByBlockNumberPgStr = `SELECT cid, data
|
|
||||||
FROM eth.header_cids
|
|
||||||
INNER JOIN public.blocks ON (
|
|
||||||
header_cids.mh_key = blocks.key
|
|
||||||
AND header_cids.block_number = blocks.block_number
|
|
||||||
)
|
|
||||||
WHERE header_cids.block_number = $1`
|
|
||||||
RetrieveHeaderByHashPgStr = `SELECT cid, data
|
RetrieveHeaderByHashPgStr = `SELECT cid, data
|
||||||
FROM eth.header_cids
|
FROM eth.header_cids
|
||||||
INNER JOIN public.blocks ON (
|
INNER JOIN public.blocks ON (
|
||||||
@ -53,13 +38,6 @@ const (
|
|||||||
AND header_cids.block_number = blocks.block_number
|
AND header_cids.block_number = blocks.block_number
|
||||||
)
|
)
|
||||||
WHERE block_hash = $1`
|
WHERE block_hash = $1`
|
||||||
RetrieveUnclesByHashesPgStr = `SELECT cid, data
|
|
||||||
FROM eth.uncle_cids
|
|
||||||
INNER JOIN public.blocks ON (
|
|
||||||
uncle_cids.mh_key = blocks.key
|
|
||||||
AND uncle_cids.block_number = blocks.block_number
|
|
||||||
)
|
|
||||||
WHERE block_hash = ANY($1::VARCHAR(66)[])`
|
|
||||||
RetrieveUnclesPgStr = `SELECT uncle_cids.cid, data
|
RetrieveUnclesPgStr = `SELECT uncle_cids.cid, data
|
||||||
FROM eth.uncle_cids
|
FROM eth.uncle_cids
|
||||||
INNER JOIN eth.header_cids ON (
|
INNER JOIN eth.header_cids ON (
|
||||||
@ -85,31 +63,6 @@ const (
|
|||||||
)
|
)
|
||||||
WHERE header_cids.block_hash = $1
|
WHERE header_cids.block_hash = $1
|
||||||
ORDER BY uncle_cids.parent_hash`
|
ORDER BY uncle_cids.parent_hash`
|
||||||
RetrieveUnclesByBlockNumberPgStr = `SELECT uncle_cids.cid, data
|
|
||||||
FROM eth.uncle_cids
|
|
||||||
INNER JOIN eth.header_cids ON (
|
|
||||||
uncle_cids.header_id = header_cids.block_hash
|
|
||||||
AND uncle_cids.block_number = header_cids.block_number
|
|
||||||
)
|
|
||||||
INNER JOIN public.blocks ON (
|
|
||||||
uncle_cids.mh_key = blocks.key
|
|
||||||
AND uncle_cids.block_number = blocks.block_number
|
|
||||||
)
|
|
||||||
WHERE header_cids.block_number = $1`
|
|
||||||
RetrieveUncleByHashPgStr = `SELECT cid, data
|
|
||||||
FROM eth.uncle_cids
|
|
||||||
INNER JOIN public.blocks ON (
|
|
||||||
uncle_cids.mh_key = blocks.key
|
|
||||||
AND uncle_cids.block_number = blocks.block_number
|
|
||||||
)
|
|
||||||
WHERE block_hash = $1`
|
|
||||||
RetrieveTransactionsByHashesPgStr = `SELECT DISTINCT ON (tx_hash) cid, data
|
|
||||||
FROM eth.transaction_cids
|
|
||||||
INNER JOIN public.blocks ON (
|
|
||||||
transaction_cids.mh_key = blocks.key
|
|
||||||
AND transaction_cids.block_number = blocks.block_number
|
|
||||||
)
|
|
||||||
WHERE tx_hash = ANY($1::VARCHAR(66)[])`
|
|
||||||
RetrieveTransactionsPgStr = `SELECT transaction_cids.cid, data
|
RetrieveTransactionsPgStr = `SELECT transaction_cids.cid, data
|
||||||
FROM eth.transaction_cids
|
FROM eth.transaction_cids
|
||||||
INNER JOIN eth.header_cids ON (
|
INNER JOIN eth.header_cids ON (
|
||||||
@ -135,39 +88,6 @@ const (
|
|||||||
)
|
)
|
||||||
WHERE block_hash = $1
|
WHERE block_hash = $1
|
||||||
ORDER BY eth.transaction_cids.index ASC`
|
ORDER BY eth.transaction_cids.index ASC`
|
||||||
RetrieveTransactionsByBlockNumberPgStr = `SELECT transaction_cids.cid, data
|
|
||||||
FROM eth.transaction_cids
|
|
||||||
INNER JOIN eth.header_cids ON (
|
|
||||||
transaction_cids.header_id = header_cids.block_hash
|
|
||||||
AND transaction_cids.block_number = header_cids.block_number
|
|
||||||
)
|
|
||||||
INNER JOIN public.blocks ON (
|
|
||||||
transaction_cids.mh_key = blocks.key
|
|
||||||
AND transaction_cids.block_number = blocks.block_number
|
|
||||||
)
|
|
||||||
WHERE header_cids.block_number = $1
|
|
||||||
AND block_hash = (SELECT canonical_header_hash(header_cids.block_number))
|
|
||||||
ORDER BY eth.transaction_cids.index ASC`
|
|
||||||
RetrieveTransactionByHashPgStr = `SELECT DISTINCT ON (tx_hash) cid, data
|
|
||||||
FROM eth.transaction_cids
|
|
||||||
INNER JOIN public.blocks ON (
|
|
||||||
transaction_cids.mh_key = blocks.key
|
|
||||||
AND transaction_cids.block_number = blocks.block_number
|
|
||||||
)
|
|
||||||
WHERE tx_hash = $1`
|
|
||||||
RetrieveReceiptsByTxHashesPgStr = `SELECT receipt_cids.leaf_cid, data
|
|
||||||
FROM eth.receipt_cids
|
|
||||||
INNER JOIN eth.transaction_cids ON (
|
|
||||||
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
|
|
||||||
)
|
|
||||||
INNER JOIN public.blocks ON (
|
|
||||||
receipt_cids.leaf_mh_key = blocks.key
|
|
||||||
AND receipt_cids.block_number = blocks.block_number
|
|
||||||
)
|
|
||||||
WHERE tx_hash = ANY($1::VARCHAR(66)[])
|
|
||||||
AND transaction_cids.header_id = (SELECT canonical_header_hash(transaction_cids.block_number))`
|
|
||||||
RetrieveReceiptsPgStr = `SELECT receipt_cids.leaf_cid, data, eth.transaction_cids.tx_hash
|
RetrieveReceiptsPgStr = `SELECT receipt_cids.leaf_cid, data, eth.transaction_cids.tx_hash
|
||||||
FROM eth.receipt_cids
|
FROM eth.receipt_cids
|
||||||
INNER JOIN eth.transaction_cids ON (
|
INNER JOIN eth.transaction_cids ON (
|
||||||
@ -203,37 +123,6 @@ const (
|
|||||||
)
|
)
|
||||||
WHERE block_hash = $1
|
WHERE block_hash = $1
|
||||||
ORDER BY eth.transaction_cids.index ASC`
|
ORDER BY eth.transaction_cids.index ASC`
|
||||||
RetrieveReceiptsByBlockNumberPgStr = `SELECT receipt_cids.leaf_cid, data
|
|
||||||
FROM eth.receipt_cids
|
|
||||||
INNER JOIN eth.transaction_cids ON (
|
|
||||||
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
|
|
||||||
)
|
|
||||||
INNER JOIN eth.header_cids ON (
|
|
||||||
transaction_cids.header_id = header_cids.block_hash
|
|
||||||
AND transaction_cids.block_number = header_cids.block_number
|
|
||||||
)
|
|
||||||
INNER JOIN public.blocks ON (
|
|
||||||
receipt_cids.leaf_mh_key = blocks.key
|
|
||||||
AND receipt_cids.block_number = blocks.block_number
|
|
||||||
)
|
|
||||||
WHERE header_cids.block_number = $1
|
|
||||||
AND block_hash = (SELECT canonical_header_hash(header_cids.block_number))
|
|
||||||
ORDER BY eth.transaction_cids.index ASC`
|
|
||||||
RetrieveReceiptByTxHashPgStr = `SELECT receipt_cids.leaf_cid, data
|
|
||||||
FROM eth.receipt_cids
|
|
||||||
INNER JOIN eth.transaction_cids ON (
|
|
||||||
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
|
|
||||||
)
|
|
||||||
INNER JOIN public.blocks ON (
|
|
||||||
receipt_cids.leaf_mh_key = blocks.key
|
|
||||||
AND receipt_cids.block_number = blocks.block_number
|
|
||||||
)
|
|
||||||
WHERE tx_hash = $1
|
|
||||||
AND transaction_cids.header_id = (SELECT canonical_header_hash(transaction_cids.block_number))`
|
|
||||||
RetrieveAccountByLeafKeyAndBlockHashPgStr = `SELECT state_cids.cid, state_cids.mh_key, state_cids.block_number, state_cids.node_type
|
RetrieveAccountByLeafKeyAndBlockHashPgStr = `SELECT state_cids.cid, state_cids.mh_key, state_cids.block_number, state_cids.node_type
|
||||||
FROM eth.state_cids
|
FROM eth.state_cids
|
||||||
INNER JOIN eth.header_cids ON (
|
INNER JOIN eth.header_cids ON (
|
||||||
@ -247,18 +136,7 @@ const (
|
|||||||
AND header_cids.block_hash = (SELECT canonical_header_hash(header_cids.block_number))
|
AND header_cids.block_hash = (SELECT canonical_header_hash(header_cids.block_number))
|
||||||
ORDER BY header_cids.block_number DESC
|
ORDER BY header_cids.block_number DESC
|
||||||
LIMIT 1`
|
LIMIT 1`
|
||||||
RetrieveAccountByLeafKeyAndBlockNumberPgStr = `SELECT state_cids.cid, state_cids.mh_key, state_cids.node_type
|
RetrieveStorageLeafByAddressHashAndLeafKeyAndBlockHashPgStr = `SELECT cid, mh_key, block_number, node_type, state_leaf_removed FROM get_storage_at_by_hash($1, $2, $3)`
|
||||||
FROM eth.state_cids
|
|
||||||
INNER JOIN eth.header_cids ON (
|
|
||||||
state_cids.header_id = header_cids.block_hash
|
|
||||||
AND state_cids.block_number = header_cids.block_number
|
|
||||||
)
|
|
||||||
WHERE state_leaf_key = $1
|
|
||||||
AND header_cids.block_number <= $2
|
|
||||||
ORDER BY header_cids.block_number DESC
|
|
||||||
LIMIT 1`
|
|
||||||
RetrieveStorageLeafByAddressHashAndLeafKeyAndBlockNumberPgStr = `SELECT cid, mh_key, block_number, node_type, state_leaf_removed FROM get_storage_at_by_number($1, $2, $3)`
|
|
||||||
RetrieveStorageLeafByAddressHashAndLeafKeyAndBlockHashPgStr = `SELECT cid, mh_key, block_number, node_type, state_leaf_removed FROM get_storage_at_by_hash($1, $2, $3)`
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var EmptyNodeValue = make([]byte, common.HashLength)
|
var EmptyNodeValue = make([]byte, common.HashLength)
|
||||||
@ -285,66 +163,12 @@ func NewIPLDRetriever(db *sqlx.DB) *IPLDRetriever {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// RetrieveHeadersByHashes returns the cids and rlp bytes for the headers corresponding to the provided block hashes
|
|
||||||
func (r *IPLDRetriever) RetrieveHeadersByHashes(hashes []common.Hash) ([]string, [][]byte, error) {
|
|
||||||
headerResults := make([]ipldResult, 0)
|
|
||||||
hashStrs := make([]string, len(hashes))
|
|
||||||
for i, hash := range hashes {
|
|
||||||
hashStrs[i] = hash.Hex()
|
|
||||||
}
|
|
||||||
if err := r.db.Select(&headerResults, RetrieveHeadersByHashesPgStr, pq.Array(hashStrs)); err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
cids := make([]string, len(headerResults))
|
|
||||||
headers := make([][]byte, len(headerResults))
|
|
||||||
for i, res := range headerResults {
|
|
||||||
cids[i] = res.CID
|
|
||||||
headers[i] = res.Data
|
|
||||||
}
|
|
||||||
return cids, headers, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// RetrieveHeadersByBlockNumber returns the cids and rlp bytes for the headers corresponding to the provided block number
|
|
||||||
// This can return more than one result since there can be more than one header (non-canonical headers)
|
|
||||||
func (r *IPLDRetriever) RetrieveHeadersByBlockNumber(number uint64) ([]string, [][]byte, error) {
|
|
||||||
headerResults := make([]ipldResult, 0)
|
|
||||||
if err := r.db.Select(&headerResults, RetrieveHeadersByBlockNumberPgStr, number); err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
cids := make([]string, len(headerResults))
|
|
||||||
headers := make([][]byte, len(headerResults))
|
|
||||||
for i, res := range headerResults {
|
|
||||||
cids[i] = res.CID
|
|
||||||
headers[i] = res.Data
|
|
||||||
}
|
|
||||||
return cids, headers, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// RetrieveHeaderByHash returns the cid and rlp bytes for the header corresponding to the provided block hash
|
// RetrieveHeaderByHash returns the cid and rlp bytes for the header corresponding to the provided block hash
|
||||||
func (r *IPLDRetriever) RetrieveHeaderByHash(tx *sqlx.Tx, hash common.Hash) (string, []byte, error) {
|
func (r *IPLDRetriever) RetrieveHeaderByHash(tx *sqlx.Tx, hash common.Hash) (string, []byte, error) {
|
||||||
headerResult := new(ipldResult)
|
headerResult := new(ipldResult)
|
||||||
return headerResult.CID, headerResult.Data, tx.Get(headerResult, RetrieveHeaderByHashPgStr, hash.Hex())
|
return headerResult.CID, headerResult.Data, tx.Get(headerResult, RetrieveHeaderByHashPgStr, hash.Hex())
|
||||||
}
|
}
|
||||||
|
|
||||||
// RetrieveUnclesByHashes returns the cids and rlp bytes for the uncles corresponding to the provided uncle hashes
|
|
||||||
func (r *IPLDRetriever) RetrieveUnclesByHashes(hashes []common.Hash) ([]string, [][]byte, error) {
|
|
||||||
uncleResults := make([]ipldResult, 0)
|
|
||||||
hashStrs := make([]string, len(hashes))
|
|
||||||
for i, hash := range hashes {
|
|
||||||
hashStrs[i] = hash.Hex()
|
|
||||||
}
|
|
||||||
if err := r.db.Select(&uncleResults, RetrieveUnclesByHashesPgStr, pq.Array(hashStrs)); 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
|
|
||||||
}
|
|
||||||
|
|
||||||
// RetrieveUncles returns the cids and rlp bytes for the uncles corresponding to the provided block hash, number (of non-omner root block)
|
// RetrieveUncles returns the cids and rlp bytes for the uncles corresponding to the provided block hash, number (of non-omner root block)
|
||||||
func (r *IPLDRetriever) RetrieveUncles(tx *sqlx.Tx, hash common.Hash, number uint64) ([]string, [][]byte, error) {
|
func (r *IPLDRetriever) RetrieveUncles(tx *sqlx.Tx, hash common.Hash, number uint64) ([]string, [][]byte, error) {
|
||||||
uncleResults := make([]ipldResult, 0)
|
uncleResults := make([]ipldResult, 0)
|
||||||
@ -375,46 +199,6 @@ func (r *IPLDRetriever) RetrieveUnclesByBlockHash(tx *sqlx.Tx, hash common.Hash)
|
|||||||
return cids, uncles, nil
|
return cids, uncles, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// RetrieveUnclesByBlockNumber returns the cids and rlp bytes for the uncles corresponding to the provided block number (of non-omner root block)
|
|
||||||
func (r *IPLDRetriever) RetrieveUnclesByBlockNumber(number uint64) ([]string, [][]byte, error) {
|
|
||||||
uncleResults := make([]ipldResult, 0)
|
|
||||||
if err := r.db.Select(&uncleResults, RetrieveUnclesByBlockNumberPgStr, 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
|
|
||||||
}
|
|
||||||
|
|
||||||
// RetrieveUncleByHash returns the cid and rlp bytes for the uncle corresponding to the provided uncle hash
|
|
||||||
func (r *IPLDRetriever) RetrieveUncleByHash(hash common.Hash) (string, []byte, error) {
|
|
||||||
uncleResult := new(ipldResult)
|
|
||||||
return uncleResult.CID, uncleResult.Data, r.db.Get(uncleResult, RetrieveUncleByHashPgStr, hash.Hex())
|
|
||||||
}
|
|
||||||
|
|
||||||
// RetrieveTransactionsByHashes returns the cids and rlp bytes for the transactions corresponding to the provided tx hashes
|
|
||||||
func (r *IPLDRetriever) RetrieveTransactionsByHashes(hashes []common.Hash) ([]string, [][]byte, error) {
|
|
||||||
txResults := make([]ipldResult, 0)
|
|
||||||
hashStrs := make([]string, len(hashes))
|
|
||||||
for i, hash := range hashes {
|
|
||||||
hashStrs[i] = hash.Hex()
|
|
||||||
}
|
|
||||||
if err := r.db.Select(&txResults, RetrieveTransactionsByHashesPgStr, pq.Array(hashStrs)); 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
|
|
||||||
}
|
|
||||||
|
|
||||||
// RetrieveTransactions returns the cids and rlp bytes for the transactions corresponding to the provided block hash, number
|
// RetrieveTransactions returns the cids and rlp bytes for the transactions corresponding to the provided block hash, number
|
||||||
func (r *IPLDRetriever) RetrieveTransactions(tx *sqlx.Tx, hash common.Hash, number uint64) ([]string, [][]byte, error) {
|
func (r *IPLDRetriever) RetrieveTransactions(tx *sqlx.Tx, hash common.Hash, number uint64) ([]string, [][]byte, error) {
|
||||||
txResults := make([]ipldResult, 0)
|
txResults := make([]ipldResult, 0)
|
||||||
@ -445,27 +229,6 @@ func (r *IPLDRetriever) RetrieveTransactionsByBlockHash(tx *sqlx.Tx, hash common
|
|||||||
return cids, txs, nil
|
return cids, txs, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// RetrieveTransactionsByBlockNumber returns the cids and rlp bytes for the transactions corresponding to the provided block number
|
|
||||||
func (r *IPLDRetriever) RetrieveTransactionsByBlockNumber(number uint64) ([]string, [][]byte, error) {
|
|
||||||
txResults := make([]ipldResult, 0)
|
|
||||||
if err := r.db.Select(&txResults, RetrieveTransactionsByBlockNumberPgStr, 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
|
|
||||||
}
|
|
||||||
|
|
||||||
// RetrieveTransactionByTxHash returns the cid and rlp bytes for the transaction corresponding to the provided tx hash
|
|
||||||
func (r *IPLDRetriever) RetrieveTransactionByTxHash(hash common.Hash) (string, []byte, error) {
|
|
||||||
txResult := new(ipldResult)
|
|
||||||
return txResult.CID, txResult.Data, r.db.Get(txResult, RetrieveTransactionByHashPgStr, hash.Hex())
|
|
||||||
}
|
|
||||||
|
|
||||||
// DecodeLeafNode decodes the leaf node data
|
// DecodeLeafNode decodes the leaf node data
|
||||||
func DecodeLeafNode(node []byte) ([]byte, error) {
|
func DecodeLeafNode(node []byte) ([]byte, error) {
|
||||||
var nodeElements []interface{}
|
var nodeElements []interface{}
|
||||||
@ -483,29 +246,6 @@ func DecodeLeafNode(node []byte) ([]byte, error) {
|
|||||||
return nodeElements[1].([]byte), nil
|
return nodeElements[1].([]byte), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// RetrieveReceiptsByTxHashes returns the cids and rlp bytes for the receipts corresponding to the provided tx hashes
|
|
||||||
func (r *IPLDRetriever) RetrieveReceiptsByTxHashes(hashes []common.Hash) ([]string, [][]byte, error) {
|
|
||||||
rctResults := make([]rctIpldResult, 0)
|
|
||||||
hashStrs := make([]string, len(hashes))
|
|
||||||
for i, hash := range hashes {
|
|
||||||
hashStrs[i] = hash.Hex()
|
|
||||||
}
|
|
||||||
if err := r.db.Select(&rctResults, RetrieveReceiptsByTxHashesPgStr, pq.Array(hashStrs)); err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
cids := make([]string, len(rctResults))
|
|
||||||
rcts := make([][]byte, len(rctResults))
|
|
||||||
for i, res := range rctResults {
|
|
||||||
cids[i] = res.LeafCID
|
|
||||||
nodeVal, err := DecodeLeafNode(res.Data)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
rcts[i] = nodeVal
|
|
||||||
}
|
|
||||||
return cids, rcts, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// RetrieveReceipts returns the cids and rlp bytes for the receipts corresponding to the provided block hash, number.
|
// 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.
|
// cid returned corresponds to the leaf node data which contains the receipt.
|
||||||
func (r *IPLDRetriever) RetrieveReceipts(tx *sqlx.Tx, hash common.Hash, number uint64) ([]string, [][]byte, []common.Hash, error) {
|
func (r *IPLDRetriever) RetrieveReceipts(tx *sqlx.Tx, hash common.Hash, number uint64) ([]string, [][]byte, []common.Hash, error) {
|
||||||
@ -554,41 +294,6 @@ func (r *IPLDRetriever) RetrieveReceiptsByBlockHash(tx *sqlx.Tx, hash common.Has
|
|||||||
return cids, rcts, txs, nil
|
return cids, rcts, txs, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// RetrieveReceiptsByBlockNumber 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 *IPLDRetriever) RetrieveReceiptsByBlockNumber(number uint64) ([]string, [][]byte, error) {
|
|
||||||
rctResults := make([]rctIpldResult, 0)
|
|
||||||
if err := r.db.Select(&rctResults, RetrieveReceiptsByBlockNumberPgStr, number); err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
cids := make([]string, len(rctResults))
|
|
||||||
rcts := make([][]byte, len(rctResults))
|
|
||||||
for i, res := range rctResults {
|
|
||||||
cids[i] = res.LeafCID
|
|
||||||
nodeVal, err := DecodeLeafNode(res.Data)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
rcts[i] = nodeVal
|
|
||||||
}
|
|
||||||
return cids, rcts, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// RetrieveReceiptByHash returns the cid and rlp bytes for the receipt corresponding to the provided tx hash.
|
|
||||||
// cid returned corresponds to the leaf node data which contains the receipt.
|
|
||||||
func (r *IPLDRetriever) RetrieveReceiptByHash(hash common.Hash) (string, []byte, error) {
|
|
||||||
rctResult := new(rctIpldResult)
|
|
||||||
if err := r.db.Select(&rctResult, RetrieveReceiptByTxHashPgStr, hash.Hex()); err != nil {
|
|
||||||
return "", nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
nodeVal, err := DecodeLeafNode(rctResult.Data)
|
|
||||||
if err != nil {
|
|
||||||
return "", nil, err
|
|
||||||
}
|
|
||||||
return rctResult.LeafCID, nodeVal, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type nodeInfo struct {
|
type nodeInfo struct {
|
||||||
CID string `db:"cid"`
|
CID string `db:"cid"`
|
||||||
MhKey string `db:"mh_key"`
|
MhKey string `db:"mh_key"`
|
||||||
@ -630,35 +335,6 @@ func (r *IPLDRetriever) RetrieveAccountByAddressAndBlockHash(address common.Addr
|
|||||||
return accountResult.CID, i[1].([]byte), nil
|
return accountResult.CID, i[1].([]byte), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// RetrieveAccountByAddressAndBlockNumber returns the cid and rlp bytes for the account corresponding to the provided address and block number
|
|
||||||
// This can return a non-canonical account
|
|
||||||
func (r *IPLDRetriever) RetrieveAccountByAddressAndBlockNumber(address common.Address, number uint64) (string, []byte, error) {
|
|
||||||
accountResult := new(nodeInfo)
|
|
||||||
leafKey := crypto.Keccak256Hash(address.Bytes())
|
|
||||||
if err := r.db.Get(accountResult, RetrieveAccountByLeafKeyAndBlockNumberPgStr, leafKey.Hex(), number); err != nil {
|
|
||||||
return "", nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if accountResult.NodeType == sdtypes.Removed.Int() {
|
|
||||||
return "", EmptyNodeValue, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
var err error
|
|
||||||
accountResult.Data, err = shared.FetchIPLD(r.db, accountResult.MhKey, number)
|
|
||||||
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 IPLDRetriever 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
|
// RetrieveStorageAtByAddressAndStorageSlotAndBlockHash returns the cid and rlp bytes for the storage value corresponding to the provided address, storage slot, and block hash
|
||||||
func (r *IPLDRetriever) RetrieveStorageAtByAddressAndStorageSlotAndBlockHash(address common.Address, key, hash common.Hash) (string, []byte, []byte, error) {
|
func (r *IPLDRetriever) RetrieveStorageAtByAddressAndStorageSlotAndBlockHash(address common.Address, key, hash common.Hash) (string, []byte, []byte, error) {
|
||||||
storageResult := new(nodeInfo)
|
storageResult := new(nodeInfo)
|
||||||
@ -690,32 +366,3 @@ func (r *IPLDRetriever) RetrieveStorageAtByAddressAndStorageSlotAndBlockHash(add
|
|||||||
}
|
}
|
||||||
return storageResult.CID, storageResult.Data, i[1].([]byte), nil
|
return storageResult.CID, storageResult.Data, i[1].([]byte), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// RetrieveStorageAtByAddressAndStorageKeyAndBlockNumber returns the cid and rlp bytes for the storage value corresponding to the provided address, storage key, and block number
|
|
||||||
// This can retrun a non-canonical value
|
|
||||||
func (r *IPLDRetriever) RetrieveStorageAtByAddressAndStorageKeyAndBlockNumber(address common.Address, storageLeafKey common.Hash, number uint64) (string, []byte, error) {
|
|
||||||
storageResult := new(nodeInfo)
|
|
||||||
stateLeafKey := crypto.Keccak256Hash(address.Bytes())
|
|
||||||
if err := r.db.Get(storageResult, RetrieveStorageLeafByAddressHashAndLeafKeyAndBlockNumberPgStr, stateLeafKey.Hex(), storageLeafKey.Hex(), number); err != nil {
|
|
||||||
return "", nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if storageResult.StateLeafRemoved || storageResult.NodeType == sdtypes.Removed.Int() {
|
|
||||||
return "", EmptyNodeValue, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
var err error
|
|
||||||
storageResult.Data, err = shared.FetchIPLD(r.db, storageResult.MhKey, number)
|
|
||||||
if err != nil {
|
|
||||||
return "", nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
var i []interface{}
|
|
||||||
if err := rlp.DecodeBytes(storageResult.Data, &i); err != nil {
|
|
||||||
return "", nil, fmt.Errorf("error decoding storage leaf node rlp: %s", err.Error())
|
|
||||||
}
|
|
||||||
if len(i) != 2 {
|
|
||||||
return "", nil, fmt.Errorf("eth IPLDRetriever expected storage leaf node rlp to decode into two elements")
|
|
||||||
}
|
|
||||||
return storageResult.CID, i[1].([]byte), nil
|
|
||||||
}
|
|
||||||
|
@ -16,38 +16,6 @@
|
|||||||
|
|
||||||
package eth
|
package eth
|
||||||
|
|
||||||
import (
|
|
||||||
"math/big"
|
|
||||||
|
|
||||||
"github.com/spf13/viper"
|
|
||||||
)
|
|
||||||
|
|
||||||
// SubscriptionSettings config is used by a subscriber to specify what eth data to stream from the watcher
|
|
||||||
type SubscriptionSettings struct {
|
|
||||||
BackFill bool
|
|
||||||
BackFillOnly bool
|
|
||||||
Start *big.Int
|
|
||||||
End *big.Int // set to 0 or a negative value to have no ending block
|
|
||||||
HeaderFilter HeaderFilter
|
|
||||||
TxFilter TxFilter
|
|
||||||
ReceiptFilter ReceiptFilter
|
|
||||||
StateFilter StateFilter
|
|
||||||
StorageFilter StorageFilter
|
|
||||||
}
|
|
||||||
|
|
||||||
// HeaderFilter contains filter settings for headers
|
|
||||||
type HeaderFilter struct {
|
|
||||||
Off bool
|
|
||||||
Uncles bool
|
|
||||||
}
|
|
||||||
|
|
||||||
// TxFilter contains filter settings for txs
|
|
||||||
type TxFilter struct {
|
|
||||||
Off bool
|
|
||||||
Src []string
|
|
||||||
Dst []string
|
|
||||||
}
|
|
||||||
|
|
||||||
// ReceiptFilter contains filter settings for receipts
|
// ReceiptFilter contains filter settings for receipts
|
||||||
type ReceiptFilter struct {
|
type ReceiptFilter struct {
|
||||||
Off bool
|
Off bool
|
||||||
@ -56,70 +24,3 @@ type ReceiptFilter struct {
|
|||||||
LogAddresses []string // receipt contains logs from the provided addresses
|
LogAddresses []string // receipt contains logs from the provided addresses
|
||||||
Topics [][]string
|
Topics [][]string
|
||||||
}
|
}
|
||||||
|
|
||||||
// StateFilter contains filter settings for state
|
|
||||||
type StateFilter struct {
|
|
||||||
Off bool
|
|
||||||
Addresses []string // is converted to state key by taking its keccak256 hash
|
|
||||||
IntermediateNodes bool
|
|
||||||
}
|
|
||||||
|
|
||||||
// StorageFilter contains filter settings for storage
|
|
||||||
type StorageFilter struct {
|
|
||||||
Off bool
|
|
||||||
Addresses []string
|
|
||||||
StorageKeys []string // need to be the hashs key themselves not slot position
|
|
||||||
IntermediateNodes bool
|
|
||||||
}
|
|
||||||
|
|
||||||
// Init is used to initialize a EthSubscription struct with env variables
|
|
||||||
func NewEthSubscriptionConfig() (*SubscriptionSettings, error) {
|
|
||||||
sc := new(SubscriptionSettings)
|
|
||||||
// Below default to false, which means we do not backfill by default
|
|
||||||
sc.BackFill = viper.GetBool("watcher.ethSubscription.historicalData")
|
|
||||||
sc.BackFillOnly = viper.GetBool("watcher.ethSubscription.historicalDataOnly")
|
|
||||||
// Below default to 0
|
|
||||||
// 0 start means we start at the beginning and 0 end means we continue indefinitely
|
|
||||||
sc.Start = big.NewInt(viper.GetInt64("watcher.ethSubscription.startingBlock"))
|
|
||||||
sc.End = big.NewInt(viper.GetInt64("watcher.ethSubscription.endingBlock"))
|
|
||||||
// Below default to false, which means we get all headers and no uncles by default
|
|
||||||
sc.HeaderFilter = HeaderFilter{
|
|
||||||
Off: viper.GetBool("watcher.ethSubscription.headerFilter.off"),
|
|
||||||
Uncles: viper.GetBool("watcher.ethSubscription.headerFilter.uncles"),
|
|
||||||
}
|
|
||||||
// Below defaults to false and two slices of length 0
|
|
||||||
// Which means we get all transactions by default
|
|
||||||
sc.TxFilter = TxFilter{
|
|
||||||
Off: viper.GetBool("watcher.ethSubscription.txFilter.off"),
|
|
||||||
Src: viper.GetStringSlice("watcher.ethSubscription.txFilter.src"),
|
|
||||||
Dst: viper.GetStringSlice("watcher.ethSubscription.txFilter.dst"),
|
|
||||||
}
|
|
||||||
// By default all of the topic slices will be empty => match on any/all topics
|
|
||||||
topics := make([][]string, 4)
|
|
||||||
topics[0] = viper.GetStringSlice("watcher.ethSubscription.receiptFilter.topic0s")
|
|
||||||
topics[1] = viper.GetStringSlice("watcher.ethSubscription.receiptFilter.topic1s")
|
|
||||||
topics[2] = viper.GetStringSlice("watcher.ethSubscription.receiptFilter.topic2s")
|
|
||||||
topics[3] = viper.GetStringSlice("watcher.ethSubscription.receiptFilter.topic3s")
|
|
||||||
sc.ReceiptFilter = ReceiptFilter{
|
|
||||||
Off: viper.GetBool("watcher.ethSubscription.receiptFilter.off"),
|
|
||||||
MatchTxs: viper.GetBool("watcher.ethSubscription.receiptFilter.matchTxs"),
|
|
||||||
LogAddresses: viper.GetStringSlice("watcher.ethSubscription.receiptFilter.contracts"),
|
|
||||||
Topics: topics,
|
|
||||||
}
|
|
||||||
// Below defaults to two false, and a slice of length 0
|
|
||||||
// Which means we get all state leafs by default, but no intermediate nodes
|
|
||||||
sc.StateFilter = StateFilter{
|
|
||||||
Off: viper.GetBool("watcher.ethSubscription.stateFilter.off"),
|
|
||||||
IntermediateNodes: viper.GetBool("watcher.ethSubscription.stateFilter.intermediateNodes"),
|
|
||||||
Addresses: viper.GetStringSlice("watcher.ethSubscription.stateFilter.addresses"),
|
|
||||||
}
|
|
||||||
// Below defaults to two false, and two slices of length 0
|
|
||||||
// Which means we get all storage leafs by default, but no intermediate nodes
|
|
||||||
sc.StorageFilter = StorageFilter{
|
|
||||||
Off: viper.GetBool("watcher.ethSubscription.storageFilter.off"),
|
|
||||||
IntermediateNodes: viper.GetBool("watcher.ethSubscription.storageFilter.intermediateNodes"),
|
|
||||||
Addresses: viper.GetStringSlice("watcher.ethSubscription.storageFilter.addresses"),
|
|
||||||
StorageKeys: viper.GetStringSlice("watcher.ethSubscription.storageFilter.storageKeys"),
|
|
||||||
}
|
|
||||||
return sc, nil
|
|
||||||
}
|
|
||||||
|
@ -17,6 +17,7 @@
|
|||||||
package test_helpers
|
package test_helpers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"math/big"
|
"math/big"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
@ -27,7 +28,10 @@ import (
|
|||||||
"github.com/ethereum/go-ethereum/core/vm"
|
"github.com/ethereum/go-ethereum/core/vm"
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"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/test_helpers"
|
"github.com/ethereum/go-ethereum/statediff/test_helpers"
|
||||||
|
"github.com/ipfs/go-cid"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Test variables
|
// Test variables
|
||||||
@ -106,3 +110,40 @@ func TestChainGen(i int, block *core.BlockGen) {
|
|||||||
block.AddTx(tx)
|
block.AddTx(tx)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetRctLeafNodeData converts the receipts to receipt trie and returns the receipt leaf node IPLD data and
|
||||||
|
// corresponding CIDs
|
||||||
|
func GetRctLeafNodeData(rcts types.Receipts) ([]cid.Cid, [][]byte, error) {
|
||||||
|
receiptTrie := ipld.NewRctTrie()
|
||||||
|
for idx, rct := range rcts {
|
||||||
|
ethRct, err := ipld.NewReceipt(rct)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
if err = receiptTrie.Add(idx, ethRct.RawData()); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rctLeafNodes, keys, err := receiptTrie.GetLeafNodes()
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
ethRctleafNodeCids := make([]cid.Cid, len(rctLeafNodes))
|
||||||
|
ethRctleafNodeData := make([][]byte, len(rctLeafNodes))
|
||||||
|
for i, rln := range rctLeafNodes {
|
||||||
|
var idx uint
|
||||||
|
|
||||||
|
r := bytes.NewReader(keys[i].TrieKey)
|
||||||
|
err = rlp.Decode(r, &idx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
ethRctleafNodeCids[idx] = rln.Cid()
|
||||||
|
ethRctleafNodeData[idx] = rln.RawData()
|
||||||
|
}
|
||||||
|
|
||||||
|
return ethRctleafNodeCids, ethRctleafNodeData, nil
|
||||||
|
}
|
||||||
|
@ -162,7 +162,7 @@ var (
|
|||||||
Tx3 = GetTxnRlp(2, MockTransactions)
|
Tx3 = GetTxnRlp(2, MockTransactions)
|
||||||
Tx4 = GetTxnRlp(3, MockTransactions)
|
Tx4 = GetTxnRlp(3, MockTransactions)
|
||||||
|
|
||||||
rctCIDs, rctIPLDData, _ = eth.GetRctLeafNodeData(MockReceipts)
|
rctCIDs, rctIPLDData, _ = GetRctLeafNodeData(MockReceipts)
|
||||||
HeaderCID, _ = ipld.RawdataToCid(ipld.MEthHeader, MockHeaderRlp, multihash.KECCAK_256)
|
HeaderCID, _ = ipld.RawdataToCid(ipld.MEthHeader, MockHeaderRlp, multihash.KECCAK_256)
|
||||||
HeaderMhKey = shared.MultihashKeyFromCID(HeaderCID)
|
HeaderMhKey = shared.MultihashKeyFromCID(HeaderCID)
|
||||||
Trx1CID, _ = ipld.RawdataToCid(ipld.MEthTx, Tx1, multihash.KECCAK_256)
|
Trx1CID, _ = ipld.RawdataToCid(ipld.MEthTx, Tx1, multihash.KECCAK_256)
|
||||||
|
@ -17,13 +17,8 @@
|
|||||||
package serve
|
package serve
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
|
|
||||||
"github.com/cerc-io/ipld-eth-server/v4/pkg/log"
|
|
||||||
"github.com/ethereum/go-ethereum/rpc"
|
"github.com/ethereum/go-ethereum/rpc"
|
||||||
"github.com/ethereum/go-ethereum/statediff/types"
|
"github.com/ethereum/go-ethereum/statediff/types"
|
||||||
|
|
||||||
"github.com/cerc-io/ipld-eth-server/v4/pkg/eth"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// APIName is the namespace used for the state diffing service API
|
// APIName is the namespace used for the state diffing service API
|
||||||
@ -46,45 +41,6 @@ func NewPublicServerAPI(w Server, client *rpc.Client) *PublicServerAPI {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Stream is the public method to setup a subscription that fires off IPLD payloads as they are processed
|
|
||||||
func (api *PublicServerAPI) Stream(ctx context.Context, params eth.SubscriptionSettings) (*rpc.Subscription, error) {
|
|
||||||
// ensure that the RPC connection supports subscriptions
|
|
||||||
notifier, supported := rpc.NotifierFromContext(ctx)
|
|
||||||
if !supported {
|
|
||||||
return nil, rpc.ErrNotificationsUnsupported
|
|
||||||
}
|
|
||||||
|
|
||||||
// create subscription and start waiting for stream events
|
|
||||||
rpcSub := notifier.CreateSubscription()
|
|
||||||
|
|
||||||
go func() {
|
|
||||||
// subscribe to events from the SyncPublishScreenAndServe service
|
|
||||||
payloadChannel := make(chan SubscriptionPayload, PayloadChanBufferSize)
|
|
||||||
quitChan := make(chan bool, 1)
|
|
||||||
go api.w.Subscribe(rpcSub.ID, payloadChannel, quitChan, params)
|
|
||||||
|
|
||||||
// loop and await payloads and relay them to the subscriber using notifier
|
|
||||||
for {
|
|
||||||
select {
|
|
||||||
case packet := <-payloadChannel:
|
|
||||||
if err := notifier.Notify(rpcSub.ID, packet); err != nil {
|
|
||||||
log.Error("Failed to send watcher data packet", "err", err)
|
|
||||||
api.w.Unsubscribe(rpcSub.ID)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
case <-rpcSub.Err():
|
|
||||||
api.w.Unsubscribe(rpcSub.ID)
|
|
||||||
return
|
|
||||||
case <-quitChan:
|
|
||||||
// don't need to unsubscribe from the watcher, the service does so before sending the quit signal this way
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
return rpcSub, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// WatchAddress makes a geth WatchAddress API call with the given operation and args
|
// WatchAddress makes a geth WatchAddress API call with the given operation and args
|
||||||
func (api *PublicServerAPI) WatchAddress(operation types.OperationType, args []types.WatchAddressArg) error {
|
func (api *PublicServerAPI) WatchAddress(operation types.OperationType, args []types.WatchAddressArg) error {
|
||||||
err := api.rpc.Call(nil, "statediff_watchAddress", operation, args)
|
err := api.rpc.Call(nil, "statediff_watchAddress", operation, args)
|
||||||
|
@ -17,19 +17,15 @@
|
|||||||
package serve
|
package serve
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"strconv"
|
"strconv"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/cerc-io/ipld-eth-server/v4/pkg/log"
|
"github.com/cerc-io/ipld-eth-server/v4/pkg/log"
|
||||||
"github.com/ethereum/go-ethereum/common"
|
|
||||||
"github.com/ethereum/go-ethereum/core/vm"
|
"github.com/ethereum/go-ethereum/core/vm"
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
|
||||||
"github.com/ethereum/go-ethereum/eth/tracers"
|
"github.com/ethereum/go-ethereum/eth/tracers"
|
||||||
ethnode "github.com/ethereum/go-ethereum/node"
|
ethnode "github.com/ethereum/go-ethereum/node"
|
||||||
"github.com/ethereum/go-ethereum/p2p"
|
"github.com/ethereum/go-ethereum/p2p"
|
||||||
"github.com/ethereum/go-ethereum/rlp"
|
|
||||||
"github.com/ethereum/go-ethereum/rpc"
|
"github.com/ethereum/go-ethereum/rpc"
|
||||||
"github.com/jmoiron/sqlx"
|
"github.com/jmoiron/sqlx"
|
||||||
|
|
||||||
@ -51,11 +47,7 @@ type Server interface {
|
|||||||
APIs() []rpc.API
|
APIs() []rpc.API
|
||||||
Protocols() []p2p.Protocol
|
Protocols() []p2p.Protocol
|
||||||
// Pub-Sub handling event loop
|
// Pub-Sub handling event loop
|
||||||
Serve(wg *sync.WaitGroup, screenAndServePayload <-chan eth.ConvertedPayload)
|
Serve(wg *sync.WaitGroup)
|
||||||
// Method to subscribe to the service
|
|
||||||
Subscribe(id rpc.ID, sub chan<- SubscriptionPayload, quitChan chan<- bool, params eth.SubscriptionSettings)
|
|
||||||
// Method to unsubscribe from the service
|
|
||||||
Unsubscribe(id rpc.ID)
|
|
||||||
// Backend exposes the server's backend
|
// Backend exposes the server's backend
|
||||||
Backend() *eth.Backend
|
Backend() *eth.Backend
|
||||||
}
|
}
|
||||||
@ -64,22 +56,10 @@ type Server interface {
|
|||||||
type Service struct {
|
type Service struct {
|
||||||
// Used to sync access to the Subscriptions
|
// Used to sync access to the Subscriptions
|
||||||
sync.Mutex
|
sync.Mutex
|
||||||
// Interface for filtering and serving data according to subscribed clients according to their specification
|
|
||||||
Filterer eth.Filterer
|
|
||||||
// Interface for fetching IPLD objects from IPFS
|
|
||||||
IPLDFetcher eth.Fetcher
|
|
||||||
// Interface for searching and retrieving CIDs from Postgres index
|
|
||||||
Retriever eth.Retriever
|
|
||||||
// Used to signal shutdown of the service
|
// Used to signal shutdown of the service
|
||||||
QuitChan chan bool
|
QuitChan chan bool
|
||||||
// A mapping of rpc.IDs to their subscription channels, mapped to their subscription type (hash of the StreamFilters)
|
|
||||||
Subscriptions map[common.Hash]map[rpc.ID]Subscription
|
|
||||||
// A mapping of subscription params hash to the corresponding subscription params
|
|
||||||
SubscriptionTypes map[common.Hash]eth.SubscriptionSettings
|
|
||||||
// Underlying db
|
// Underlying db
|
||||||
db *sqlx.DB
|
db *sqlx.DB
|
||||||
// wg for syncing serve processes
|
|
||||||
serveWg *sync.WaitGroup
|
|
||||||
// rpc client for forwarding cache misses
|
// rpc client for forwarding cache misses
|
||||||
client *rpc.Client
|
client *rpc.Client
|
||||||
// whether the proxied client supports state diffing
|
// whether the proxied client supports state diffing
|
||||||
@ -101,13 +81,8 @@ type Service struct {
|
|||||||
// NewServer creates a new Server using an underlying Service struct
|
// NewServer creates a new Server using an underlying Service struct
|
||||||
func NewServer(settings *Config) (Server, error) {
|
func NewServer(settings *Config) (Server, error) {
|
||||||
sap := new(Service)
|
sap := new(Service)
|
||||||
sap.Retriever = eth.NewCIDRetriever(settings.DB)
|
|
||||||
sap.IPLDFetcher = eth.NewIPLDFetcher(settings.DB)
|
|
||||||
sap.Filterer = eth.NewResponseFilterer()
|
|
||||||
sap.db = settings.DB
|
sap.db = settings.DB
|
||||||
sap.QuitChan = make(chan bool)
|
sap.QuitChan = make(chan bool)
|
||||||
sap.Subscriptions = make(map[common.Hash]map[rpc.ID]Subscription)
|
|
||||||
sap.SubscriptionTypes = make(map[common.Hash]eth.SubscriptionSettings)
|
|
||||||
sap.client = settings.Client
|
sap.client = settings.Client
|
||||||
sap.supportsStateDiffing = settings.SupportStateDiff
|
sap.supportsStateDiffing = settings.SupportStateDiff
|
||||||
sap.stateDiffTimeout = settings.StateDiffTimeout
|
sap.stateDiffTimeout = settings.StateDiffTimeout
|
||||||
@ -177,200 +152,22 @@ func (sap *Service) APIs() []rpc.API {
|
|||||||
// It filters and sends this data to any subscribers to the service
|
// It filters and sends this data to any subscribers to the service
|
||||||
// This process can also be stood up alone, without an screenAndServePayload attached to a Sync process
|
// This process can also be stood up alone, without an screenAndServePayload attached to a Sync process
|
||||||
// and it will hang on the WaitGroup indefinitely, allowing the Service to serve historical data requests only
|
// and it will hang on the WaitGroup indefinitely, allowing the Service to serve historical data requests only
|
||||||
func (sap *Service) Serve(wg *sync.WaitGroup, screenAndServePayload <-chan eth.ConvertedPayload) {
|
func (sap *Service) Serve(wg *sync.WaitGroup) {
|
||||||
sap.serveWg = wg
|
|
||||||
go func() {
|
go func() {
|
||||||
wg.Add(1)
|
wg.Add(1)
|
||||||
defer wg.Done()
|
defer wg.Done()
|
||||||
for {
|
<-sap.QuitChan
|
||||||
select {
|
log.Info("quiting eth ipld server process")
|
||||||
case payload := <-screenAndServePayload:
|
|
||||||
sap.filterAndServe(payload)
|
|
||||||
case <-sap.QuitChan:
|
|
||||||
log.Info("quiting eth ipld server process")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}()
|
}()
|
||||||
log.Info("eth ipld server process successfully spun up")
|
log.Info("eth ipld server process successfully spun up")
|
||||||
}
|
}
|
||||||
|
|
||||||
// filterAndServe filters the payload according to each subscription type and sends to the subscriptions
|
|
||||||
func (sap *Service) filterAndServe(payload eth.ConvertedPayload) {
|
|
||||||
log.Debug("sending eth ipld payload to subscriptions")
|
|
||||||
sap.Lock()
|
|
||||||
sap.serveWg.Add(1)
|
|
||||||
defer sap.Unlock()
|
|
||||||
defer sap.serveWg.Done()
|
|
||||||
for ty, subs := range sap.Subscriptions {
|
|
||||||
// Retrieve the subscription parameters for this subscription type
|
|
||||||
subConfig, ok := sap.SubscriptionTypes[ty]
|
|
||||||
if !ok {
|
|
||||||
log.Errorf("eth ipld server subscription configuration for subscription type %s not available", ty.Hex())
|
|
||||||
sap.closeType(ty)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if subConfig.End.Int64() > 0 && subConfig.End.Int64() < payload.Block.Number().Int64() {
|
|
||||||
// We are not out of range for this subscription type
|
|
||||||
// close it, and continue to the next
|
|
||||||
sap.closeType(ty)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
response, err := sap.Filterer.Filter(subConfig, payload)
|
|
||||||
if err != nil {
|
|
||||||
log.Errorf("eth ipld server filtering error: %v", err)
|
|
||||||
sap.closeType(ty)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
responseRLP, err := rlp.EncodeToBytes(response)
|
|
||||||
if err != nil {
|
|
||||||
log.Errorf("eth ipld server rlp encoding error: %v", err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
for id, sub := range subs {
|
|
||||||
select {
|
|
||||||
case sub.PayloadChan <- SubscriptionPayload{Data: responseRLP, Err: "", Flag: EmptyFlag, Height: response.BlockNumber.Int64()}:
|
|
||||||
log.Debugf("sending eth ipld server payload to subscription %s", id)
|
|
||||||
default:
|
|
||||||
log.Infof("unable to send eth ipld payload to subscription %s; channel has no receiver", id)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Subscribe is used by the API to remotely subscribe to the service loop
|
|
||||||
// The params must be rlp serializable and satisfy the SubscriptionSettings() interface
|
|
||||||
func (sap *Service) Subscribe(id rpc.ID, sub chan<- SubscriptionPayload, quitChan chan<- bool, params eth.SubscriptionSettings) {
|
|
||||||
sap.serveWg.Add(1)
|
|
||||||
defer sap.serveWg.Done()
|
|
||||||
log.Infof("new eth ipld subscription %s", id)
|
|
||||||
subscription := Subscription{
|
|
||||||
ID: id,
|
|
||||||
PayloadChan: sub,
|
|
||||||
QuitChan: quitChan,
|
|
||||||
}
|
|
||||||
// Subscription type is defined as the hash of the rlp-serialized subscription settings
|
|
||||||
by, err := rlp.EncodeToBytes(params)
|
|
||||||
if err != nil {
|
|
||||||
sendNonBlockingErr(subscription, err)
|
|
||||||
sendNonBlockingQuit(subscription)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
subscriptionType := crypto.Keccak256Hash(by)
|
|
||||||
if !params.BackFillOnly {
|
|
||||||
// Add subscriber
|
|
||||||
sap.Lock()
|
|
||||||
if sap.Subscriptions[subscriptionType] == nil {
|
|
||||||
sap.Subscriptions[subscriptionType] = make(map[rpc.ID]Subscription)
|
|
||||||
}
|
|
||||||
sap.Subscriptions[subscriptionType][id] = subscription
|
|
||||||
sap.SubscriptionTypes[subscriptionType] = params
|
|
||||||
sap.Unlock()
|
|
||||||
}
|
|
||||||
// If the subscription requests a backfill, use the Postgres index to lookup and retrieve historical data
|
|
||||||
// Otherwise we only filter new data as it is streamed in from the state diffing geth node
|
|
||||||
if params.BackFill || params.BackFillOnly {
|
|
||||||
if err := sap.sendHistoricalData(subscription, id, params); err != nil {
|
|
||||||
sendNonBlockingErr(subscription, fmt.Errorf("eth ipld server subscription backfill error: %v", err))
|
|
||||||
sendNonBlockingQuit(subscription)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// sendHistoricalData sends historical data to the requesting subscription
|
|
||||||
func (sap *Service) sendHistoricalData(sub Subscription, id rpc.ID, params eth.SubscriptionSettings) error {
|
|
||||||
log.Infof("sending eth ipld historical data to subscription %s", id)
|
|
||||||
// Retrieve cached CIDs relevant to this subscriber
|
|
||||||
var endingBlock int64
|
|
||||||
var startingBlock int64
|
|
||||||
var err error
|
|
||||||
startingBlock, err = sap.Retriever.RetrieveFirstBlockNumber()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if startingBlock < params.Start.Int64() {
|
|
||||||
startingBlock = params.Start.Int64()
|
|
||||||
}
|
|
||||||
endingBlock, err = sap.Retriever.RetrieveLastBlockNumber()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if endingBlock > params.End.Int64() && params.End.Int64() > 0 && params.End.Int64() > startingBlock {
|
|
||||||
endingBlock = params.End.Int64()
|
|
||||||
}
|
|
||||||
log.Debugf("eth ipld historical data starting block: %d", params.Start.Int64())
|
|
||||||
log.Debugf("eth ipld historical data ending block: %d", endingBlock)
|
|
||||||
go func() {
|
|
||||||
sap.serveWg.Add(1)
|
|
||||||
defer sap.serveWg.Done()
|
|
||||||
for i := startingBlock; i <= endingBlock; i++ {
|
|
||||||
select {
|
|
||||||
case <-sap.QuitChan:
|
|
||||||
log.Infof("ethereum historical data feed to subscription %s closed", id)
|
|
||||||
return
|
|
||||||
default:
|
|
||||||
}
|
|
||||||
cidWrappers, empty, err := sap.Retriever.Retrieve(params, i)
|
|
||||||
if err != nil {
|
|
||||||
sendNonBlockingErr(sub, fmt.Errorf("eth ipld server cid retrieval error at block %d\r%s", i, err.Error()))
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if empty {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
for _, cids := range cidWrappers {
|
|
||||||
response, err := sap.IPLDFetcher.Fetch(cids)
|
|
||||||
if err != nil {
|
|
||||||
sendNonBlockingErr(sub, fmt.Errorf("eth ipld server ipld fetching error at block %d\r%s", i, err.Error()))
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
responseRLP, err := rlp.EncodeToBytes(response)
|
|
||||||
if err != nil {
|
|
||||||
log.Error(err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
select {
|
|
||||||
case sub.PayloadChan <- SubscriptionPayload{Data: responseRLP, Err: "", Flag: EmptyFlag, Height: response.BlockNumber.Int64()}:
|
|
||||||
log.Debugf("eth ipld server sending historical data payload to subscription %s", id)
|
|
||||||
default:
|
|
||||||
log.Infof("eth ipld server unable to send backFill payload to subscription %s; channel has no receiver", id)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// when we are done backfilling send an empty payload signifying so in the msg
|
|
||||||
select {
|
|
||||||
case sub.PayloadChan <- SubscriptionPayload{Data: nil, Err: "", Flag: BackFillCompleteFlag}:
|
|
||||||
log.Debugf("eth ipld server sending backFill completion notice to subscription %s", id)
|
|
||||||
default:
|
|
||||||
log.Infof("eth ipld server unable to send backFill completion notice to subscription %s", id)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Unsubscribe is used by the API to remotely unsubscribe to the StateDiffingService loop
|
|
||||||
func (sap *Service) Unsubscribe(id rpc.ID) {
|
|
||||||
log.Infof("unsubscribing %s from the eth ipld server", id)
|
|
||||||
sap.Lock()
|
|
||||||
for ty := range sap.Subscriptions {
|
|
||||||
delete(sap.Subscriptions[ty], id)
|
|
||||||
if len(sap.Subscriptions[ty]) == 0 {
|
|
||||||
// If we removed the last subscription of this type, remove the subscription type outright
|
|
||||||
delete(sap.Subscriptions, ty)
|
|
||||||
delete(sap.SubscriptionTypes, ty)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
sap.Unlock()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Start is used to begin the service
|
// Start is used to begin the service
|
||||||
// This is mostly just to satisfy the node.Service interface
|
// This is mostly just to satisfy the node.Service interface
|
||||||
func (sap *Service) Start() error {
|
func (sap *Service) Start() error {
|
||||||
log.Info("starting eth ipld server")
|
log.Info("starting eth ipld server")
|
||||||
wg := new(sync.WaitGroup)
|
wg := new(sync.WaitGroup)
|
||||||
payloadChan := make(chan eth.ConvertedPayload, PayloadChanBufferSize)
|
sap.Serve(wg)
|
||||||
sap.Serve(wg, payloadChan)
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -380,7 +177,6 @@ func (sap *Service) Stop() error {
|
|||||||
log.Infof("stopping eth ipld server")
|
log.Infof("stopping eth ipld server")
|
||||||
sap.Lock()
|
sap.Lock()
|
||||||
close(sap.QuitChan)
|
close(sap.QuitChan)
|
||||||
sap.close()
|
|
||||||
sap.Unlock()
|
sap.Unlock()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -389,28 +185,3 @@ func (sap *Service) Stop() error {
|
|||||||
func (sap *Service) Backend() *eth.Backend {
|
func (sap *Service) Backend() *eth.Backend {
|
||||||
return sap.backend
|
return sap.backend
|
||||||
}
|
}
|
||||||
|
|
||||||
// close is used to close all listening subscriptions
|
|
||||||
// close needs to be called with subscription access locked
|
|
||||||
func (sap *Service) close() {
|
|
||||||
log.Infof("closing all eth ipld server subscriptions")
|
|
||||||
for subType, subs := range sap.Subscriptions {
|
|
||||||
for _, sub := range subs {
|
|
||||||
sendNonBlockingQuit(sub)
|
|
||||||
}
|
|
||||||
delete(sap.Subscriptions, subType)
|
|
||||||
delete(sap.SubscriptionTypes, subType)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// closeType is used to close all subscriptions of given type
|
|
||||||
// closeType needs to be called with subscription access locked
|
|
||||||
func (sap *Service) closeType(subType common.Hash) {
|
|
||||||
log.Infof("closing all eth ipld server subscriptions of type %s", subType.String())
|
|
||||||
subs := sap.Subscriptions[subType]
|
|
||||||
for _, sub := range subs {
|
|
||||||
sendNonBlockingQuit(sub)
|
|
||||||
}
|
|
||||||
delete(sap.Subscriptions, subType)
|
|
||||||
delete(sap.SubscriptionTypes, subType)
|
|
||||||
}
|
|
||||||
|
Loading…
Reference in New Issue
Block a user