Skip to content

Commit

Permalink
Merge pull request #363 from onflow/gregor/remote-ledger-storage
Browse files Browse the repository at this point in the history
Get storage at API
  • Loading branch information
sideninja authored Jul 18, 2024
2 parents 0880eaa + dd16b2f commit e648106
Show file tree
Hide file tree
Showing 4 changed files with 121 additions and 39 deletions.
108 changes: 74 additions & 34 deletions api/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@ package api
import (
"context"
_ "embed"
"encoding/hex"
"errors"
"fmt"
"math/big"
"strings"

evmTypes "github.com/onflow/flow-go/fvm/evm/types"
"github.com/onflow/go-ethereum/common"
Expand Down Expand Up @@ -690,28 +692,6 @@ func (b *BlockChainAPI) GetCode(
return code, nil
}

// handleError takes in an error and in case the error is of type ErrNotFound
// it returns nil instead of an error since that is according to the API spec,
// if the error is not of type ErrNotFound it will return the error and the generic
// empty type.
func handleError[T any](log zerolog.Logger, err error) (T, error) {
var zero T
switch {
// as per specification returning nil and nil for not found resources
case errors.Is(err, storageErrs.ErrNotFound):
return zero, nil
case errors.Is(err, storageErrs.ErrInvalidRange):
return zero, err
case errors.Is(err, requester.ErrOutOfRange):
return zero, fmt.Errorf("requested height is out of supported range")
case errors.Is(err, errs.ErrInvalid):
return zero, err
default:
log.Error().Err(err).Msg("api error")
return zero, errs.ErrInternal
}
}

func (b *BlockChainAPI) fetchBlockTransactions(
ctx context.Context,
block *evmTypes.Block,
Expand Down Expand Up @@ -896,6 +876,78 @@ func (b *BlockChainAPI) FeeHistory(
}, nil
}

// GetStorageAt returns the storage from the state at the given address, key and
// block number. The rpc.LatestBlockNumber and rpc.PendingBlockNumber meta block
// numbers are also allowed.
func (b *BlockChainAPI) GetStorageAt(
ctx context.Context,
address common.Address,
storageSlot string,
blockNumberOrHash *rpc.BlockNumberOrHash,
) (hexutil.Bytes, error) {
if err := rateLimit(ctx, b.limiter, b.logger); err != nil {
return nil, err
}

key, _, err := decodeHash(storageSlot)
if err != nil {
return handleError[hexutil.Bytes](b.logger, errors.Join(errs.ErrInvalid, err))
}

evmHeight, err := b.getBlockNumber(blockNumberOrHash)
if err != nil {
return handleError[hexutil.Bytes](b.logger, err)
}

result, err := b.evm.GetStorageAt(ctx, address, key, evmHeight)
if err != nil {
return handleError[hexutil.Bytes](b.logger, err)
}

return result[:], nil
}

// handleError takes in an error and in case the error is of type ErrNotFound
// it returns nil instead of an error since that is according to the API spec,
// if the error is not of type ErrNotFound it will return the error and the generic
// empty type.
func handleError[T any](log zerolog.Logger, err error) (T, error) {
var zero T
switch {
// as per specification returning nil and nil for not found resources
case errors.Is(err, storageErrs.ErrNotFound):
return zero, nil
case errors.Is(err, storageErrs.ErrInvalidRange):
return zero, err
case errors.Is(err, requester.ErrOutOfRange):
return zero, fmt.Errorf("requested height is out of supported range")
case errors.Is(err, errs.ErrInvalid):
return zero, err
default:
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, errors.New("hex string invalid")
}
if len(b) > 32 {
return common.Hash{}, len(b), errors.New("hex string too long, want at most 32 bytes")
}
return common.BytesToHash(b), len(b), nil
}

/*
Static responses section
Expand Down Expand Up @@ -978,18 +1030,6 @@ func (b *BlockChainAPI) GetProof(
return nil, errs.ErrNotSupported
}

// GetStorageAt returns the storage from the state at the given address, key and
// block number. The rpc.LatestBlockNumber and rpc.PendingBlockNumber meta block
// numbers are also allowed.
func (b *BlockChainAPI) GetStorageAt(
ctx context.Context,
address common.Address,
storageSlot string,
blockNumberOrHash *rpc.BlockNumberOrHash,
) (hexutil.Bytes, error) {
return nil, errs.ErrNotSupported
}

// CreateAccessList creates an EIP-2930 type AccessList for the given transaction.
// Reexec and blockNumberOrHash can be specified to create the accessList on top of a certain state.
func (b *BlockChainAPI) CreateAccessList(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package emulator
package requester

import (
"context"
Expand All @@ -15,7 +15,7 @@ import (
"google.golang.org/grpc/status"
)

var previewnetStorage = flow.HexToAddress("0x4f6fd534ddd3fc5f")
var previewnetStorageAddress = flow.HexToAddress("0x4f6fd534ddd3fc5f")

var _ atree.Ledger = &remoteLedger{}

Expand Down Expand Up @@ -64,6 +64,7 @@ func (l *remoteLedger) GetValue(owner, key []byte) ([]byte, error) {
}

if response != nil && len(response.Values) > 0 {
// we only request one register so 0 index
return response.Values[0], nil
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package emulator
package requester

import (
"context"
Expand All @@ -24,7 +24,7 @@ func Test_E2E_Previewnet_RemoteLedger(t *testing.T) {
require.NoError(t, err)
testAddress := types.NewAddressFromBytes(addrBytes).ToCommon()

stateDB, err := state.NewStateDB(ledger, previewnetStorage)
stateDB, err := state.NewStateDB(ledger, previewnetStorageAddress)
require.NoError(t, err)

assert.NotEmpty(t, stateDB.GetCode(testAddress))
Expand All @@ -51,7 +51,7 @@ func Benchmark_RemoteLedger_GetBalance(b *testing.B) {
ledger, err := newRemoteLedger(previewnetHost, cadenceHeight)
require.NoError(b, err)

stateDB, err := state.NewStateDB(ledger, previewnetStorage)
stateDB, err := state.NewStateDB(ledger, previewnetStorageAddress)
require.NoError(b, err)

addrBytes, err := hex.DecodeString("BC9985a24c0846cbEdd6249868020A84Df83Ea85")
Expand Down
41 changes: 41 additions & 0 deletions services/requester/requester.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (
"github.com/onflow/flow-go-sdk"
"github.com/onflow/flow-go-sdk/crypto"
"github.com/onflow/flow-go/fvm/evm/emulator"
"github.com/onflow/flow-go/fvm/evm/emulator/state"
"github.com/onflow/flow-go/fvm/evm/stdlib"
evmTypes "github.com/onflow/flow-go/fvm/evm/types"
"github.com/onflow/flow-go/fvm/systemcontracts"
Expand Down Expand Up @@ -86,6 +87,9 @@ type Requester interface {

// GetLatestEVMHeight returns the latest EVM height of the network.
GetLatestEVMHeight(ctx context.Context) (uint64, error)

// GetStorageAt returns the storage from the state at the given address, key and block number.
GetStorageAt(ctx context.Context, address common.Address, hash common.Hash, evmHeight int64) (common.Hash, error)
}

var _ Requester = &EVM{}
Expand Down Expand Up @@ -370,6 +374,43 @@ func (e *EVM) GetNonce(
return nonce, nil
}

func (e *EVM) stateAt(evmHeight int64) (*state.StateDB, error) {
cadenceHeight, err := e.evmToCadenceHeight(evmHeight)
if err != nil {
return nil, err
}

if cadenceHeight == LatestBlockHeight {
h, err := e.client.GetLatestBlockHeader(context.Background(), true)
if err != nil {
return nil, err
}
cadenceHeight = h.Height
}

ledger, err := newRemoteLedger(e.config.AccessNodeHost, cadenceHeight)
if err != nil {
return nil, fmt.Errorf("could not create a remote ledger: %w", err)
}

return state.NewStateDB(ledger, previewnetStorageAddress)
}

func (e *EVM) GetStorageAt(
ctx context.Context,
address common.Address,
hash common.Hash,
evmHeight int64,
) (common.Hash, error) {
stateDB, err := e.stateAt(evmHeight)
if err != nil {
return common.Hash{}, err
}

result := stateDB.GetState(address, hash)
return result, stateDB.Error()
}

func (e *EVM) Call(
ctx context.Context,
data []byte,
Expand Down

0 comments on commit e648106

Please sign in to comment.