graphql: embed *Resolver instead of backend interface (#25468)

This creates some infrastructure to share resources between graphql
API objects.
This commit is contained in:
Felix Lange 2022-08-03 19:08:32 +02:00 committed by GitHub
parent 948e08d55b
commit f809cf6ea6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 66 additions and 70 deletions

@ -76,14 +76,14 @@ func (b *Long) UnmarshalGraphQL(input interface{}) error {
// Account represents an Ethereum account at a particular block. // Account represents an Ethereum account at a particular block.
type Account struct { type Account struct {
backend ethapi.Backend r *Resolver
address common.Address address common.Address
blockNrOrHash rpc.BlockNumberOrHash blockNrOrHash rpc.BlockNumberOrHash
} }
// getState fetches the StateDB object for an account. // getState fetches the StateDB object for an account.
func (a *Account) getState(ctx context.Context) (*state.StateDB, error) { func (a *Account) getState(ctx context.Context) (*state.StateDB, error) {
state, _, err := a.backend.StateAndHeaderByNumberOrHash(ctx, a.blockNrOrHash) state, _, err := a.r.backend.StateAndHeaderByNumberOrHash(ctx, a.blockNrOrHash)
return state, err return state, err
} }
@ -106,7 +106,7 @@ func (a *Account) Balance(ctx context.Context) (hexutil.Big, error) {
func (a *Account) TransactionCount(ctx context.Context) (hexutil.Uint64, error) { func (a *Account) TransactionCount(ctx context.Context) (hexutil.Uint64, error) {
// Ask transaction pool for the nonce which includes pending transactions // Ask transaction pool for the nonce which includes pending transactions
if blockNr, ok := a.blockNrOrHash.Number(); ok && blockNr == rpc.PendingBlockNumber { if blockNr, ok := a.blockNrOrHash.Number(); ok && blockNr == rpc.PendingBlockNumber {
nonce, err := a.backend.GetPoolNonce(ctx, a.address) nonce, err := a.r.backend.GetPoolNonce(ctx, a.address)
if err != nil { if err != nil {
return 0, err return 0, err
} }
@ -137,7 +137,7 @@ func (a *Account) Storage(ctx context.Context, args struct{ Slot common.Hash })
// Log represents an individual log message. All arguments are mandatory. // Log represents an individual log message. All arguments are mandatory.
type Log struct { type Log struct {
backend ethapi.Backend r *Resolver
transaction *Transaction transaction *Transaction
log *types.Log log *types.Log
} }
@ -148,7 +148,7 @@ func (l *Log) Transaction(ctx context.Context) *Transaction {
func (l *Log) Account(ctx context.Context, args BlockNumberArgs) *Account { func (l *Log) Account(ctx context.Context, args BlockNumberArgs) *Account {
return &Account{ return &Account{
backend: l.backend, r: l.r,
address: l.log.Address, address: l.log.Address,
blockNrOrHash: args.NumberOrLatest(), blockNrOrHash: args.NumberOrLatest(),
} }
@ -183,30 +183,30 @@ func (at *AccessTuple) StorageKeys(ctx context.Context) []common.Hash {
// Transaction represents an Ethereum transaction. // Transaction represents an Ethereum transaction.
// backend and hash are mandatory; all others will be fetched when required. // backend and hash are mandatory; all others will be fetched when required.
type Transaction struct { type Transaction struct {
backend ethapi.Backend r *Resolver
hash common.Hash hash common.Hash
tx *types.Transaction tx *types.Transaction
block *Block block *Block
index uint64 index uint64
} }
// resolve returns the internal transaction object, fetching it if needed. // resolve returns the internal transaction object, fetching it if needed.
func (t *Transaction) resolve(ctx context.Context) (*types.Transaction, error) { func (t *Transaction) resolve(ctx context.Context) (*types.Transaction, error) {
if t.tx == nil { if t.tx == nil {
// Try to return an already finalized transaction // Try to return an already finalized transaction
tx, blockHash, _, index, err := t.backend.GetTransaction(ctx, t.hash) tx, blockHash, _, index, err := t.r.backend.GetTransaction(ctx, t.hash)
if err == nil && tx != nil { if err == nil && tx != nil {
t.tx = tx t.tx = tx
blockNrOrHash := rpc.BlockNumberOrHashWithHash(blockHash, false) blockNrOrHash := rpc.BlockNumberOrHashWithHash(blockHash, false)
t.block = &Block{ t.block = &Block{
backend: t.backend, r: t.r,
numberOrHash: &blockNrOrHash, numberOrHash: &blockNrOrHash,
} }
t.index = index t.index = index
return t.tx, nil return t.tx, nil
} }
// No finalized transaction, try to retrieve it from the pool // No finalized transaction, try to retrieve it from the pool
t.tx = t.backend.GetPoolTransaction(t.hash) t.tx = t.r.backend.GetPoolTransaction(t.hash)
} }
return t.tx, nil return t.tx, nil
} }
@ -354,7 +354,7 @@ func (t *Transaction) To(ctx context.Context, args BlockNumberArgs) (*Account, e
return nil, nil return nil, nil
} }
return &Account{ return &Account{
backend: t.backend, r: t.r,
address: *to, address: *to,
blockNrOrHash: args.NumberOrLatest(), blockNrOrHash: args.NumberOrLatest(),
}, nil }, nil
@ -365,10 +365,10 @@ func (t *Transaction) From(ctx context.Context, args BlockNumberArgs) (*Account,
if err != nil || tx == nil { if err != nil || tx == nil {
return nil, err return nil, err
} }
signer := types.LatestSigner(t.backend.ChainConfig()) signer := types.LatestSigner(t.r.backend.ChainConfig())
from, _ := types.Sender(signer, tx) from, _ := types.Sender(signer, tx)
return &Account{ return &Account{
backend: t.backend, r: t.r,
address: from, address: from,
blockNrOrHash: args.NumberOrLatest(), blockNrOrHash: args.NumberOrLatest(),
}, nil }, nil
@ -443,7 +443,7 @@ func (t *Transaction) CreatedContract(ctx context.Context, args BlockNumberArgs)
return nil, err return nil, err
} }
return &Account{ return &Account{
backend: t.backend, r: t.r,
address: receipt.ContractAddress, address: receipt.ContractAddress,
blockNrOrHash: args.NumberOrLatest(), blockNrOrHash: args.NumberOrLatest(),
}, nil }, nil
@ -457,7 +457,7 @@ func (t *Transaction) Logs(ctx context.Context) (*[]*Log, error) {
ret := make([]*Log, 0, len(receipt.Logs)) ret := make([]*Log, 0, len(receipt.Logs))
for _, log := range receipt.Logs { for _, log := range receipt.Logs {
ret = append(ret, &Log{ ret = append(ret, &Log{
backend: t.backend, r: t.r,
transaction: t, transaction: t,
log: log, log: log,
}) })
@ -539,7 +539,7 @@ type BlockType int
// backend, and numberOrHash are mandatory. All other fields are lazily fetched // backend, and numberOrHash are mandatory. All other fields are lazily fetched
// when required. // when required.
type Block struct { type Block struct {
backend ethapi.Backend r *Resolver
numberOrHash *rpc.BlockNumberOrHash numberOrHash *rpc.BlockNumberOrHash
hash common.Hash hash common.Hash
header *types.Header header *types.Header
@ -558,7 +558,7 @@ func (b *Block) resolve(ctx context.Context) (*types.Block, error) {
b.numberOrHash = &latest b.numberOrHash = &latest
} }
var err error var err error
b.block, err = b.backend.BlockByNumberOrHash(ctx, *b.numberOrHash) b.block, err = b.r.backend.BlockByNumberOrHash(ctx, *b.numberOrHash)
if b.block != nil && b.header == nil { if b.block != nil && b.header == nil {
b.header = b.block.Header() b.header = b.block.Header()
if hash, ok := b.numberOrHash.Hash(); ok { if hash, ok := b.numberOrHash.Hash(); ok {
@ -578,9 +578,9 @@ func (b *Block) resolveHeader(ctx context.Context) (*types.Header, error) {
var err error var err error
if b.header == nil { if b.header == nil {
if b.hash != (common.Hash{}) { if b.hash != (common.Hash{}) {
b.header, err = b.backend.HeaderByHash(ctx, b.hash) b.header, err = b.r.backend.HeaderByHash(ctx, b.hash)
} else { } else {
b.header, err = b.backend.HeaderByNumberOrHash(ctx, *b.numberOrHash) b.header, err = b.r.backend.HeaderByNumberOrHash(ctx, *b.numberOrHash)
} }
} }
return b.header, err return b.header, err
@ -598,7 +598,7 @@ func (b *Block) resolveReceipts(ctx context.Context) ([]*types.Receipt, error) {
} }
hash = header.Hash() hash = header.Hash()
} }
receipts, err := b.backend.GetReceipts(ctx, hash) receipts, err := b.r.backend.GetReceipts(ctx, hash)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -659,7 +659,7 @@ func (b *Block) NextBaseFeePerGas(ctx context.Context) (*hexutil.Big, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
chaincfg := b.backend.ChainConfig() chaincfg := b.r.backend.ChainConfig()
if header.BaseFee == nil { if header.BaseFee == nil {
// Make sure next block doesn't enable EIP-1559 // Make sure next block doesn't enable EIP-1559
if !chaincfg.IsLondon(new(big.Int).Add(header.Number, common.Big1)) { if !chaincfg.IsLondon(new(big.Int).Add(header.Number, common.Big1)) {
@ -679,7 +679,7 @@ func (b *Block) Parent(ctx context.Context) (*Block, error) {
} }
num := rpc.BlockNumberOrHashWithNumber(rpc.BlockNumber(b.header.Number.Uint64() - 1)) num := rpc.BlockNumberOrHashWithNumber(rpc.BlockNumber(b.header.Number.Uint64() - 1))
return &Block{ return &Block{
backend: b.backend, r: b.r,
numberOrHash: &num, numberOrHash: &num,
hash: b.header.ParentHash, hash: b.header.ParentHash,
}, nil }, nil
@ -767,7 +767,7 @@ func (b *Block) Ommers(ctx context.Context) (*[]*Block, error) {
for _, uncle := range block.Uncles() { for _, uncle := range block.Uncles() {
blockNumberOrHash := rpc.BlockNumberOrHashWithHash(uncle.Hash(), false) blockNumberOrHash := rpc.BlockNumberOrHashWithHash(uncle.Hash(), false)
ret = append(ret, &Block{ ret = append(ret, &Block{
backend: b.backend, r: b.r,
numberOrHash: &blockNumberOrHash, numberOrHash: &blockNumberOrHash,
header: uncle, header: uncle,
}) })
@ -800,7 +800,7 @@ func (b *Block) TotalDifficulty(ctx context.Context) (hexutil.Big, error) {
} }
h = header.Hash() h = header.Hash()
} }
td := b.backend.GetTd(ctx, h) td := b.r.backend.GetTd(ctx, h)
if td == nil { if td == nil {
return hexutil.Big{}, fmt.Errorf("total difficulty not found %x", b.hash) return hexutil.Big{}, fmt.Errorf("total difficulty not found %x", b.hash)
} }
@ -853,7 +853,7 @@ func (b *Block) Miner(ctx context.Context, args BlockNumberArgs) (*Account, erro
return nil, err return nil, err
} }
return &Account{ return &Account{
backend: b.backend, r: b.r,
address: header.Coinbase, address: header.Coinbase,
blockNrOrHash: args.NumberOrLatest(), blockNrOrHash: args.NumberOrLatest(),
}, nil }, nil
@ -876,11 +876,11 @@ func (b *Block) Transactions(ctx context.Context) (*[]*Transaction, error) {
ret := make([]*Transaction, 0, len(block.Transactions())) ret := make([]*Transaction, 0, len(block.Transactions()))
for i, tx := range block.Transactions() { for i, tx := range block.Transactions() {
ret = append(ret, &Transaction{ ret = append(ret, &Transaction{
backend: b.backend, r: b.r,
hash: tx.Hash(), hash: tx.Hash(),
tx: tx, tx: tx,
block: b, block: b,
index: uint64(i), index: uint64(i),
}) })
} }
return &ret, nil return &ret, nil
@ -897,11 +897,11 @@ func (b *Block) TransactionAt(ctx context.Context, args struct{ Index int32 }) (
} }
tx := txs[args.Index] tx := txs[args.Index]
return &Transaction{ return &Transaction{
backend: b.backend, r: b.r,
hash: tx.Hash(), hash: tx.Hash(),
tx: tx, tx: tx,
block: b, block: b,
index: uint64(args.Index), index: uint64(args.Index),
}, nil }, nil
} }
@ -917,7 +917,7 @@ func (b *Block) OmmerAt(ctx context.Context, args struct{ Index int32 }) (*Block
uncle := uncles[args.Index] uncle := uncles[args.Index]
blockNumberOrHash := rpc.BlockNumberOrHashWithHash(uncle.Hash(), false) blockNumberOrHash := rpc.BlockNumberOrHashWithHash(uncle.Hash(), false)
return &Block{ return &Block{
backend: b.backend, r: b.r,
numberOrHash: &blockNumberOrHash, numberOrHash: &blockNumberOrHash,
header: uncle, header: uncle,
}, nil }, nil
@ -944,7 +944,7 @@ type BlockFilterCriteria struct {
// runFilter accepts a filter and executes it, returning all its results as // runFilter accepts a filter and executes it, returning all its results as
// `Log` objects. // `Log` objects.
func runFilter(ctx context.Context, be ethapi.Backend, filter *filters.Filter) ([]*Log, error) { func runFilter(ctx context.Context, r *Resolver, filter *filters.Filter) ([]*Log, error) {
logs, err := filter.Logs(ctx) logs, err := filter.Logs(ctx)
if err != nil || logs == nil { if err != nil || logs == nil {
return nil, err return nil, err
@ -952,8 +952,8 @@ func runFilter(ctx context.Context, be ethapi.Backend, filter *filters.Filter) (
ret := make([]*Log, 0, len(logs)) ret := make([]*Log, 0, len(logs))
for _, log := range logs { for _, log := range logs {
ret = append(ret, &Log{ ret = append(ret, &Log{
backend: be, r: r,
transaction: &Transaction{backend: be, hash: log.TxHash}, transaction: &Transaction{r: r, hash: log.TxHash},
log: log, log: log,
}) })
} }
@ -978,10 +978,10 @@ func (b *Block) Logs(ctx context.Context, args struct{ Filter BlockFilterCriteri
hash = header.Hash() hash = header.Hash()
} }
// Construct the range filter // Construct the range filter
filter := filters.NewBlockFilter(b.backend, hash, addresses, topics) filter := filters.NewBlockFilter(b.r.backend, hash, addresses, topics)
// Run the filter and return all the logs // Run the filter and return all the logs
return runFilter(ctx, b.backend, filter) return runFilter(ctx, b.r, filter)
} }
func (b *Block) Account(ctx context.Context, args struct { func (b *Block) Account(ctx context.Context, args struct {
@ -994,7 +994,7 @@ func (b *Block) Account(ctx context.Context, args struct {
} }
} }
return &Account{ return &Account{
backend: b.backend, r: b.r,
address: args.Address, address: args.Address,
blockNrOrHash: *b.numberOrHash, blockNrOrHash: *b.numberOrHash,
}, nil }, nil
@ -1041,7 +1041,7 @@ func (b *Block) Call(ctx context.Context, args struct {
return nil, err return nil, err
} }
} }
result, err := ethapi.DoCall(ctx, b.backend, args.Data, *b.numberOrHash, nil, b.backend.RPCEVMTimeout(), b.backend.RPCGasCap()) result, err := ethapi.DoCall(ctx, b.r.backend, args.Data, *b.numberOrHash, nil, b.r.backend.RPCEVMTimeout(), b.r.backend.RPCGasCap())
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -1066,31 +1066,31 @@ func (b *Block) EstimateGas(ctx context.Context, args struct {
return 0, err return 0, err
} }
} }
gas, err := ethapi.DoEstimateGas(ctx, b.backend, args.Data, *b.numberOrHash, b.backend.RPCGasCap()) gas, err := ethapi.DoEstimateGas(ctx, b.r.backend, args.Data, *b.numberOrHash, b.r.backend.RPCGasCap())
return Long(gas), err return Long(gas), err
} }
type Pending struct { type Pending struct {
backend ethapi.Backend r *Resolver
} }
func (p *Pending) TransactionCount(ctx context.Context) (int32, error) { func (p *Pending) TransactionCount(ctx context.Context) (int32, error) {
txs, err := p.backend.GetPoolTransactions() txs, err := p.r.backend.GetPoolTransactions()
return int32(len(txs)), err return int32(len(txs)), err
} }
func (p *Pending) Transactions(ctx context.Context) (*[]*Transaction, error) { func (p *Pending) Transactions(ctx context.Context) (*[]*Transaction, error) {
txs, err := p.backend.GetPoolTransactions() txs, err := p.r.backend.GetPoolTransactions()
if err != nil { if err != nil {
return nil, err return nil, err
} }
ret := make([]*Transaction, 0, len(txs)) ret := make([]*Transaction, 0, len(txs))
for i, tx := range txs { for i, tx := range txs {
ret = append(ret, &Transaction{ ret = append(ret, &Transaction{
backend: p.backend, r: p.r,
hash: tx.Hash(), hash: tx.Hash(),
tx: tx, tx: tx,
index: uint64(i), index: uint64(i),
}) })
} }
return &ret, nil return &ret, nil
@ -1101,7 +1101,7 @@ func (p *Pending) Account(ctx context.Context, args struct {
}) *Account { }) *Account {
pendingBlockNr := rpc.BlockNumberOrHashWithNumber(rpc.PendingBlockNumber) pendingBlockNr := rpc.BlockNumberOrHashWithNumber(rpc.PendingBlockNumber)
return &Account{ return &Account{
backend: p.backend, r: p.r,
address: args.Address, address: args.Address,
blockNrOrHash: pendingBlockNr, blockNrOrHash: pendingBlockNr,
} }
@ -1111,7 +1111,7 @@ func (p *Pending) Call(ctx context.Context, args struct {
Data ethapi.TransactionArgs Data ethapi.TransactionArgs
}) (*CallResult, error) { }) (*CallResult, error) {
pendingBlockNr := rpc.BlockNumberOrHashWithNumber(rpc.PendingBlockNumber) pendingBlockNr := rpc.BlockNumberOrHashWithNumber(rpc.PendingBlockNumber)
result, err := ethapi.DoCall(ctx, p.backend, args.Data, pendingBlockNr, nil, p.backend.RPCEVMTimeout(), p.backend.RPCGasCap()) result, err := ethapi.DoCall(ctx, p.r.backend, args.Data, pendingBlockNr, nil, p.r.backend.RPCEVMTimeout(), p.r.backend.RPCGasCap())
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -1131,7 +1131,7 @@ func (p *Pending) EstimateGas(ctx context.Context, args struct {
Data ethapi.TransactionArgs Data ethapi.TransactionArgs
}) (Long, error) { }) (Long, error) {
pendingBlockNr := rpc.BlockNumberOrHashWithNumber(rpc.PendingBlockNumber) pendingBlockNr := rpc.BlockNumberOrHashWithNumber(rpc.PendingBlockNumber)
gas, err := ethapi.DoEstimateGas(ctx, p.backend, args.Data, pendingBlockNr, p.backend.RPCGasCap()) gas, err := ethapi.DoEstimateGas(ctx, p.r.backend, args.Data, pendingBlockNr, p.r.backend.RPCGasCap())
return Long(gas), err return Long(gas), err
} }
@ -1152,19 +1152,19 @@ func (r *Resolver) Block(ctx context.Context, args struct {
number := rpc.BlockNumber(*args.Number) number := rpc.BlockNumber(*args.Number)
numberOrHash := rpc.BlockNumberOrHashWithNumber(number) numberOrHash := rpc.BlockNumberOrHashWithNumber(number)
block = &Block{ block = &Block{
backend: r.backend, r: r,
numberOrHash: &numberOrHash, numberOrHash: &numberOrHash,
} }
} else if args.Hash != nil { } else if args.Hash != nil {
numberOrHash := rpc.BlockNumberOrHashWithHash(*args.Hash, false) numberOrHash := rpc.BlockNumberOrHashWithHash(*args.Hash, false)
block = &Block{ block = &Block{
backend: r.backend, r: r,
numberOrHash: &numberOrHash, numberOrHash: &numberOrHash,
} }
} else { } else {
numberOrHash := rpc.BlockNumberOrHashWithNumber(rpc.LatestBlockNumber) numberOrHash := rpc.BlockNumberOrHashWithNumber(rpc.LatestBlockNumber)
block = &Block{ block = &Block{
backend: r.backend, r: r,
numberOrHash: &numberOrHash, numberOrHash: &numberOrHash,
} }
} }
@ -1199,7 +1199,7 @@ func (r *Resolver) Blocks(ctx context.Context, args struct {
for i := from; i <= to; i++ { for i := from; i <= to; i++ {
numberOrHash := rpc.BlockNumberOrHashWithNumber(i) numberOrHash := rpc.BlockNumberOrHashWithNumber(i)
block := &Block{ block := &Block{
backend: r.backend, r: r,
numberOrHash: &numberOrHash, numberOrHash: &numberOrHash,
} }
// Resolve the header to check for existence. // Resolve the header to check for existence.
@ -1218,13 +1218,13 @@ func (r *Resolver) Blocks(ctx context.Context, args struct {
} }
func (r *Resolver) Pending(ctx context.Context) *Pending { func (r *Resolver) Pending(ctx context.Context) *Pending {
return &Pending{r.backend} return &Pending{r}
} }
func (r *Resolver) Transaction(ctx context.Context, args struct{ Hash common.Hash }) (*Transaction, error) { func (r *Resolver) Transaction(ctx context.Context, args struct{ Hash common.Hash }) (*Transaction, error) {
tx := &Transaction{ tx := &Transaction{
backend: r.backend, r: r,
hash: args.Hash, hash: args.Hash,
} }
// Resolve the transaction; if it doesn't exist, return nil. // Resolve the transaction; if it doesn't exist, return nil.
t, err := tx.resolve(ctx) t, err := tx.resolve(ctx)
@ -1284,8 +1284,8 @@ func (r *Resolver) Logs(ctx context.Context, args struct{ Filter FilterCriteria
topics = *args.Filter.Topics topics = *args.Filter.Topics
} }
// Construct the range filter // Construct the range filter
filter := filters.NewRangeFilter(filters.Backend(r.backend), begin, end, addresses, topics) filter := filters.NewRangeFilter(r.backend, begin, end, addresses, topics)
return runFilter(ctx, r.backend, filter) return runFilter(ctx, r, filter)
} }
func (r *Resolver) GasPrice(ctx context.Context) (hexutil.Big, error) { func (r *Resolver) GasPrice(ctx context.Context) (hexutil.Big, error) {

@ -56,10 +56,6 @@ func (h handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
// New constructs a new GraphQL service instance. // New constructs a new GraphQL service instance.
func New(stack *node.Node, backend ethapi.Backend, cors, vhosts []string) error { func New(stack *node.Node, backend ethapi.Backend, cors, vhosts []string) error {
if backend == nil {
panic("missing backend")
}
// check if http server with given endpoint exists and enable graphQL on it
return newHandler(stack, backend, cors, vhosts) return newHandler(stack, backend, cors, vhosts)
} }