Skip to content

Commit

Permalink
Merge pull request #635 from onflow/mpeter/implement-storage-provider
Browse files Browse the repository at this point in the history
Integrate & incorporate flow-go's `onchain` package
  • Loading branch information
m-Peter authored Nov 12, 2024
2 parents 63f3d41 + 9b09e88 commit 6781972
Show file tree
Hide file tree
Showing 35 changed files with 1,719 additions and 1,202 deletions.
148 changes: 21 additions & 127 deletions api/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,8 @@ package api

import (
"context"
_ "embed"
"encoding/hex"
"errors"
"fmt"
"math/big"
"strings"

"github.com/onflow/go-ethereum/common"
"github.com/onflow/go-ethereum/common/hexutil"
Expand All @@ -30,6 +26,8 @@ import (
"github.com/onflow/flow-evm-gateway/storage"
)

const BlockGasLimit uint64 = 120_000_000

const maxFeeHistoryBlockCount = 1024

var baseFeesPerGas = big.NewInt(1)
Expand Down Expand Up @@ -76,6 +74,7 @@ var validMethods = map[string]struct{}{
"debug_traceTransaction": {},
"debug_traceBlockByNumber": {},
"debug_traceBlockByHash": {},
"debug_traceCall": {},

// web3 namespace
"web3_clientVersion": {},
Expand Down Expand Up @@ -282,12 +281,12 @@ func (b *BlockChainAPI) GetBalance(
return nil, err
}

evmHeight, err := b.getBlockNumber(&blockNumberOrHash)
height, err := resolveBlockTag(&blockNumberOrHash, b.blocks, b.logger)
if err != nil {
return handleError[*hexutil.Big](err, l, b.collector)
}

balance, err := b.evm.GetBalance(ctx, address, int64(evmHeight))
balance, err := b.evm.GetBalance(address, height)
if err != nil {
return handleError[*hexutil.Big](err, l, b.collector)
}
Expand Down Expand Up @@ -518,12 +517,12 @@ func (b *BlockChainAPI) GetBlockReceipts(
return nil, err
}

evmHeight, err := b.getBlockNumber(&blockNumberOrHash)
height, err := resolveBlockTag(&blockNumberOrHash, b.blocks, b.logger)
if err != nil {
return handleError[[]map[string]interface{}](err, l, b.collector)
}

block, err := b.blocks.GetByHeight(evmHeight)
block, err := b.blocks.GetByHeight(height)
if err != nil {
return handleError[[]map[string]interface{}](err, l, b.collector)
}
Expand Down Expand Up @@ -635,7 +634,7 @@ func (b *BlockChainAPI) Call(
blockNumberOrHash = &latestBlockNumberOrHash
}

evmHeight, err := b.getBlockNumber(blockNumberOrHash)
height, err := resolveBlockTag(blockNumberOrHash, b.blocks, b.logger)
if err != nil {
return handleError[hexutil.Bytes](err, l, b.collector)
}
Expand All @@ -651,7 +650,7 @@ func (b *BlockChainAPI) Call(
from = *args.From
}

res, err := b.evm.Call(ctx, tx, from, int64(evmHeight))
res, err := b.evm.Call(tx, from, height)
if err != nil {
return handleError[hexutil.Bytes](err, l, b.collector)
}
Expand Down Expand Up @@ -753,12 +752,12 @@ func (b *BlockChainAPI) GetTransactionCount(
return nil, err
}

evmHeight, err := b.getBlockNumber(&blockNumberOrHash)
height, err := resolveBlockTag(&blockNumberOrHash, b.blocks, b.logger)
if err != nil {
return handleError[*hexutil.Uint64](err, l, b.collector)
}

networkNonce, err := b.evm.GetNonce(ctx, address, int64(evmHeight))
networkNonce, err := b.evm.GetNonce(address, height)
if err != nil {
return handleError[*hexutil.Uint64](err, l, b.collector)
}
Expand Down Expand Up @@ -806,7 +805,7 @@ func (b *BlockChainAPI) EstimateGas(

tx, err := encodeTxFromArgs(args)
if err != nil {
return hexutil.Uint64(blockGasLimit), nil // return block gas limit
return hexutil.Uint64(BlockGasLimit), nil // return block gas limit
}

// Default address in case user does not provide one
Expand All @@ -819,12 +818,12 @@ func (b *BlockChainAPI) EstimateGas(
blockNumberOrHash = &latestBlockNumberOrHash
}

evmHeight, err := b.getBlockNumber(blockNumberOrHash)
height, err := resolveBlockTag(blockNumberOrHash, b.blocks, b.logger)
if err != nil {
return handleError[hexutil.Uint64](err, l, b.collector)
}

estimatedGas, err := b.evm.EstimateGas(ctx, tx, from, int64(evmHeight))
estimatedGas, err := b.evm.EstimateGas(tx, from, height)
if err != nil {
return handleError[hexutil.Uint64](err, l, b.collector)
}
Expand All @@ -848,12 +847,12 @@ func (b *BlockChainAPI) GetCode(
return nil, err
}

evmHeight, err := b.getBlockNumber(&blockNumberOrHash)
height, err := resolveBlockTag(&blockNumberOrHash, b.blocks, b.logger)
if err != nil {
return handleError[hexutil.Bytes](err, l, b.collector)
}

code, err := b.evm.GetCode(ctx, address, int64(evmHeight))
code, err := b.evm.GetCode(address, height)
if err != nil {
return handleError[hexutil.Bytes](err, l, b.collector)
}
Expand Down Expand Up @@ -934,7 +933,7 @@ func (b *BlockChainAPI) FeeHistory(

rewards = append(rewards, blockRewards)

gasUsedRatio := float64(block.TotalGasUsed) / float64(blockGasLimit)
gasUsedRatio := float64(block.TotalGasUsed) / float64(BlockGasLimit)
gasUsedRatios = append(gasUsedRatios, gasUsedRatio)
}

Expand Down Expand Up @@ -964,7 +963,7 @@ func (b *BlockChainAPI) GetStorageAt(
return nil, err
}

key, _, err := decodeHash(storageSlot)
key, err := decodeHash(storageSlot)
if err != nil {
return handleError[hexutil.Bytes](
fmt.Errorf("%w: %w", errs.ErrInvalid, err),
Expand All @@ -973,12 +972,12 @@ func (b *BlockChainAPI) GetStorageAt(
)
}

evmHeight, err := b.getBlockNumber(&blockNumberOrHash)
height, err := resolveBlockTag(&blockNumberOrHash, b.blocks, b.logger)
if err != nil {
return handleError[hexutil.Bytes](err, l, b.collector)
}

result, err := b.evm.GetStorageAt(ctx, address, key, int64(evmHeight))
result, err := b.evm.GetStorageAt(address, key, height)
if err != nil {
return handleError[hexutil.Bytes](err, l, b.collector)
}
Expand Down Expand Up @@ -1043,7 +1042,7 @@ func (b *BlockChainAPI) prepareBlockResponse(
TransactionsRoot: block.TransactionHashRoot,
Transactions: block.TransactionHashes,
Uncles: []common.Hash{},
GasLimit: hexutil.Uint64(blockGasLimit),
GasLimit: hexutil.Uint64(BlockGasLimit),
Nonce: types.BlockNonce{0x1},
Timestamp: hexutil.Uint64(block.Timestamp),
BaseFeePerGas: hexutil.Big(*baseFeesPerGas),
Expand Down Expand Up @@ -1088,111 +1087,6 @@ func (b *BlockChainAPI) prepareBlockResponse(
return blockResponse, nil
}

func (b *BlockChainAPI) getBlockNumber(blockNumberOrHash *rpc.BlockNumberOrHash) (uint64, error) {
err := fmt.Errorf("%w: neither block number nor hash specified", errs.ErrInvalid)
if blockNumberOrHash == nil {
return 0, err
}
if number, ok := blockNumberOrHash.Number(); ok {
height, err := resolveBlockNumber(number, b.blocks)
if err != nil {
b.logger.Error().Err(err).
Stringer("block_number", number).
Msg("failed to resolve block by hash")
return 0, err
}
return height, nil
}

if hash, ok := blockNumberOrHash.Hash(); ok {
evmHeight, err := b.blocks.GetHeightByID(hash)
if err != nil {
b.logger.Error().Err(err).
Stringer("block_hash", hash).
Msg("failed to get block by hash")
return 0, err
}
return evmHeight, nil
}

return 0, err
}

func resolveBlockNumber(
number rpc.BlockNumber,
blocksDB storage.BlockIndexer,
) (uint64, error) {
height := number.Int64()

// if special values (latest) we return latest executed height
//
// all the special values are:
// SafeBlockNumber = BlockNumber(-4)
// FinalizedBlockNumber = BlockNumber(-3)
// LatestBlockNumber = BlockNumber(-2)
// PendingBlockNumber = BlockNumber(-1)
//
// EVM on Flow does not have these concepts, but the latest block is the closest fit
if height < 0 {
executed, err := blocksDB.LatestEVMHeight()
if err != nil {
return 0, err
}
height = int64(executed)
}

return uint64(height), nil
}

// handleError takes in an error and in case the error is of type ErrEntityNotFound
// it returns nil instead of an error since that is according to the API spec,
// if the error is not of type ErrEntityNotFound it will return the error and the generic
// empty type.
func handleError[T any](err error, log zerolog.Logger, collector metrics.Collector) (T, error) {
var (
zero T
revertedErr *errs.RevertError
)

switch {
// as per specification returning nil and nil for not found resources
case errors.Is(err, errs.ErrEntityNotFound):
return zero, nil
case errors.Is(err, errs.ErrInvalid):
return zero, err
case errors.Is(err, errs.ErrFailedTransaction):
return zero, err
case errors.As(err, &revertedErr):
return zero, revertedErr
default:
collector.ApiErrorOccurred()
log.Error().Err(err).Msg("api error")
return zero, errs.ErrInternal
}
}

// decodeHash parses a hex-encoded 32-byte hash. The input may optionally
// be prefixed by 0x and can have a byte length up to 32.
func decodeHash(s string) (h common.Hash, inputLength int, err error) {
if strings.HasPrefix(s, "0x") || strings.HasPrefix(s, "0X") {
s = s[2:]
}
if (len(s) & 1) > 0 {
s = "0" + s
}
b, err := hex.DecodeString(s)
if err != nil {
return common.Hash{}, 0, fmt.Errorf("invalid hex string: %s", s)
}
if len(b) > common.HashLength {
return common.Hash{}, len(b), fmt.Errorf(
"hex string too long, want at most 32 bytes, have %d bytes",
len(b),
)
}
return common.BytesToHash(b), len(b), nil
}

/*
Static responses section
Expand Down
Loading

0 comments on commit 6781972

Please sign in to comment.