[WIP] Implement postgraphile graphql queries #157
1
go.mod
1
go.mod
@ -22,6 +22,7 @@ require (
|
||||
github.com/sirupsen/logrus v1.8.1
|
||||
github.com/spf13/cobra v1.4.0
|
||||
github.com/spf13/viper v1.11.0
|
||||
github.com/thoas/go-funk v0.9.2
|
||||
github.com/vulcanize/eth-ipfs-state-validator/v4 v4.0.0-alpha
|
||||
github.com/vulcanize/gap-filler v0.3.1
|
||||
github.com/vulcanize/ipfs-ethdb/v4 v4.0.0-alpha
|
||||
|
@ -27,6 +27,7 @@ import (
|
||||
"github.com/jmoiron/sqlx"
|
||||
"github.com/lib/pq"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/thoas/go-funk"
|
||||
|
||||
"github.com/vulcanize/ipld-eth-server/v4/pkg/shared"
|
||||
)
|
||||
@ -608,7 +609,7 @@ func (ecr *CIDRetriever) RetrieveBlockByNumber(blockNumber int64) (models.Header
|
||||
// RetrieveHeaderCIDByHash returns the header for the given block hash
|
||||
func (ecr *CIDRetriever) RetrieveHeaderCIDByHash(tx *sqlx.Tx, blockHash common.Hash) (models.HeaderModel, error) {
|
||||
log.Debug("retrieving header cids for block hash ", blockHash.String())
|
||||
pgStr := `SELECT block_hash, CAST(block_number as Text), cid, mh_key FROM eth.header_cids
|
||||
pgStr := `SELECT block_hash, CAST(block_number as Text), parent_hash, cid, mh_key, timestamp FROM eth.header_cids
|
||||
WHERE block_hash = $1`
|
||||
var headerCID models.HeaderModel
|
||||
return headerCID, tx.Get(&headerCID, pgStr, blockHash.String())
|
||||
@ -626,6 +627,18 @@ func (ecr *CIDRetriever) RetrieveTxCIDsByHeaderID(tx *sqlx.Tx, headerID string,
|
||||
return txCIDs, tx.Select(&txCIDs, pgStr, headerID, blockNumber)
|
||||
}
|
||||
|
||||
// RetrieveTxCIDsByBlockNumber retrieves all tx CIDs for the given blockNumber
|
||||
func (ecr *CIDRetriever) RetrieveTxCIDsByBlockNumber(tx *sqlx.Tx, blockNumber int64) ([]models.TxModel, error) {
|
||||
log.Debug("retrieving tx cids for block number ", blockNumber)
|
||||
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 block_number = $1
|
||||
ORDER BY index`
|
||||
var txCIDs []models.TxModel
|
||||
return txCIDs, tx.Select(&txCIDs, pgStr, blockNumber)
|
||||
}
|
||||
|
||||
// RetrieveReceiptCIDsByTxIDs retrieves receipt CIDs by their associated tx IDs
|
||||
func (ecr *CIDRetriever) RetrieveReceiptCIDsByTxIDs(tx *sqlx.Tx, txHashes []string) ([]models.ReceiptModel, error) {
|
||||
log.Debugf("retrieving receipt cids for tx hashes %v", txHashes)
|
||||
@ -639,3 +652,131 @@ func (ecr *CIDRetriever) RetrieveReceiptCIDsByTxIDs(tx *sqlx.Tx, txHashes []stri
|
||||
var rctCIDs []models.ReceiptModel
|
||||
return rctCIDs, tx.Select(&rctCIDs, pgStr, pq.Array(txHashes))
|
||||
}
|
||||
|
||||
// RetrieveHeaderAndTxCIDsByBlockNumber retrieves header CIDs and their associated tx CIDs by block number
|
||||
func (ecr *CIDRetriever) RetrieveHeaderAndTxCIDsByBlockNumber(blockNumber int64) ([]models.HeaderModel, [][]models.TxModel, error) {
|
||||
log.Debug("retrieving header cids and tx cids for block number ", blockNumber)
|
||||
|
||||
// Begin new db tx
|
||||
tx, err := ecr.db.Beginx()
|
||||
if err != nil {
|
||||
return 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 headerCIDs []models.HeaderModel
|
||||
headerCIDs, err = ecr.RetrieveHeaderCIDs(tx, blockNumber)
|
||||
if err != nil {
|
||||
log.Error("header cid retrieval error")
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
var allTxCIDs [][]models.TxModel
|
||||
txCIDs, err := ecr.RetrieveTxCIDsByBlockNumber(tx, blockNumber)
|
||||
if err != nil {
|
||||
log.Error("tx cid retrieval error")
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
txCIDsByHeaderID := funk.Reduce(
|
||||
txCIDs,
|
||||
func(acc map[string][]models.TxModel, txCID models.TxModel) map[string][]models.TxModel {
|
||||
if _, ok := acc[txCID.HeaderID]; !ok {
|
||||
acc[txCID.HeaderID] = []models.TxModel{}
|
||||
}
|
||||
|
||||
txCIDs = append(acc[txCID.HeaderID], txCID)
|
||||
acc[txCID.HeaderID] = txCIDs
|
||||
return acc
|
||||
},
|
||||
make(map[string][]models.TxModel),
|
||||
)
|
||||
|
||||
txCIDsByHeaderIDMap := txCIDsByHeaderID.(map[string][]models.TxModel)
|
||||
|
||||
for _, headerCID := range headerCIDs {
|
||||
txCIDs := txCIDsByHeaderIDMap[headerCID.BlockHash]
|
||||
allTxCIDs = append(allTxCIDs, txCIDs)
|
||||
}
|
||||
|
||||
return headerCIDs, allTxCIDs, nil
|
||||
}
|
||||
|
||||
// RetrieveHeaderAndTxCIDsByBlockHash retrieves header CID and their associated tx CIDs by block hash
|
||||
func (ecr *CIDRetriever) RetrieveHeaderAndTxCIDsByBlockHash(blockHash common.Hash) (models.HeaderModel, []models.TxModel, error) {
|
||||
log.Debug("retrieving header cid and tx cids for block hash ", blockHash.String())
|
||||
|
||||
// Begin new db tx
|
||||
tx, err := ecr.db.Beginx()
|
||||
if err != nil {
|
||||
return models.HeaderModel{}, 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, err
|
||||
}
|
||||
blockNumber, err := strconv.ParseInt(headerCID.BlockNumber, 10, 64)
|
||||
if err != nil {
|
||||
return models.HeaderModel{}, 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, err
|
||||
}
|
||||
|
||||
return headerCID, txCIDs, nil
|
||||
}
|
||||
|
||||
// RetrieveTxCIDByHash returns the tx for the given tx hash
|
||||
func (ecr *CIDRetriever) RetrieveTxCIDByHash(txHash string) (models.TxModel, error) {
|
||||
log.Debug("retrieving tx cid for tx hash ", txHash)
|
||||
|
||||
// Begin new db tx
|
||||
tx, err := ecr.db.Beginx()
|
||||
if err != nil {
|
||||
return models.TxModel{}, err
|
||||
}
|
||||
defer func() {
|
||||
if p := recover(); p != nil {
|
||||
shared.Rollback(tx)
|
||||
panic(p)
|
||||
} else if err != nil {
|
||||
shared.Rollback(tx)
|
||||
} else {
|
||||
err = tx.Commit()
|
||||
}
|
||||
}()
|
||||
|
||||
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 tx_hash = $1
|
||||
ORDER BY index`
|
||||
var txCID models.TxModel
|
||||
return txCID, tx.Get(&txCID, pgStr, txHash)
|
||||
}
|
||||
|
@ -26,6 +26,7 @@ import (
|
||||
"github.com/ethereum/go-ethereum/statediff/indexer/models"
|
||||
"github.com/jmoiron/sqlx"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/thoas/go-funk"
|
||||
"github.com/vulcanize/ipld-eth-server/v4/pkg/shared"
|
||||
)
|
||||
|
||||
@ -100,7 +101,7 @@ func (f *IPLDFetcher) Fetch(cids CIDWrapper) (*IPLDs, error) {
|
||||
return iplds, err
|
||||
}
|
||||
|
||||
// FetchHeaders fetches headers
|
||||
// FetchHeader fetches header
|
||||
func (f *IPLDFetcher) FetchHeader(tx *sqlx.Tx, c models.HeaderModel) (models.IPLDModel, error) {
|
||||
log.Debug("fetching header ipld")
|
||||
blockNumber, err := strconv.ParseUint(c.BlockNumber, 10, 64)
|
||||
@ -119,6 +120,42 @@ func (f *IPLDFetcher) FetchHeader(tx *sqlx.Tx, c models.HeaderModel) (models.IPL
|
||||
}, nil
|
||||
}
|
||||
|
||||
// FetchHeaders fetches headers
|
||||
func (f *IPLDFetcher) FetchHeaders(tx *sqlx.Tx, cids []models.HeaderModel) ([]models.IPLDModel, error) {
|
||||
log.Debug("fetching header iplds")
|
||||
headerIPLDs := make([]models.IPLDModel, len(cids))
|
||||
|
||||
if len(cids) < 1 {
|
||||
return headerIPLDs, nil
|
||||
}
|
||||
|
||||
blockNumbers := make([]uint64, len(cids))
|
||||
mhKeys := make([]string, len(cids))
|
||||
for i, c := range cids {
|
||||
var err error
|
||||
mhKeys[i] = c.MhKey
|
||||
blockNumbers[i], err = strconv.ParseUint(c.BlockNumber, 10, 64)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
fetchedIPLDs, err := shared.FetchIPLDsByMhKeysAndBlockNumbers(tx, mhKeys, blockNumbers)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for i, c := range cids {
|
||||
headerIPLD := funk.Find(fetchedIPLDs, func(ipld models.IPLDModel) bool {
|
||||
return ipld.Key == c.MhKey
|
||||
}).(models.IPLDModel)
|
||||
|
||||
headerIPLDs[i] = headerIPLD
|
||||
}
|
||||
|
||||
return headerIPLDs, nil
|
||||
}
|
||||
|
||||
// FetchUncles fetches uncles
|
||||
func (f *IPLDFetcher) FetchUncles(tx *sqlx.Tx, cids []models.UncleModel) ([]models.IPLDModel, error) {
|
||||
log.Debug("fetching uncle iplds")
|
||||
|
@ -22,6 +22,7 @@ import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
@ -32,8 +33,10 @@ import (
|
||||
"github.com/ethereum/go-ethereum/eth/filters"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
"github.com/ethereum/go-ethereum/rpc"
|
||||
"github.com/ethereum/go-ethereum/statediff/indexer/models"
|
||||
|
||||
"github.com/vulcanize/ipld-eth-server/v4/pkg/eth"
|
||||
"github.com/vulcanize/ipld-eth-server/v4/pkg/shared"
|
||||
)
|
||||
|
||||
var (
|
||||
@ -1017,7 +1020,7 @@ func (r *Resolver) GetStorageAt(ctx context.Context, args struct {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if bytes.Compare(rlpValue, eth.EmptyNodeValue) == 0 {
|
||||
if bytes.Equal(rlpValue, eth.EmptyNodeValue) {
|
||||
return &StorageResult{value: eth.EmptyNodeValue, cid: cid, ipldBlock: ipldBlock}, nil
|
||||
}
|
||||
|
||||
@ -1122,3 +1125,274 @@ func decomposeGQLLogs(logCIDs []eth.LogResult) []logsCID {
|
||||
|
||||
return logs
|
||||
}
|
||||
|
||||
type EthTransactionCid struct {
|
||||
cid string
|
||||
txHash string
|
||||
index int32
|
||||
src string
|
||||
dst string
|
||||
ipfsBlock IPFSBlock
|
||||
}
|
||||
|
||||
func (t EthTransactionCid) Cid(ctx context.Context) string {
|
||||
return t.cid
|
||||
}
|
||||
|
||||
func (t EthTransactionCid) TxHash(ctx context.Context) string {
|
||||
return t.txHash
|
||||
}
|
||||
|
||||
func (t EthTransactionCid) Index(ctx context.Context) int32 {
|
||||
return t.index
|
||||
}
|
||||
|
||||
func (t EthTransactionCid) Src(ctx context.Context) string {
|
||||
return t.src
|
||||
}
|
||||
|
||||
func (t EthTransactionCid) Dst(ctx context.Context) string {
|
||||
return t.dst
|
||||
}
|
||||
|
||||
func (t EthTransactionCid) BlockByMhKey(ctx context.Context) IPFSBlock {
|
||||
return t.ipfsBlock
|
||||
}
|
||||
|
||||
type EthTransactionCidsConnection struct {
|
||||
nodes []*EthTransactionCid
|
||||
}
|
||||
|
||||
func (transactionCIDResult EthTransactionCidsConnection) Nodes(ctx context.Context) []*EthTransactionCid {
|
||||
return transactionCIDResult.nodes
|
||||
}
|
||||
|
||||
type IPFSBlock struct {
|
||||
key string
|
||||
data string
|
||||
}
|
||||
|
||||
func (b IPFSBlock) Key(ctx context.Context) string {
|
||||
return b.key
|
||||
}
|
||||
|
||||
func (b IPFSBlock) Data(ctx context.Context) string {
|
||||
return b.data
|
||||
}
|
||||
|
||||
type EthHeaderCid struct {
|
||||
cid string
|
||||
blockNumber BigInt
|
||||
blockHash string
|
||||
parentHash string
|
||||
timestamp BigInt
|
||||
stateRoot string
|
||||
td BigInt
|
||||
txRoot string
|
||||
receiptRoot string
|
||||
uncleRoot string
|
||||
bloom string
|
||||
transactions []*EthTransactionCid
|
||||
ipfsBlock IPFSBlock
|
||||
}
|
||||
|
||||
func (h EthHeaderCid) Cid(ctx context.Context) string {
|
||||
return h.cid
|
||||
}
|
||||
|
||||
func (h EthHeaderCid) BlockNumber(ctx context.Context) BigInt {
|
||||
return h.blockNumber
|
||||
}
|
||||
|
||||
func (h EthHeaderCid) BlockHash(ctx context.Context) string {
|
||||
return h.blockHash
|
||||
}
|
||||
|
||||
func (h EthHeaderCid) ParentHash(ctx context.Context) string {
|
||||
return h.parentHash
|
||||
}
|
||||
|
||||
func (h EthHeaderCid) Timestamp(ctx context.Context) BigInt {
|
||||
return h.timestamp
|
||||
}
|
||||
|
||||
func (h EthHeaderCid) StateRoot(ctx context.Context) string {
|
||||
return h.stateRoot
|
||||
}
|
||||
|
||||
func (h EthHeaderCid) Td(ctx context.Context) BigInt {
|
||||
return h.td
|
||||
}
|
||||
|
||||
func (h EthHeaderCid) TxRoot(ctx context.Context) string {
|
||||
return h.txRoot
|
||||
}
|
||||
|
||||
func (h EthHeaderCid) ReceiptRoot(ctx context.Context) string {
|
||||
return h.receiptRoot
|
||||
}
|
||||
|
||||
func (h EthHeaderCid) UncleRoot(ctx context.Context) string {
|
||||
return h.uncleRoot
|
||||
}
|
||||
|
||||
func (h EthHeaderCid) Bloom(ctx context.Context) string {
|
||||
return h.bloom
|
||||
}
|
||||
|
||||
func (h EthHeaderCid) EthTransactionCidsByHeaderId(ctx context.Context) EthTransactionCidsConnection {
|
||||
return EthTransactionCidsConnection{nodes: h.transactions}
|
||||
}
|
||||
|
||||
func (h EthHeaderCid) BlockByMhKey(ctx context.Context) IPFSBlock {
|
||||
return h.ipfsBlock
|
||||
}
|
||||
|
||||
type EthHeaderCidsConnection struct {
|
||||
nodes []*EthHeaderCid
|
||||
}
|
||||
|
||||
func (headerCIDResult EthHeaderCidsConnection) Nodes(ctx context.Context) []*EthHeaderCid {
|
||||
return headerCIDResult.nodes
|
||||
}
|
||||
|
||||
type EthHeaderCidCondition struct {
|
||||
BlockNumber *BigInt
|
||||
BlockHash *string
|
||||
}
|
||||
|
||||
func (r *Resolver) AllEthHeaderCids(ctx context.Context, args struct {
|
||||
Condition *EthHeaderCidCondition
|
||||
}) (*EthHeaderCidsConnection, error) {
|
||||
var headerCIDs []models.HeaderModel
|
||||
var allTxCIDs [][]models.TxModel
|
||||
var err error
|
||||
if args.Condition.BlockHash != nil {
|
||||
headerCID, txCIDs, err := r.backend.Retriever.RetrieveHeaderAndTxCIDsByBlockHash(common.HexToHash(*args.Condition.BlockHash))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
headerCIDs = append(headerCIDs, headerCID)
|
||||
allTxCIDs = append(allTxCIDs, txCIDs)
|
||||
} else if args.Condition.BlockNumber != nil {
|
||||
headerCIDs, allTxCIDs, err = r.backend.Retriever.RetrieveHeaderAndTxCIDsByBlockNumber(args.Condition.BlockNumber.ToInt().Int64())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
return nil, fmt.Errorf("provide block number or block hash")
|
||||
}
|
||||
|
||||
// Begin tx
|
||||
tx, err := r.backend.DB.Beginx()
|
||||
if err != nil {
|
||||
return 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()
|
||||
}
|
||||
}()
|
||||
|
||||
headerIPLDs, err := r.backend.Fetcher.FetchHeaders(tx, headerCIDs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var resultNodes []*EthHeaderCid
|
||||
for idx, headerCID := range headerCIDs {
|
||||
var blockNumber BigInt
|
||||
blockNumber.UnmarshalText([]byte(headerCID.BlockNumber))
|
||||
|
||||
var timestamp BigInt
|
||||
timestamp.SetUint64(headerCID.Timestamp)
|
||||
|
||||
var td BigInt
|
||||
td.UnmarshalText([]byte(headerCID.TotalDifficulty))
|
||||
|
||||
ethHeaderCidNode := EthHeaderCid{
|
||||
cid: headerCID.CID,
|
||||
blockNumber: blockNumber,
|
||||
blockHash: headerCID.BlockHash,
|
||||
parentHash: headerCID.ParentHash,
|
||||
timestamp: timestamp,
|
||||
stateRoot: headerCID.StateRoot,
|
||||
td: td,
|
||||
txRoot: headerCID.TxRoot,
|
||||
receiptRoot: headerCID.RctRoot,
|
||||
uncleRoot: headerCID.UncleRoot,
|
||||
bloom: Bytes(headerCID.Bloom).String(),
|
||||
}
|
||||
|
||||
txCIDs := allTxCIDs[idx]
|
||||
for _, txCID := range txCIDs {
|
||||
ethHeaderCidNode.transactions = append(ethHeaderCidNode.transactions, &EthTransactionCid{
|
||||
cid: txCID.CID,
|
||||
txHash: txCID.TxHash,
|
||||
index: int32(txCID.Index),
|
||||
src: txCID.Src,
|
||||
dst: txCID.Dst,
|
||||
})
|
||||
}
|
||||
|
||||
ethHeaderCidNode.ipfsBlock = IPFSBlock{
|
||||
key: headerIPLDs[idx].Key,
|
||||
data: Bytes(headerIPLDs[idx].Data).String(),
|
||||
}
|
||||
|
||||
resultNodes = append(resultNodes, ðHeaderCidNode)
|
||||
}
|
||||
|
||||
return &EthHeaderCidsConnection{
|
||||
nodes: resultNodes,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (r *Resolver) EthTransactionCidByTxHash(ctx context.Context, args struct {
|
||||
TxHash string
|
||||
}) (*EthTransactionCid, error) {
|
||||
txCID, err := r.backend.Retriever.RetrieveTxCIDByHash(args.TxHash)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Begin tx
|
||||
tx, err := r.backend.DB.Beginx()
|
||||
if err != nil {
|
||||
return 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()
|
||||
}
|
||||
}()
|
||||
|
||||
txIPLDs, err := r.backend.Fetcher.FetchTrxs(tx, []models.TxModel{txCID})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &EthTransactionCid{
|
||||
cid: txCID.CID,
|
||||
txHash: txCID.TxHash,
|
||||
index: int32(txCID.Index),
|
||||
src: txCID.Src,
|
||||
dst: txCID.Dst,
|
||||
ipfsBlock: IPFSBlock{
|
||||
key: txIPLDs[0].Key,
|
||||
data: Bytes(txIPLDs[0].Data).String(),
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
@ -25,8 +25,7 @@ const schema string = `
|
||||
# An empty byte string is represented as '0x'. Byte strings must have an even number of hexadecimal nybbles.
|
||||
scalar Bytes
|
||||
# BigInt is a large integer. Input is accepted as either a JSON number or as a string.
|
||||
# Strings may be either decimal or 0x-prefixed hexadecimal. Output values are all
|
||||
# 0x-prefixed hexadecimal.
|
||||
# Input and output strings may be either decimal or 0x-prefixed hexadecimal depending upon the resolver implementation.
|
||||
scalar BigInt
|
||||
# Long is a 64 bit unsigned integer.
|
||||
scalar Long
|
||||
@ -138,16 +137,16 @@ const schema string = `
|
||||
# empty, results will not be filtered by address.
|
||||
addresses: [Address!]
|
||||
# Topics list restricts matches to particular event topics. Each event has a list
|
||||
# of topics. Topics matches a prefix of that list. An empty element array matches any
|
||||
# topic. Non-empty elements represent an alternative that matches any of the
|
||||
# contained topics.
|
||||
#
|
||||
# Examples:
|
||||
# - [] or nil matches any topic list
|
||||
# - [[A]] matches topic A in first position
|
||||
# - [[], [B]] matches any topic in first position, B in second position
|
||||
# - [[A], [B]] matches topic A in first position, B in second position
|
||||
# - [[A, B]], [C, D]] matches topic (A OR B) in first position, (C OR D) in second position
|
||||
# of topics. Topics matches a prefix of that list. An empty element array matches any
|
||||
# topic. Non-empty elements represent an alternative that matches any of the
|
||||
# contained topics.
|
||||
#
|
||||
# Examples:
|
||||
# - [] or nil matches any topic list
|
||||
# - [[A]] matches topic A in first position
|
||||
# - [[], [B]] matches any topic in first position, B in second position
|
||||
# - [[A], [B]] matches topic A in first position, B in second position
|
||||
# - [[A, B]], [C, D]] matches topic (A OR B) in first position, (C OR D) in second position
|
||||
topics: [[Bytes32!]!]
|
||||
}
|
||||
|
||||
@ -258,16 +257,16 @@ const schema string = `
|
||||
# empty, results will not be filtered by address.
|
||||
addresses: [Address!]
|
||||
# Topics list restricts matches to particular event topics. Each event has a list
|
||||
# of topics. Topics matches a prefix of that list. An empty element array matches any
|
||||
# topic. Non-empty elements represent an alternative that matches any of the
|
||||
# contained topics.
|
||||
#
|
||||
# Examples:
|
||||
# - [] or nil matches any topic list
|
||||
# - [[A]] matches topic A in first position
|
||||
# - [[], [B]] matches any topic in first position, B in second position
|
||||
# - [[A], [B]] matches topic A in first position, B in second position
|
||||
# - [[A, B]], [C, D]] matches topic (A OR B) in first position, (C OR D) in second position
|
||||
# of topics. Topics matches a prefix of that list. An empty element array matches any
|
||||
# topic. Non-empty elements represent an alternative that matches any of the
|
||||
# contained topics.
|
||||
#
|
||||
# Examples:
|
||||
# - [] or nil matches any topic list
|
||||
# - [[A]] matches topic A in first position
|
||||
# - [[], [B]] matches any topic in first position, B in second position
|
||||
# - [[A], [B]] matches topic A in first position, B in second position
|
||||
# - [[A, B]], [C, D]] matches topic (A OR B) in first position, (C OR D) in second position
|
||||
topics: [[Bytes32!]!]
|
||||
}
|
||||
|
||||
@ -282,6 +281,49 @@ const schema string = `
|
||||
ipldBlock: Bytes!
|
||||
}
|
||||
|
||||
input EthHeaderCidCondition {
|
||||
blockNumber: BigInt
|
||||
blockHash: String
|
||||
}
|
||||
|
||||
type EthTransactionCid {
|
||||
cid: String!
|
||||
txHash: String!
|
||||
index: Int!
|
||||
src: String!
|
||||
dst: String!
|
||||
blockByMhKey: IPFSBlock!
|
||||
}
|
||||
|
||||
type EthTransactionCidsConnection {
|
||||
nodes: [EthTransactionCid]!
|
||||
}
|
||||
|
||||
type IPFSBlock {
|
||||
key: String!
|
||||
data: String!
|
||||
}
|
||||
|
||||
type EthHeaderCid {
|
||||
cid: String!
|
||||
blockNumber: BigInt!
|
||||
blockHash: String!
|
||||
parentHash: String!
|
||||
timestamp: BigInt!
|
||||
stateRoot: String!
|
||||
td: BigInt!
|
||||
txRoot: String!
|
||||
receiptRoot: String!
|
||||
uncleRoot: String!
|
||||
bloom: String!
|
||||
ethTransactionCidsByHeaderId: EthTransactionCidsConnection!
|
||||
blockByMhKey: IPFSBlock!
|
||||
}
|
||||
|
||||
type EthHeaderCidsConnection {
|
||||
nodes: [EthHeaderCid]!
|
||||
}
|
||||
|
||||
type Query {
|
||||
# Block fetches an Ethereum block by number or by hash. If neither is
|
||||
# supplied, the most recent known block is returned.
|
||||
@ -302,5 +344,11 @@ const schema string = `
|
||||
|
||||
# Get contract logs by block hash and contract address.
|
||||
getLogs(blockHash: Bytes32!, contract: Address): [Log!]
|
||||
|
||||
# PostGraphile alternative to get headers with transactions using block number or block hash.
|
||||
allEthHeaderCids(condition: EthHeaderCidCondition): EthHeaderCidsConnection
|
||||
|
||||
# PostGraphile alternative to get transactions using transaction hash.
|
||||
ethTransactionCidByTxHash(txHash: String!): EthTransactionCid
|
||||
}
|
||||
`
|
||||
|
122
pkg/graphql/types.go
Normal file
122
pkg/graphql/types.go
Normal file
@ -0,0 +1,122 @@
|
||||
// VulcanizeDB
|
||||
// Copyright © 2022 Vulcanize
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package graphql
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"math/big"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
)
|
||||
|
||||
// Bytes marshals as a JSON string with \x prefix.
|
||||
// The empty slice marshals as "\x".
|
||||
type Bytes []byte
|
||||
|
||||
// MarshalText implements encoding.TextMarshaler
|
||||
func (b Bytes) MarshalText() ([]byte, error) {
|
||||
result := make([]byte, len(b)*2+2)
|
||||
copy(result, `\x`)
|
||||
hex.Encode(result[2:], b)
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// String returns the hex encoding of b.
|
||||
func (b Bytes) String() string {
|
||||
return b.encode()
|
||||
}
|
||||
|
||||
// Encode encodes b as a hex string with "\x" prefix.
|
||||
// This is to make the output to be the same as given by postgraphile.
|
||||
// graphql-go prepends another "\" to the output resulting in prefix "\\x".
|
||||
func (b Bytes) encode() string {
|
||||
result := make([]byte, len(b)*2+2)
|
||||
copy(result, `\x`)
|
||||
hex.Encode(result[2:], b)
|
||||
return string(result)
|
||||
}
|
||||
|
||||
type BigInt big.Int
|
||||
|
||||
// ToInt converts b to a big.Int.
|
||||
func (b *BigInt) ToInt() *big.Int {
|
||||
return (*big.Int)(b)
|
||||
}
|
||||
|
||||
// String returns value of b as a decimal string.
|
||||
func (b *BigInt) String() string {
|
||||
return b.ToInt().String()
|
||||
}
|
||||
|
||||
// SetUint64 sets b to x and returns x.
|
||||
func (b *BigInt) SetUint64(x uint64) *BigInt {
|
||||
var val big.Int
|
||||
val.SetUint64(x)
|
||||
*b = (BigInt)(val)
|
||||
return b
|
||||
}
|
||||
|
||||
// MarshalText implements encoding.TextMarshaler
|
||||
func (b BigInt) MarshalText() ([]byte, error) {
|
||||
return []byte(b.String()), nil
|
||||
}
|
||||
|
||||
// UnmarshalText implements encoding.TextUnmarshaler
|
||||
func (b *BigInt) UnmarshalText(input []byte) error {
|
||||
raw, err := checkNumberText(input)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(raw) > 64 {
|
||||
return hexutil.ErrBig256Range
|
||||
}
|
||||
|
||||
var val big.Int
|
||||
val.SetString(string(input[:]), 10)
|
||||
*b = (BigInt)(val)
|
||||
return nil
|
||||
}
|
||||
|
||||
// ImplementsGraphQLType returns true if BigInt implements the provided GraphQL type.
|
||||
func (b BigInt) ImplementsGraphQLType(name string) bool { return name == "BigInt" }
|
||||
|
||||
// UnmarshalGraphQL unmarshals the provided GraphQL query data.
|
||||
func (b *BigInt) UnmarshalGraphQL(input interface{}) error {
|
||||
var err error
|
||||
switch input := input.(type) {
|
||||
case string:
|
||||
return b.UnmarshalText([]byte(input))
|
||||
case int32:
|
||||
var num big.Int
|
||||
num.SetInt64(int64(input))
|
||||
*b = BigInt(num)
|
||||
default:
|
||||
err = fmt.Errorf("unexpected type %T for BigInt", input)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func checkNumberText(input []byte) (raw []byte, err error) {
|
||||
if len(input) == 0 {
|
||||
return nil, nil // empty strings are allowed
|
||||
}
|
||||
if len(input) > 1 && input[0] == '0' {
|
||||
return nil, hexutil.ErrLeadingZero
|
||||
}
|
||||
return input, nil
|
||||
}
|
@ -18,6 +18,7 @@ package shared
|
||||
|
||||
import (
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/statediff/indexer/models"
|
||||
"github.com/ipfs/go-cid"
|
||||
blockstore "github.com/ipfs/go-ipfs-blockstore"
|
||||
dshelp "github.com/ipfs/go-ipfs-ds-help"
|
||||
@ -55,6 +56,19 @@ func FetchIPLDByMhKeyAndBlockNumber(tx *sqlx.Tx, mhKey string, blockNumber uint6
|
||||
return block, tx.Get(&block, pgStr, mhKey, blockNumber)
|
||||
}
|
||||
|
||||
// FetchIPLDByMhKeysAndBlockNumbers is used to retrieve iplds from Postgres blockstore with the provided tx, mhkey strings and blockNumbers
|
||||
func FetchIPLDsByMhKeysAndBlockNumbers(tx *sqlx.Tx, mhKeys []string, blockNumbers []uint64) ([]models.IPLDModel, error) {
|
||||
var blocks []models.IPLDModel
|
||||
pgStr := `SELECT key, data, block_number FROM public.blocks WHERE key IN (?) AND block_number IN (?)`
|
||||
query, args, err := sqlx.In(pgStr, mhKeys, blockNumbers)
|
||||
if err != nil {
|
||||
return blocks, err
|
||||
}
|
||||
query = tx.Rebind(query)
|
||||
|
||||
return blocks, tx.Select(&blocks, query, args...)
|
||||
}
|
||||
|
||||
// MultihashKeyFromCID converts a cid into a blockstore-prefixed multihash db key string
|
||||
func MultihashKeyFromCID(c cid.Cid) string {
|
||||
dbKey := dshelp.MultihashToDsKey(c.Hash())
|
||||
|
Loading…
Reference in New Issue
Block a user