diff --git a/api/api.go b/api/api.go index 36fe48222..e2080db2d 100644 --- a/api/api.go +++ b/api/api.go @@ -19,14 +19,13 @@ import ( "github.com/rs/zerolog" "github.com/sethvargo/go-limiter" - errs "github.com/onflow/flow-evm-gateway/api/errors" "github.com/onflow/flow-evm-gateway/config" "github.com/onflow/flow-evm-gateway/metrics" "github.com/onflow/flow-evm-gateway/models" + errs "github.com/onflow/flow-evm-gateway/models/errors" "github.com/onflow/flow-evm-gateway/services/logs" "github.com/onflow/flow-evm-gateway/services/requester" "github.com/onflow/flow-evm-gateway/storage" - storageErrs "github.com/onflow/flow-evm-gateway/storage/errors" ) const maxFeeHistoryBlockCount = 1024 @@ -1017,27 +1016,20 @@ func (b *BlockChainAPI) getBlockNumber(blockNumberOrHash *rpc.BlockNumberOrHash) // empty type. func handleError[T any](err error, log zerolog.Logger, collector metrics.Collector) (T, error) { var ( - zero T - errGasPriceTooLow *errs.GasPriceTooLowError - revertError *errs.RevertError + zero T + revertedErr *errs.RevertError ) switch { // as per specification returning nil and nil for not found resources - case errors.Is(err, storageErrs.ErrNotFound): + case errors.Is(err, errs.ErrNotFound): return zero, nil - case errors.As(err, &revertError): - return zero, revertError - case errors.Is(err, storageErrs.ErrInvalidRange): - return zero, err - case errors.Is(err, models.ErrInvalidEVMTransaction): - return zero, err - case errors.Is(err, requester.ErrOutOfRange): - return zero, err case errors.Is(err, errs.ErrInvalid): return zero, err - case errors.As(err, &errGasPriceTooLow): - return zero, errGasPriceTooLow + 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") diff --git a/api/encode_transaction.go b/api/encode_transaction.go index bcf4024fe..4cb071a90 100644 --- a/api/encode_transaction.go +++ b/api/encode_transaction.go @@ -6,7 +6,7 @@ import ( "github.com/onflow/go-ethereum/core/types" - errs "github.com/onflow/flow-evm-gateway/api/errors" + errs "github.com/onflow/flow-evm-gateway/models/errors" ) const blockGasLimit uint64 = 15_000_000 diff --git a/api/errors/errors.go b/api/errors/errors.go deleted file mode 100644 index fe6d216cb..000000000 --- a/api/errors/errors.go +++ /dev/null @@ -1,67 +0,0 @@ -package errors - -import ( - "errors" - "fmt" - "math/big" - - "github.com/onflow/go-ethereum/accounts/abi" - "github.com/onflow/go-ethereum/common/hexutil" - gethVM "github.com/onflow/go-ethereum/core/vm" -) - -var ( - ErrNotSupported = errors.New("endpoint is not supported") - ErrInvalid = errors.New("invalid request") - ErrInternal = errors.New("internal error") - ErrRateLimit = errors.New("limit of requests per second reached") -) - -type GasPriceTooLowError struct { - GasPrice *big.Int -} - -func (e *GasPriceTooLowError) Error() string { - return fmt.Sprintf( - "the minimum accepted gas price for transactions is: %d", - e.GasPrice, - ) -} - -func NewErrGasPriceTooLow(gasPrice *big.Int) *GasPriceTooLowError { - return &GasPriceTooLowError{ - GasPrice: gasPrice, - } -} - -// RevertError is an API error that encompasses an EVM revert with JSON error -// code and a binary data blob. -type RevertError struct { - error - Reason string // revert reason hex encoded -} - -// ErrorCode returns the JSON error code for a revert. -// See: https://github.com/ethereum/wiki/wiki/JSON-RPC-Error-Codes-Improvement-Proposal -func (e *RevertError) ErrorCode() int { - return 3 -} - -// ErrorData returns the hex encoded revert reason. -func (e *RevertError) ErrorData() interface{} { - return e.Reason -} - -// NewRevertError creates a revertError instance with the provided revert data. -func NewRevertError(revert []byte) *RevertError { - err := gethVM.ErrExecutionReverted - - reason, errUnpack := abi.UnpackRevert(revert) - if errUnpack == nil { - err = fmt.Errorf("%w: %v", gethVM.ErrExecutionReverted, reason) - } - return &RevertError{ - error: err, - Reason: hexutil.Encode(revert), - } -} diff --git a/api/models.go b/api/models.go index 6acd1b19b..64d38b612 100644 --- a/api/models.go +++ b/api/models.go @@ -6,8 +6,8 @@ import ( "fmt" "math/big" - errs "github.com/onflow/flow-evm-gateway/api/errors" "github.com/onflow/flow-evm-gateway/models" + errs "github.com/onflow/flow-evm-gateway/models/errors" "github.com/onflow/go-ethereum/common" "github.com/onflow/go-ethereum/common/hexutil" @@ -40,7 +40,9 @@ type TransactionArgs struct { func (txArgs TransactionArgs) Validate() error { // Prevent accidental erroneous usage of both 'input' and 'data' (show stopper) if txArgs.Data != nil && txArgs.Input != nil && !bytes.Equal(*txArgs.Data, *txArgs.Input) { - return errors.New(`ambiguous request: both "data" and "input" are set and are not identical`) + return errs.InvalidTransaction( + errors.New(`ambiguous request: both "data" and "input" are set and are not identical`), + ) } // Place data on 'data' @@ -62,11 +64,13 @@ func (txArgs TransactionArgs) Validate() error { if txDataLen == 0 { // Prevent sending ether into black hole (show stopper) if txArgs.Value.ToInt().Cmp(big.NewInt(0)) > 0 { - return errors.New("transaction will create a contract with value but empty code") + return errs.InvalidTransaction( + errors.New("transaction will create a contract with value but empty code"), + ) } // No value submitted at least, critically Warn, but don't blow up - return errors.New("transaction will create a contract with empty code") + return errs.InvalidTransaction(errors.New("transaction will create a contract with empty code")) } } @@ -75,7 +79,7 @@ func (txArgs TransactionArgs) Validate() error { to := common.NewMixedcaseAddress(*txArgs.To) if bytes.Equal(to.Address().Bytes(), common.Address{}.Bytes()) { - return errors.New("transaction recipient is the zero address") + return errs.InvalidTransaction(errors.New("transaction recipient is the zero address")) } } diff --git a/api/pull.go b/api/pull.go index 46e1e0c00..e3b12e055 100644 --- a/api/pull.go +++ b/api/pull.go @@ -17,9 +17,9 @@ import ( "github.com/onflow/flow-evm-gateway/config" "github.com/onflow/flow-evm-gateway/models" + errs "github.com/onflow/flow-evm-gateway/models/errors" "github.com/onflow/flow-evm-gateway/services/logs" "github.com/onflow/flow-evm-gateway/storage" - errs "github.com/onflow/flow-evm-gateway/storage/errors" ) // maxFilters limits the max active filters at any time to prevent abuse and OOM diff --git a/api/ratelimiter.go b/api/ratelimiter.go index 61355ec74..95c3dc715 100644 --- a/api/ratelimiter.go +++ b/api/ratelimiter.go @@ -6,8 +6,9 @@ import ( "github.com/rs/zerolog" "github.com/sethvargo/go-limiter" - errs "github.com/onflow/flow-evm-gateway/api/errors" "github.com/onflow/go-ethereum/rpc" + + errs "github.com/onflow/flow-evm-gateway/models/errors" ) // rateLimit will limit requests with the provider limiter, in case the limit diff --git a/api/server.go b/api/server.go index f17bc90e1..72b046238 100644 --- a/api/server.go +++ b/api/server.go @@ -24,9 +24,9 @@ import ( "github.com/rs/zerolog" slogzerolog "github.com/samber/slog-zerolog" - errs "github.com/onflow/flow-evm-gateway/api/errors" "github.com/onflow/flow-evm-gateway/config" "github.com/onflow/flow-evm-gateway/metrics" + errs "github.com/onflow/flow-evm-gateway/models/errors" ) type rpcHandler struct { @@ -437,7 +437,7 @@ func (w *loggingResponseWriter) Write(data []byte) (int, error) { // don't error log known handled errors if !errorIs(err, errs.ErrRateLimit) && !errorIs(err, errs.ErrInvalid) && - !errorIs(err, errs.ErrInternal) && + !errorIs(err, errs.ErrFailedTransaction) && !errorIs(err, errs.ErrNotSupported) { l = w.logger.Error() } diff --git a/bootstrap/bootstrap.go b/bootstrap/bootstrap.go index 0ba0c8924..cddc7bd7f 100644 --- a/bootstrap/bootstrap.go +++ b/bootstrap/bootstrap.go @@ -17,11 +17,11 @@ import ( "github.com/onflow/flow-evm-gateway/config" "github.com/onflow/flow-evm-gateway/metrics" "github.com/onflow/flow-evm-gateway/models" + errs "github.com/onflow/flow-evm-gateway/models/errors" "github.com/onflow/flow-evm-gateway/services/ingestion" "github.com/onflow/flow-evm-gateway/services/requester" "github.com/onflow/flow-evm-gateway/services/traces" "github.com/onflow/flow-evm-gateway/storage" - storageErrs "github.com/onflow/flow-evm-gateway/storage/errors" "github.com/onflow/flow-evm-gateway/storage/pebble" ) @@ -81,7 +81,7 @@ func Start(ctx context.Context, cfg *config.Config) error { } // if database is not initialized require init height - if _, err := blocks.LatestCadenceHeight(); errors.Is(err, storageErrs.ErrNotInitialized) { + if _, err := blocks.LatestCadenceHeight(); errors.Is(err, errs.ErrNotInitialized) { cadenceHeight := cfg.InitCadenceHeight cadenceBlock, err := client.GetBlockHeaderByHeight(ctx, cadenceHeight) if err != nil { diff --git a/models/engine.go b/models/engine.go index 282bba9ec..2fcd685c5 100644 --- a/models/engine.go +++ b/models/engine.go @@ -2,9 +2,13 @@ package models import ( "context" + "errors" "fmt" - "github.com/rs/zerolog" "time" + + "github.com/rs/zerolog" + + errs "github.com/onflow/flow-evm-gateway/models/errors" ) // Engine defines a processing unit @@ -85,7 +89,7 @@ func (r *RestartableEngine) Run(ctx context.Context) error { // don't restart if no error is returned, normal after stop procedure is done return nil } - if !IsRecoverableError(err) { + if !errors.Is(err, errs.ErrRecoverable) { r.logger.Error().Err(err).Msg("received unrecoverable error") // if error is not recoverable just die return err diff --git a/models/engine_test.go b/models/engine_test.go index 338d4f135..3bf4eb077 100644 --- a/models/engine_test.go +++ b/models/engine_test.go @@ -6,11 +6,13 @@ import ( "testing" "time" - "github.com/onflow/flow-evm-gateway/models/mocks" "github.com/rs/zerolog" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" + + errs "github.com/onflow/flow-evm-gateway/models/errors" + "github.com/onflow/flow-evm-gateway/models/mocks" ) func Test_RestartableEngine(t *testing.T) { @@ -61,13 +63,13 @@ func Test_RestartableEngine(t *testing.T) { // make sure time diff increases with each retry assert.True(t, prevDiff < curDiff) prevDiff = curDiff - return ErrDisconnected + return errs.ErrDisconnected }). Times(int(retries)) r := NewRestartableEngine(mockEngine, retries, zerolog.New(zerolog.NewTestWriter(t))) err := r.Run(context.Background()) - require.EqualError(t, ErrDisconnected, err.Error()) + require.EqualError(t, errs.ErrDisconnected, err.Error()) }) t.Run("should restart when recoverable error is returned but then return nil after error is no longer returned", func(t *testing.T) { @@ -83,7 +85,7 @@ func Test_RestartableEngine(t *testing.T) { }). Once() - return ErrDisconnected + return errs.ErrDisconnected }). Once() diff --git a/models/errors.go b/models/errors.go deleted file mode 100644 index 270539666..000000000 --- a/models/errors.go +++ /dev/null @@ -1,33 +0,0 @@ -package models - -import ( - "errors" - "fmt" -) - -var ( - ErrDisconnected = NewRecoverableError(errors.New("disconnected")) - ErrInvalidEVMTransaction = errors.New("invalid evm transaction") -) - -func NewRecoverableError(err error) RecoverableError { - return RecoverableError{err} -} - -// RecoverableError is used to signal any types of errors that if encountered -// could be retried again -type RecoverableError struct { - err error -} - -func (r RecoverableError) Unwrap() error { - return r.err -} - -func (r RecoverableError) Error() string { - return fmt.Sprintf("recoverable error: %v", r.err) -} - -func IsRecoverableError(err error) bool { - return errors.As(err, &RecoverableError{}) -} diff --git a/models/errors/errors.go b/models/errors/errors.go new file mode 100644 index 000000000..5f55edd94 --- /dev/null +++ b/models/errors/errors.go @@ -0,0 +1,96 @@ +package errors + +import ( + "errors" + "fmt" + "math/big" + + "github.com/onflow/go-ethereum/accounts/abi" + "github.com/onflow/go-ethereum/common/hexutil" + gethVM "github.com/onflow/go-ethereum/core/vm" +) + +var ( + // API specific errors + + ErrNotSupported = errors.New("endpoint is not supported") + ErrRateLimit = errors.New("limit of requests per second reached") + + // General errors + + ErrInternal = errors.New("internal error") + ErrInvalid = errors.New("invalid") + ErrRecoverable = errors.New("recoverable") + ErrDisconnected = Recoverable(errors.New("disconnected")) + + // Transaction errors + + ErrFailedTransaction = errors.New("failed transaction") + ErrInvalidTransaction = errors.Join(ErrFailedTransaction, errors.New("invalid")) + + // Storage errors + + // ErrNotInitialized indicates storage instance was not correctly initialized and contains empty required values. + ErrNotInitialized = errors.New("storage not initialized") + // ErrNotFound indicates the resource does not exist. + ErrNotFound = errors.New("entity not found") + // ErrDuplicate indicates that the entity can not be stored due to an already existing same entity. + ErrDuplicate = errors.New("entity duplicate") + // ErrInvalidRange indicates that the block range provided as start and end height is invalid. + ErrInvalidRange = errors.Join(ErrInvalid, errors.New("invalid block height range")) + // ErrOutOfRange indicates the requested height is out of available range + ErrOutOfRange = errors.Join(ErrInvalid, errors.New("height is out of available range")) +) + +func FailedTransaction(reason string) error { + return errors.Join(ErrFailedTransaction, errors.New(reason)) +} + +func InvalidTransaction(err error) error { + return errors.Join(ErrInvalidTransaction, err) +} + +func TransactionGasPriceTooLow(gasPrice *big.Int) error { + return InvalidTransaction(fmt.Errorf( + "the minimum accepted gas price for transactions is: %d", + gasPrice, + )) +} + +func Recoverable(err error) error { + return errors.Join(ErrRecoverable, err) +} + +// RevertError is an API error that encompasses an EVM revert with JSON error +// code and a binary data blob. +// We need this custom error type defined because the Geth server implementation +// expects this type when serialising the error API response. +type RevertError struct { + error + Reason string // revert reason hex encoded +} + +// ErrorCode returns the JSON error code for a revert. +// See: https://github.com/ethereum/wiki/wiki/JSON-RPC-Error-Codes-Improvement-Proposal +func (e *RevertError) ErrorCode() int { + return 3 +} + +// ErrorData returns the hex encoded revert reason. +func (e *RevertError) ErrorData() interface{} { + return e.Reason +} + +// NewRevertError creates a revertError instance with the provided revert data. +func NewRevertError(revert []byte) *RevertError { + err := gethVM.ErrExecutionReverted + + reason, errUnpack := abi.UnpackRevert(revert) + if errUnpack == nil { + err = fmt.Errorf("%w: %v", gethVM.ErrExecutionReverted, reason) + } + return &RevertError{ + error: err, + Reason: hexutil.Encode(revert), + } +} diff --git a/services/ingestion/subscriber.go b/services/ingestion/subscriber.go index f127e2607..57a679212 100644 --- a/services/ingestion/subscriber.go +++ b/services/ingestion/subscriber.go @@ -9,6 +9,7 @@ import ( "github.com/onflow/flow-go/fvm/evm/events" "github.com/onflow/flow-evm-gateway/models" + errs "github.com/onflow/flow-evm-gateway/models/errors" "github.com/onflow/flow-evm-gateway/services/requester" "github.com/onflow/flow-go-sdk" @@ -116,7 +117,7 @@ func (r *RPCSubscriber) subscribe(ctx context.Context, height uint64, opts ...ac return events } - evs, errs, err := r.client.SubscribeEventsByBlockHeight(ctx, height, r.blocksFilter(), opts...) + evs, errChan, err := r.client.SubscribeEventsByBlockHeight(ctx, height, r.blocksFilter(), opts...) if err != nil { events <- models.NewBlockEventsError(fmt.Errorf("failed to subscribe to events by block height: %w", err)) return events @@ -136,7 +137,7 @@ func (r *RPCSubscriber) subscribe(ctx context.Context, height uint64, opts ...ac case blockEvents, ok := <-evs: if !ok { var err error - err = models.ErrDisconnected + err = errs.ErrDisconnected if ctx.Err() != nil { err = ctx.Err() } @@ -146,10 +147,10 @@ func (r *RPCSubscriber) subscribe(ctx context.Context, height uint64, opts ...ac events <- models.NewBlockEvents(blockEvents) - case err, ok := <-errs: + case err, ok := <-errChan: if !ok { var err error - err = models.ErrDisconnected + err = errs.ErrDisconnected if ctx.Err() != nil { err = ctx.Err() } @@ -157,7 +158,7 @@ func (r *RPCSubscriber) subscribe(ctx context.Context, height uint64, opts ...ac return } - events <- models.NewBlockEventsError(errors.Join(err, models.ErrDisconnected)) + events <- models.NewBlockEventsError(errors.Join(err, errs.ErrDisconnected)) return } } diff --git a/services/ingestion/subscriber_test.go b/services/ingestion/subscriber_test.go index 61cf74600..40278db4b 100644 --- a/services/ingestion/subscriber_test.go +++ b/services/ingestion/subscriber_test.go @@ -6,7 +6,7 @@ import ( "github.com/onflow/flow-go-sdk/access" - "github.com/onflow/flow-evm-gateway/models" + errs "github.com/onflow/flow-evm-gateway/models/errors" "github.com/onflow/flow-evm-gateway/services/requester" "github.com/onflow/flow-evm-gateway/services/testutils" @@ -44,7 +44,7 @@ func Test_Subscribing(t *testing.T) { for ev := range events { if prevHeight == endHeight { - require.ErrorIs(t, ev.Err, models.ErrDisconnected) + require.ErrorIs(t, ev.Err, errs.ErrDisconnected) break } diff --git a/services/logs/filter.go b/services/logs/filter.go index 02a17352a..3d7fee461 100644 --- a/services/logs/filter.go +++ b/services/logs/filter.go @@ -9,7 +9,7 @@ import ( gethTypes "github.com/onflow/go-ethereum/core/types" "golang.org/x/exp/slices" - errs "github.com/onflow/flow-evm-gateway/api/errors" + errs "github.com/onflow/flow-evm-gateway/models/errors" "github.com/onflow/flow-evm-gateway/storage" ) diff --git a/services/logs/filter_test.go b/services/logs/filter_test.go index 832bb1b3b..1ba517954 100644 --- a/services/logs/filter_test.go +++ b/services/logs/filter_test.go @@ -11,8 +11,8 @@ import ( "github.com/stretchr/testify/require" "github.com/onflow/flow-evm-gateway/models" + errs "github.com/onflow/flow-evm-gateway/models/errors" "github.com/onflow/flow-evm-gateway/storage" - "github.com/onflow/flow-evm-gateway/storage/errors" "github.com/onflow/flow-evm-gateway/storage/mocks" ) @@ -103,7 +103,7 @@ func blockStorage() storage.BlockIndexer { return b, nil } } - return nil, errors.ErrNotFound + return nil, errs.ErrNotFound }) return blockStorage @@ -127,7 +127,7 @@ func receiptStorage() storage.ReceiptIndexer { } if len(rcps) == 0 { - return nil, errors.ErrNotFound + return nil, errs.ErrNotFound } return rcps, nil diff --git a/services/requester/cross-spork_client.go b/services/requester/cross-spork_client.go index 73bdfd195..4e9910234 100644 --- a/services/requester/cross-spork_client.go +++ b/services/requester/cross-spork_client.go @@ -2,7 +2,6 @@ package requester import ( "context" - "errors" "fmt" "github.com/onflow/cadence" @@ -11,9 +10,9 @@ import ( flowGo "github.com/onflow/flow-go/model/flow" "github.com/rs/zerolog" "golang.org/x/exp/slices" -) -var ErrOutOfRange = errors.New("height is out of available range") + errs "github.com/onflow/flow-evm-gateway/models/errors" +) type sporkClient struct { firstHeight uint64 @@ -149,7 +148,7 @@ func (c *CrossSporkClient) getClientForHeight(height uint64) (access.Client, err client := c.sporkClients.get(height) if client == nil { - return nil, ErrOutOfRange + return nil, errs.ErrOutOfRange } c.logger.Debug(). diff --git a/services/requester/cross-spork_client_test.go b/services/requester/cross-spork_client_test.go index 93e446fc7..34e045dd1 100644 --- a/services/requester/cross-spork_client_test.go +++ b/services/requester/cross-spork_client_test.go @@ -10,6 +10,7 @@ import ( "github.com/rs/zerolog" "github.com/stretchr/testify/require" + errs "github.com/onflow/flow-evm-gateway/models/errors" "github.com/onflow/flow-evm-gateway/services/testutils" ) @@ -101,7 +102,7 @@ func Test_CrossSpork(t *testing.T) { c, err = client.getClientForHeight(10) require.Nil(t, c) - require.ErrorIs(t, err, ErrOutOfRange) + require.ErrorIs(t, err, errs.ErrOutOfRange) require.True(t, client.IsPastSpork(200)) require.True(t, client.IsPastSpork(past1Last)) @@ -109,13 +110,13 @@ func Test_CrossSpork(t *testing.T) { require.False(t, client.IsPastSpork(600)) _, err = client.ExecuteScriptAtBlockHeight(context.Background(), 20, []byte{}, nil) - require.ErrorIs(t, err, ErrOutOfRange) + require.ErrorIs(t, err, errs.ErrOutOfRange) _, err = client.GetBlockHeaderByHeight(context.Background(), 20) - require.ErrorIs(t, err, ErrOutOfRange) + require.ErrorIs(t, err, errs.ErrOutOfRange) _, _, err = client.SubscribeEventsByBlockHeight(context.Background(), 20, flow.EventFilter{}, nil) - require.ErrorIs(t, err, ErrOutOfRange) + require.ErrorIs(t, err, errs.ErrOutOfRange) height, err := client.GetLatestHeightForSpork(context.Background(), past2Last-10) require.NoError(t, err) @@ -130,6 +131,6 @@ func Test_CrossSpork(t *testing.T) { require.Equal(t, currentLast, height) _, err = client.GetLatestHeightForSpork(context.Background(), 10) - require.ErrorIs(t, err, ErrOutOfRange) + require.ErrorIs(t, err, errs.ErrOutOfRange) }) } diff --git a/services/requester/pool.go b/services/requester/pool.go index f62f1735a..61405a29a 100644 --- a/services/requester/pool.go +++ b/services/requester/pool.go @@ -13,6 +13,7 @@ import ( "github.com/sethvargo/go-retry" "github.com/onflow/flow-evm-gateway/models" + errs "github.com/onflow/flow-evm-gateway/models/errors" ) const ( @@ -77,15 +78,15 @@ func (t *TxPool) Send( } if res.Error != nil { + if err, ok := parseInvalidError(res.Error); ok { + return err + } + t.logger.Error().Err(res.Error). Str("flow-id", flowTx.ID().String()). Str("evm-id", evmTx.Hash().Hex()). Msg("flow transaction error") - if err, ok := parseInvalidError(res.Error); ok { - return err - } - // hide specific cause since it's an implementation issue return fmt.Errorf("failed to submit flow evm transaction %s", evmTx.Hash()) } @@ -105,5 +106,5 @@ func parseInvalidError(err error) (error, bool) { return nil, false } - return fmt.Errorf("%w: %s", models.ErrInvalidEVMTransaction, matches[1]), true + return errs.FailedTransaction(matches[1]), true } diff --git a/services/requester/requester.go b/services/requester/requester.go index a8c780fbb..f554fed9e 100644 --- a/services/requester/requester.go +++ b/services/requester/requester.go @@ -25,9 +25,9 @@ import ( "github.com/rs/zerolog" "golang.org/x/sync/errgroup" - errs "github.com/onflow/flow-evm-gateway/api/errors" "github.com/onflow/flow-evm-gateway/config" "github.com/onflow/flow-evm-gateway/models" + errs "github.com/onflow/flow-evm-gateway/models/errors" "github.com/onflow/flow-evm-gateway/storage" ) @@ -206,7 +206,7 @@ func (e *EVM) SendRawTransaction(ctx context.Context, data []byte) (common.Hash, } if tx.GasPrice().Cmp(e.config.GasPrice) < 0 { - return common.Hash{}, errs.NewErrGasPriceTooLow(e.config.GasPrice) + return common.Hash{}, errs.TransactionGasPriceTooLow(e.config.GasPrice) } txData := hex.EncodeToString(data) @@ -308,7 +308,7 @@ func (e *EVM) GetBalance( []cadence.Value{hexEncodedAddress}, ) if err != nil { - if !errors.Is(err, ErrOutOfRange) { + if !errors.Is(err, errs.ErrOutOfRange) { e.logger.Error(). Err(err). Str("address", address.String()). @@ -349,7 +349,7 @@ func (e *EVM) GetNonce( []cadence.Value{hexEncodedAddress}, ) if err != nil { - if !errors.Is(err, ErrOutOfRange) { + if !errors.Is(err, errs.ErrOutOfRange) { e.logger.Error().Err(err). Str("address", address.String()). Int64("evm-height", evmHeight). @@ -440,7 +440,7 @@ func (e *EVM) Call( []cadence.Value{hexEncodedTx, hexEncodedAddress}, ) if err != nil { - if !errors.Is(err, ErrOutOfRange) { + if !errors.Is(err, errs.ErrOutOfRange) { e.logger.Error(). Err(err). Uint64("cadence-height", height). @@ -452,16 +452,9 @@ func (e *EVM) Call( return nil, fmt.Errorf("failed to execute script: %w", err) } - evmResult, err := stdlib.ResultSummaryFromEVMResultValue(scriptResult) + evmResult, err := parseResult(scriptResult) if err != nil { - return nil, fmt.Errorf("failed to decode EVM result from call [%s]: %w", scriptResult.String(), err) - } - - if evmResult.ErrorCode != 0 { - if evmResult.ErrorCode == evmTypes.ExecutionErrCodeExecutionReverted { - return nil, errs.NewRevertError(evmResult.ReturnedData) - } - return nil, evmTypes.ErrorFromCode(evmResult.ErrorCode) + return nil, err } result := evmResult.ReturnedData @@ -506,16 +499,9 @@ func (e *EVM) EstimateGas( return 0, fmt.Errorf("failed to execute script: %w", err) } - evmResult, err := stdlib.ResultSummaryFromEVMResultValue(scriptResult) + evmResult, err := parseResult(scriptResult) if err != nil { - return 0, fmt.Errorf("failed to decode EVM result from gas estimation: %w", err) - } - - if evmResult.ErrorCode != 0 { - if evmResult.ErrorCode == evmTypes.ExecutionErrCodeExecutionReverted { - return 0, errs.NewRevertError(evmResult.ReturnedData) - } - return 0, evmTypes.ErrorFromCode(evmResult.ErrorCode) + return 0, err } gasConsumed := evmResult.GasConsumed @@ -549,7 +535,7 @@ func (e *EVM) GetCode( []cadence.Value{hexEncodedAddress}, ) if err != nil { - if !errors.Is(err, ErrOutOfRange) { + if !errors.Is(err, errs.ErrOutOfRange) { e.logger.Error(). Err(err). Uint64("cadence-height", height). @@ -695,7 +681,7 @@ func (e *EVM) executeScriptAtHeight( // if snapshot doesn't exist on EN, the height at which script was executed is out // of the boundaries the EN keeps state, so return out of range if strings.Contains(err.Error(), "failed to create storage snapshot") { - return nil, ErrOutOfRange + return nil, errs.ErrOutOfRange } } @@ -721,3 +707,20 @@ func cadenceStringToBytes(value cadence.Value) ([]byte, error) { return code, nil } + +// handleResult +func parseResult(res cadence.Value) (*evmTypes.ResultSummary, error) { + result, err := stdlib.ResultSummaryFromEVMResultValue(res) + if err != nil { + return nil, fmt.Errorf("failed to decode EVM result: %w", err) + } + + if result.ErrorCode != 0 { + if result.ErrorCode == evmTypes.ExecutionErrCodeExecutionReverted { + return nil, errs.NewRevertError(result.ReturnedData) + } + return nil, errs.FailedTransaction(result.ErrorMessage) + } + + return result, err +} diff --git a/storage/errors/error.go b/storage/errors/error.go deleted file mode 100644 index d8639309d..000000000 --- a/storage/errors/error.go +++ /dev/null @@ -1,14 +0,0 @@ -package errors - -import "errors" - -var ( - // ErrNotInitialized indicates storage instance was not correctly initialized and contains empty required values. - ErrNotInitialized = errors.New("storage not initialized") - // ErrNotFound indicates the resource does not exist. - ErrNotFound = errors.New("entity not found") - // ErrDuplicate indicates that the entity can not be stored due to an already existing same entity. - ErrDuplicate = errors.New("entity duplicate") - // ErrInvalidRange indicates that the block range provided as start and end height is invalid. - ErrInvalidRange = errors.New("invalid block height range") -) diff --git a/storage/index_testsuite.go b/storage/index_testsuite.go index 5ac29e703..96f4e334b 100644 --- a/storage/index_testsuite.go +++ b/storage/index_testsuite.go @@ -13,7 +13,7 @@ import ( "github.com/stretchr/testify/suite" "github.com/onflow/flow-evm-gateway/models" - "github.com/onflow/flow-evm-gateway/storage/errors" + "github.com/onflow/flow-evm-gateway/models/errors" "github.com/onflow/flow-evm-gateway/storage/mocks" ) diff --git a/storage/pebble/accounts.go b/storage/pebble/accounts.go index 8628e8965..54b6fe522 100644 --- a/storage/pebble/accounts.go +++ b/storage/pebble/accounts.go @@ -11,8 +11,8 @@ import ( "github.com/onflow/go-ethereum/common" "github.com/onflow/flow-evm-gateway/models" + errs "github.com/onflow/flow-evm-gateway/models/errors" "github.com/onflow/flow-evm-gateway/storage" - errs "github.com/onflow/flow-evm-gateway/storage/errors" ) var _ storage.AccountIndexer = &Accounts{} diff --git a/storage/pebble/blocks.go b/storage/pebble/blocks.go index fef3643d7..5b7cbd611 100644 --- a/storage/pebble/blocks.go +++ b/storage/pebble/blocks.go @@ -12,8 +12,8 @@ import ( "github.com/onflow/go-ethereum/common" "github.com/onflow/flow-evm-gateway/models" + errs "github.com/onflow/flow-evm-gateway/models/errors" "github.com/onflow/flow-evm-gateway/storage" - errs "github.com/onflow/flow-evm-gateway/storage/errors" ) var _ storage.BlockIndexer = &Blocks{} diff --git a/storage/pebble/receipts.go b/storage/pebble/receipts.go index ff83b4c14..de5501f1f 100644 --- a/storage/pebble/receipts.go +++ b/storage/pebble/receipts.go @@ -14,8 +14,8 @@ import ( "github.com/onflow/go-ethereum/rlp" "github.com/onflow/flow-evm-gateway/models" + errs "github.com/onflow/flow-evm-gateway/models/errors" "github.com/onflow/flow-evm-gateway/storage" - errs "github.com/onflow/flow-evm-gateway/storage/errors" ) var _ storage.ReceiptIndexer = &Receipts{} diff --git a/storage/pebble/storage.go b/storage/pebble/storage.go index a0938e840..a3be760b9 100644 --- a/storage/pebble/storage.go +++ b/storage/pebble/storage.go @@ -8,7 +8,7 @@ import ( "github.com/cockroachdb/pebble" "github.com/rs/zerolog" - errs "github.com/onflow/flow-evm-gateway/storage/errors" + errs "github.com/onflow/flow-evm-gateway/models/errors" ) type Storage struct { diff --git a/storage/pebble/storage_test.go b/storage/pebble/storage_test.go index 69155f343..19b659b71 100644 --- a/storage/pebble/storage_test.go +++ b/storage/pebble/storage_test.go @@ -13,8 +13,8 @@ import ( "github.com/stretchr/testify/suite" "github.com/onflow/flow-evm-gateway/config" + "github.com/onflow/flow-evm-gateway/models/errors" "github.com/onflow/flow-evm-gateway/storage" - "github.com/onflow/flow-evm-gateway/storage/errors" "github.com/onflow/flow-evm-gateway/storage/mocks" )