From 6eb5cbc70dc36a5b2b256b1549414f648e9025f9 Mon Sep 17 00:00:00 2001 From: Gregor Gololicic Date: Wed, 21 Feb 2024 22:24:51 +0100 Subject: [PATCH 01/40] improve api handling error --- api/api.go | 126 ++++++++++++++++------------------------- api/server.go | 7 ++- bootstrap/bootstrap.go | 5 +- 3 files changed, 59 insertions(+), 79 deletions(-) diff --git a/api/api.go b/api/api.go index 4e4549739..add6fbd86 100644 --- a/api/api.go +++ b/api/api.go @@ -76,14 +76,14 @@ func (b *BlockChainAPI) ChainId() *hexutil.Big { } // BlockNumber returns the block number of the chain head. -func (b *BlockChainAPI) BlockNumber() hexutil.Uint64 { +func (b *BlockChainAPI) BlockNumber() (hexutil.Uint64, error) { latestBlockHeight, err := b.blocks.LatestEVMHeight() if err != nil { b.logger.Error().Err(err).Msg("failed to get latest height") - return 0 + return 0, err } - return hexutil.Uint64(latestBlockHeight) + return hexutil.Uint64(latestBlockHeight), nil } // Syncing returns false in case the node is currently not syncing with the network. It can be up-to-date or has not @@ -143,20 +143,12 @@ func (b *BlockChainAPI) GetTransactionByHash( ) (*RPCTransaction, error) { tx, err := b.transactions.Get(hash) if err != nil { - if !errors.Is(err, storageErrs.NotFound) { - b.logger.Error().Err(err).Msg("failed to get transaction by hash") - return nil, errs.ErrInternal - } - return nil, err + return handleError[*RPCTransaction](b.logger, err) } rcp, err := b.receipts.GetByTransactionID(tx.Hash()) if err != nil { - if !errors.Is(err, storageErrs.NotFound) { - b.logger.Error().Err(err).Msg("failed to get receipt by hash") - return nil, errs.ErrInternal - } - return nil, err + return handleError[*RPCTransaction](b.logger, err) } from, err := types.Sender(types.LatestSignerForChainID(tx.ChainId()), tx) @@ -193,26 +185,24 @@ func (b *BlockChainAPI) GetTransactionByBlockHashAndIndex( ctx context.Context, blockHash common.Hash, index hexutil.Uint, -) *RPCTransaction { // todo add error to the return type +) (*RPCTransaction, error) { // todo add error to the return type block, err := b.blocks.GetByID(blockHash) if err != nil { - b.logger.Error().Err(err).Msg("failed to get block by id") - return nil + return handleError[*RPCTransaction](b.logger, err) } highestIndex := len(block.TransactionHashes) - 1 if index > hexutil.Uint(highestIndex) { - return nil + return nil, nil } txHash := block.TransactionHashes[index] tx, err := b.GetTransactionByHash(ctx, txHash) if err != nil { - b.logger.Error().Err(err).Msg("failed to get transaction by id") - return nil + return handleError[*RPCTransaction](b.logger, err) } - return tx + return tx, nil } // GetTransactionByBlockNumberAndIndex returns the transaction for the given block number and index. @@ -220,31 +210,24 @@ func (b *BlockChainAPI) GetTransactionByBlockNumberAndIndex( ctx context.Context, blockNumber rpc.BlockNumber, index hexutil.Uint, -) *RPCTransaction { // todo add error to the return type +) (*RPCTransaction, error) { // todo add error to the return type block, err := b.blocks.GetByHeight(uint64(blockNumber)) if err != nil { - if !errors.Is(err, storageErrs.NotFound) { - b.logger.Error().Err(err).Msg("failed to get block by height") - } - return nil + return handleError[*RPCTransaction](b.logger, err) } highestIndex := len(block.TransactionHashes) - 1 if index > hexutil.Uint(highestIndex) { - return nil + return nil, nil } txHash := block.TransactionHashes[index] tx, err := b.GetTransactionByHash(ctx, txHash) if err != nil { - if !errors.Is(err, storageErrs.NotFound) { - b.logger.Error().Err(err).Msg("failed to get transaction by hash") - } - - return nil + return handleError[*RPCTransaction](b.logger, err) } - return tx + return tx, nil } // GetTransactionReceipt returns the transaction receipt for the given transaction hash. @@ -252,14 +235,14 @@ func (b *BlockChainAPI) GetTransactionReceipt( ctx context.Context, hash common.Hash, ) (*types.Receipt, error) { - rcp, err := b.receipts.GetByTransactionID(hash) + _, err := b.transactions.Get(hash) if err != nil { - if !errors.Is(err, storageErrs.NotFound) { - b.logger.Error().Err(err).Msg("failed to get transaction by ID") - return nil, errs.ErrInternal - } + return handleError[*types.Receipt](b.logger, err) + } - return nil, fmt.Errorf("failed to get transaction receipt: %w", err) + rcp, err := b.receipts.GetByTransactionID(hash) + if err != nil { + return handleError[*types.Receipt](b.logger, err) } return rcp, nil @@ -281,11 +264,7 @@ func (b *BlockChainAPI) GetBlockByHash( bl, err := b.blocks.GetByID(hash) if err != nil { - if !errors.Is(err, storageErrs.NotFound) { - b.logger.Error().Err(err).Msg("failed to get latest block height") - return nil, errs.ErrInternal - } - return nil, err + return handleError[map[string]any](b.logger, err) } h, err := bl.Hash() @@ -326,23 +305,15 @@ func (b *BlockChainAPI) GetBlockByNumber( if blockNumber == -2 { height, err = b.blocks.LatestEVMHeight() if err != nil { - if !errors.Is(err, storageErrs.NotFound) { - b.logger.Error().Err(err).Msg("failed to get latest block height") - return nil, errs.ErrInternal - } - return nil, err + return handleError[map[string]any](b.logger, err) } } else if blockNumber < 0 { - return nil, fmt.Errorf("not supported") + return nil, errs.ErrNotSupported } bl, err := b.blocks.GetByHeight(height) if err != nil { - if !errors.Is(err, storageErrs.NotFound) { - b.logger.Error().Err(err).Msg("failed to get block by height") - return nil, errs.ErrInternal - } - return nil, err + return handleError[map[string]any](b.logger, err) } h, err := bl.Hash() @@ -380,19 +351,14 @@ func (b *BlockChainAPI) GetBlockReceipts( ) } if err != nil { - if !errors.Is(err, storageErrs.NotFound) { - b.logger.Error().Err(err).Msg("failed to get receipt") - return nil, errs.ErrInternal - } - - return nil, err + return handleError[[]*types.Receipt](b.logger, err) } receipts := make([]*types.Receipt, len(block.TransactionHashes)) for i, hash := range block.TransactionHashes { rcp, err := b.receipts.GetByTransactionID(hash) if err != nil { - return nil, err + return handleError[[]*types.Receipt](b.logger, err) } receipts[i] = rcp } @@ -404,39 +370,33 @@ func (b *BlockChainAPI) GetBlockReceipts( func (b *BlockChainAPI) GetBlockTransactionCountByHash( ctx context.Context, blockHash common.Hash, -) *hexutil.Uint { +) (*hexutil.Uint, error) { block, err := b.blocks.GetByID(blockHash) if err != nil { - if !errors.Is(err, storageErrs.NotFound) { - b.logger.Error().Err(err).Msg("failed to get block by ID") - } - return nil + return handleError[*hexutil.Uint](b.logger, err) } count := hexutil.Uint(len(block.TransactionHashes)) - return &count + return &count, nil } // GetBlockTransactionCountByNumber returns the number of transactions in the block with the given block number. func (b *BlockChainAPI) GetBlockTransactionCountByNumber( ctx context.Context, blockNumber rpc.BlockNumber, -) *hexutil.Uint { +) (*hexutil.Uint, error) { if blockNumber < rpc.EarliestBlockNumber { // todo handle block number for negative special values in all APIs - return nil + return nil, errs.ErrNotSupported } block, err := b.blocks.GetByHeight(uint64(blockNumber)) if err != nil { - if !errors.Is(err, storageErrs.NotFound) { - b.logger.Error().Err(err).Msg("failed to get block by height") - } - return nil + return handleError[*hexutil.Uint](b.logger, err) } count := hexutil.Uint(len(block.TransactionHashes)) - return &count + return &count, nil } // Call executes the given transaction on the state for the given block number. @@ -463,13 +423,16 @@ func (b *BlockChainAPI) Call( } else if args.Input != nil { data = *args.Input } else { - return nil, fmt.Errorf("must provide either data or input values: %w", errs.ErrInvalid) + return nil, errors.Join( + errs.ErrInvalid, + fmt.Errorf("must provide either data or input values: %w", errs.ErrInvalid), + ) } res, err := b.evm.Call(ctx, *args.To, data) if err != nil { b.logger.Error().Err(err).Msg("failed to execute call") - return nil, err + return nil, errs.ErrInternal } return res, nil @@ -538,6 +501,17 @@ func (b *BlockChainAPI) EstimateGas( return hexutil.Uint64(params.MaxGasLimit), nil } +func handleError[T any](log zerolog.Logger, err error) (T, error) { + var zero T + if errors.Is(err, storageErrs.NotFound) { + // as per specification returning nil and nil for not found resources + return zero, nil + } + + log.Error().Err(err).Msg("failed to get latest block height") + return zero, errs.ErrInternal +} + /* ==================================================================================================================== NOT SUPPORTED SECTION diff --git a/api/server.go b/api/server.go index 4a470e947..fe75ea165 100644 --- a/api/server.go +++ b/api/server.go @@ -207,7 +207,12 @@ func (h *httpServer) disableRPC() bool { func (h *httpServer) ServeHTTP(w http.ResponseWriter, r *http.Request) { // Check if WebSocket request and serve if JSON-RPC over WebSocket is enabled if b, err := io.ReadAll(r.Body); err == nil { - h.log.Debug().Str("body", string(b)).Str("url", r.URL.String()).Msg("API request") + h.log.Debug(). + Str("body", string(b)). + Str("url", r.URL.String()). + Bool("ws", isWebSocket(r)). + Msg("API request") + r.Body = io.NopCloser(bytes.NewBuffer(b)) r.Body.Close() } diff --git a/bootstrap/bootstrap.go b/bootstrap/bootstrap.go index e125b4271..2a6eb6567 100644 --- a/bootstrap/bootstrap.go +++ b/bootstrap/bootstrap.go @@ -141,9 +141,10 @@ func startServer( accounts storage.AccountIndexer, logger zerolog.Logger, ) error { - logger.Info().Msg("starting up RPC server") + l := logger.With().Str("component", "API").Logger() + l.Info().Msg("starting up RPC server") - srv := api.NewHTTPServer(logger, rpc.DefaultHTTPTimeouts) + srv := api.NewHTTPServer(l, rpc.DefaultHTTPTimeouts) client, err := grpc.NewClient(cfg.AccessNodeGRPCHost) if err != nil { From 02cfeb4a9c97d3c17b3141fb31742bae2c5479db Mon Sep 17 00:00:00 2001 From: Gregor Gololicic Date: Wed, 21 Feb 2024 22:29:02 +0100 Subject: [PATCH 02/40] move height increment --- services/ingestion/engine.go | 40 ++++++++++++++++++------------------ 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/services/ingestion/engine.go b/services/ingestion/engine.go index 19329595c..8c9728266 100644 --- a/services/ingestion/engine.go +++ b/services/ingestion/engine.go @@ -16,14 +16,14 @@ var ErrDisconnected = errors.New("disconnected") var _ models.Engine = &Engine{} type Engine struct { - subscriber EventSubscriber - blocks storage.BlockIndexer - receipts storage.ReceiptIndexer - transactions storage.TransactionIndexer - accounts storage.AccountIndexer - log zerolog.Logger - lastHeight *models.SequentialHeight - status *models.EngineStatus + subscriber EventSubscriber + blocks storage.BlockIndexer + receipts storage.ReceiptIndexer + transactions storage.TransactionIndexer + accounts storage.AccountIndexer + log zerolog.Logger + evmLastHeight *models.SequentialHeight + status *models.EngineStatus } func NewEventIngestionEngine( @@ -73,16 +73,6 @@ func (e *Engine) Start(ctx context.Context) error { return fmt.Errorf("failed to get latest cadence height: %w", err) } - // only init latest height if not set - if e.lastHeight == nil { - e.lastHeight = models.NewSequentialHeight(latestCadence) - } else { // otherwise make sure the latest height is same as the one set on the engine - err = e.lastHeight.Increment(latestCadence) - if err != nil { - return err - } - } - e.log.Info().Uint64("start-cadence-height", latestCadence).Msg("starting ingestion") events, errs, err := e.subscriber.Subscribe(ctx, latestCadence) @@ -157,6 +147,16 @@ func (e *Engine) processBlockEvent(cadenceHeight uint64, event cadence.Event) er return err } + // only init latest height if not set + if e.evmLastHeight == nil { + e.evmLastHeight = models.NewSequentialHeight(block.Height) + } else { // otherwise make sure the latest height is same as the one set on the engine + err = e.evmLastHeight.Increment(block.Height) + if err != nil { + return err + } + } + h, _ := block.Hash() e.log.Info(). Str("hash", h.Hex()). @@ -165,8 +165,8 @@ func (e *Engine) processBlockEvent(cadenceHeight uint64, event cadence.Event) er Str("tx-hash", block.TransactionHashes[0].Hex()). // now we only have 1 tx per block Msg("new evm block executed event") - if err = e.lastHeight.Increment(block.Height); err != nil { - return fmt.Errorf("invalid block height, expected %d, got %d: %w", e.lastHeight.Load(), block.Height, err) + if err = e.evmLastHeight.Increment(block.Height); err != nil { + return fmt.Errorf("invalid block height, expected %d, got %d: %w", e.evmLastHeight.Load(), block.Height, err) } return e.blocks.Store(cadenceHeight, block) From 87ebc686ee9322644d2667032296149fc77357da Mon Sep 17 00:00:00 2001 From: Gregor Gololicic Date: Wed, 21 Feb 2024 22:31:08 +0100 Subject: [PATCH 03/40] increment last --- services/ingestion/engine.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/services/ingestion/engine.go b/services/ingestion/engine.go index 8c9728266..5ea92ef5a 100644 --- a/services/ingestion/engine.go +++ b/services/ingestion/engine.go @@ -73,6 +73,12 @@ func (e *Engine) Start(ctx context.Context) error { return fmt.Errorf("failed to get latest cadence height: %w", err) } + // todo we increase the latest indexed cadence height by one, but this assumes the last index of data + // was successful, in case of crash we would need to reindex. We do this now because nonces are not + // mapped to heights, which means the update to account is not idempotent, once update to account is + // idempotent we should remove this increment by 1. + latestCadence += 1 + e.log.Info().Uint64("start-cadence-height", latestCadence).Msg("starting ingestion") events, errs, err := e.subscriber.Subscribe(ctx, latestCadence) From 9520c76e98a58aef4f5da0082748eba498f378ba Mon Sep 17 00:00:00 2001 From: Gregor Gololicic Date: Wed, 21 Feb 2024 22:34:24 +0100 Subject: [PATCH 04/40] add a todo --- services/requester/requester.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/services/requester/requester.go b/services/requester/requester.go index d09a32d64..c31d30cb4 100644 --- a/services/requester/requester.go +++ b/services/requester/requester.go @@ -174,6 +174,9 @@ func (e *EVM) signAndSend(ctx context.Context, script []byte, args ...cadence.Va } // todo should we wait for the transaction result? + // we should handle a case where flow transaction is failed but we will get a result back, it would only be failed, + // but there is no evm transaction. So if we submit an evm tx and get back an ID and then we wait for receipt + // we would never get it, but this failure of sending flow transaction could somehow help with this case return flowTx.ID(), nil } From 3c20087e39b54048d2fccf9d87340aa99371233e Mon Sep 17 00:00:00 2001 From: Gregor Gololicic Date: Wed, 21 Feb 2024 22:38:43 +0100 Subject: [PATCH 05/40] improve error --- services/ingestion/engine.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/ingestion/engine.go b/services/ingestion/engine.go index 5ea92ef5a..2653d87f2 100644 --- a/services/ingestion/engine.go +++ b/services/ingestion/engine.go @@ -83,7 +83,7 @@ func (e *Engine) Start(ctx context.Context) error { events, errs, err := e.subscriber.Subscribe(ctx, latestCadence) if err != nil { - return err + return fmt.Errorf("failed to subscribe to events: %w", err) } e.status.Ready() From c50cc794dc27d85b9091f5c21d74ff987c37de70 Mon Sep 17 00:00:00 2001 From: Gregor Gololicic Date: Wed, 21 Feb 2024 22:41:05 +0100 Subject: [PATCH 06/40] improve error --- services/ingestion/subscriber.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/services/ingestion/subscriber.go b/services/ingestion/subscriber.go index 325d9e667..c7a0fda5e 100644 --- a/services/ingestion/subscriber.go +++ b/services/ingestion/subscriber.go @@ -34,6 +34,11 @@ func (r *RPCSubscriber) Subscribe(ctx context.Context, height uint64) (<-chan fl }, } + _, err := r.client.GetBlockByHeight(ctx, height) + if err != nil { + return nil, nil, fmt.Errorf("failed to subscribe for events, the block height %d doesn't exist: %w", height, err) + } + evs, errs, err := r.client.SubscribeEventsByBlockHeight(ctx, height, filter) if err != nil { return nil, nil, fmt.Errorf("failed to subscribe to events by block height: %w", err) From 6afe86d85b4e623d26af671484d236eb84faec0a Mon Sep 17 00:00:00 2001 From: Gregor Gololicic Date: Wed, 21 Feb 2024 22:57:24 +0100 Subject: [PATCH 07/40] make account update idempotent --- storage/index.go | 2 +- storage/pebble/accounts.go | 74 +++++++++++++++++++++++++++++--------- 2 files changed, 58 insertions(+), 18 deletions(-) diff --git a/storage/index.go b/storage/index.go index 0cc873a74..50d448c80 100644 --- a/storage/index.go +++ b/storage/index.go @@ -84,7 +84,7 @@ type TransactionIndexer interface { type AccountIndexer interface { // Update account with executed transactions. - Update(tx *gethTypes.Transaction) error + Update(tx *gethTypes.Transaction, receipt *gethTypes.Receipt) error // GetNonce gets an account nonce. If no nonce was indexed it returns 0. // todo add getting nonce at provided block height / hash diff --git a/storage/pebble/accounts.go b/storage/pebble/accounts.go index 644026b68..8071cbf23 100644 --- a/storage/pebble/accounts.go +++ b/storage/pebble/accounts.go @@ -3,6 +3,7 @@ package pebble import ( "encoding/binary" "errors" + "fmt" "github.com/ethereum/go-ethereum/common" gethTypes "github.com/ethereum/go-ethereum/core/types" "github.com/onflow/flow-evm-gateway/storage" @@ -17,18 +18,18 @@ type Accounts struct { store *Storage mux sync.RWMutex // todo LRU caching with size limit - nonceCache map[common.Address]uint64 + nonceCache map[common.Address][]uint64 } func NewAccounts(db *Storage) *Accounts { return &Accounts{ store: db, mux: sync.RWMutex{}, - nonceCache: make(map[common.Address]uint64), + nonceCache: make(map[common.Address][]uint64), } } -func (a *Accounts) Update(tx *gethTypes.Transaction) error { +func (a *Accounts) Update(tx *gethTypes.Transaction, receipt *gethTypes.Receipt) error { a.mux.Lock() defer a.mux.Unlock() @@ -37,50 +38,89 @@ func (a *Accounts) Update(tx *gethTypes.Transaction) error { return err } - nonce, err := a.getNonce(from) + nonce, height, err := a.getNonce(from) if err != nil { return err } - // update nonce - nonce += 1 + // update nonce only if the transaction is new, this makes update idempotent, + // so we can update multiple times with same receipt but nonce won't get increased + if receipt.BlockNumber.Uint64() > height { + nonce += 1 + } else { + return nil + } - err = a.store.set(accountNonceKey, from.Bytes(), uint64Bytes(nonce)) + data := encodeNonce(nonce, receipt.BlockNumber.Uint64()) + err = a.store.set(accountNonceKey, from.Bytes(), data) if err != nil { return err } - a.nonceCache[from] = nonce + a.nonceCache[from] = []uint64{nonce, height} return nil } -func (a *Accounts) getNonce(address common.Address) (uint64, error) { - nonce, ok := a.nonceCache[address] +func (a *Accounts) getNonce(address common.Address) (uint64, uint64, error) { + data, ok := a.nonceCache[address] if ok { // if present in cache return it - return nonce, nil + return data[0], data[1], nil } val, err := a.store.get(accountNonceKey, address.Bytes()) if err != nil { // if no nonce was yet saved for the account the nonce is 0 if errors.Is(err, errs.NotFound) { - return 0, nil + return 0, 0, nil } - return 0, err + return 0, 0, err } - nonce = binary.BigEndian.Uint64(val) - a.nonceCache[address] = nonce - return nonce, nil + nonce, height, err := decodeNonce(val) + if err != nil { + return 0, 0, err + } + + a.nonceCache[address] = []uint64{nonce, height} + return nonce, height, nil } func (a *Accounts) GetNonce(address *common.Address) (uint64, error) { a.mux.RLock() defer a.mux.RUnlock() - return a.getNonce(*address) + nonce, _, err := a.getNonce(*address) + if err != nil { + return 0, fmt.Errorf("failed to get nonce: %w", err) + } + + return nonce, nil } func (a *Accounts) GetBalance(address *common.Address) (*big.Int, error) { panic("not supported") } + +// decodeNonce converts nonce data into nonce and height +func decodeNonce(data []byte) (uint64, uint64, error) { + if len(data) != 16 { + return 0, 0, fmt.Errorf("invalid nonce data") + } + nonce := binary.BigEndian.Uint64(data[:8]) + height := binary.BigEndian.Uint64(data[8:]) + + return nonce, height, nil +} + +// encodeNonce converts nonce and height into nonce data +func encodeNonce(nonce uint64, height uint64) []byte { + payload := make([]byte, 16) + for i, b := range uint64Bytes(nonce) { + payload[i] = b + } + for i, b := range uint64Bytes(height) { + payload[i+8] = b + } + + return payload +} From e91aa4bdb65cecda91136fe7d08f1dbad0062db1 Mon Sep 17 00:00:00 2001 From: Gregor Gololicic Date: Wed, 21 Feb 2024 22:57:45 +0100 Subject: [PATCH 08/40] account idempotent --- services/ingestion/engine.go | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/services/ingestion/engine.go b/services/ingestion/engine.go index 2653d87f2..449f147cf 100644 --- a/services/ingestion/engine.go +++ b/services/ingestion/engine.go @@ -73,12 +73,6 @@ func (e *Engine) Start(ctx context.Context) error { return fmt.Errorf("failed to get latest cadence height: %w", err) } - // todo we increase the latest indexed cadence height by one, but this assumes the last index of data - // was successful, in case of crash we would need to reindex. We do this now because nonces are not - // mapped to heights, which means the update to account is not idempotent, once update to account is - // idempotent we should remove this increment by 1. - latestCadence += 1 - e.log.Info().Uint64("start-cadence-height", latestCadence).Msg("starting ingestion") events, errs, err := e.subscriber.Subscribe(ctx, latestCadence) @@ -207,7 +201,7 @@ func (e *Engine) processTransactionEvent(event cadence.Event) error { return err } - if err := e.accounts.Update(tx); err != nil { + if err := e.accounts.Update(tx, receipt); err != nil { return err } From 88b2cfeb87162aa9576717f09bd7ddb45e805659 Mon Sep 17 00:00:00 2001 From: Gregor Gololicic Date: Wed, 21 Feb 2024 23:01:01 +0100 Subject: [PATCH 09/40] add nonce enc test --- storage/pebble/storage_test.go | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/storage/pebble/storage_test.go b/storage/pebble/storage_test.go index dd61149d2..9653f1394 100644 --- a/storage/pebble/storage_test.go +++ b/storage/pebble/storage_test.go @@ -99,6 +99,18 @@ func TestBlock(t *testing.T) { }) } +func TestAccount(t *testing.T) { + t.Run("encoding decoding nonce data", func(t *testing.T) { + nonce := uint64(10) + height := uint64(20) + raw := encodeNonce(10, 20) + decNonce, decHeight, err := decodeNonce(raw) + require.NoError(t, err) + assert.Equal(t, nonce, decNonce) + assert.Equal(t, height, decHeight) + }) +} + func runDB(name string, t *testing.T, f func(t *testing.T, db *Storage)) { dir := t.TempDir() From 5c4447179a3892c1c26f747a79e080dfd7619abd Mon Sep 17 00:00:00 2001 From: Gregor Gololicic Date: Wed, 21 Feb 2024 23:01:17 +0100 Subject: [PATCH 10/40] fix test --- storage/index_testsuite.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/storage/index_testsuite.go b/storage/index_testsuite.go index 1d85a2c24..5a50960fa 100644 --- a/storage/index_testsuite.go +++ b/storage/index_testsuite.go @@ -303,6 +303,7 @@ func (a *AccountTestSuite) TestNonce() { a.Run("update account and increase nonce", func() { tx := mocks.NewTransaction(0) + rcp := mocks.NewReceipt(1, tx.Hash()) // todo add multiple accounts test from := common.HexToAddress("FACF71692421039876a5BB4F10EF7A439D8ef61E") rawKey := "f6d5333177711e562cabf1f311916196ee6ffc2a07966d9d4628094073bd5442" @@ -316,7 +317,7 @@ func (a *AccountTestSuite) TestNonce() { a.Require().Equal(uint64(0), nonce) for i := 1; i < 5; i++ { - err = a.AccountIndexer.Update(tx) + err = a.AccountIndexer.Update(tx, rcp) a.Require().NoError(err) nonce, err = a.AccountIndexer.GetNonce(&from) From d3e82ccaba2a5dd8a2dc80f22d620b2dc006e1d1 Mon Sep 17 00:00:00 2001 From: Gregor Gololicic Date: Wed, 21 Feb 2024 23:01:34 +0100 Subject: [PATCH 11/40] update mock --- storage/mocks/AccountIndexer.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/storage/mocks/AccountIndexer.go b/storage/mocks/AccountIndexer.go index c5914b030..9ea71e70e 100644 --- a/storage/mocks/AccountIndexer.go +++ b/storage/mocks/AccountIndexer.go @@ -66,13 +66,13 @@ func (_m *AccountIndexer) GetNonce(address *common.Address) (uint64, error) { return r0, r1 } -// Update provides a mock function with given fields: tx -func (_m *AccountIndexer) Update(tx *types.Transaction) error { - ret := _m.Called(tx) +// Update provides a mock function with given fields: tx, receipt +func (_m *AccountIndexer) Update(tx *types.Transaction, receipt *types.Receipt) error { + ret := _m.Called(tx, receipt) var r0 error - if rf, ok := ret.Get(0).(func(*types.Transaction) error); ok { - r0 = rf(tx) + if rf, ok := ret.Get(0).(func(*types.Transaction, *types.Receipt) error); ok { + r0 = rf(tx, receipt) } else { r0 = ret.Error(0) } From 6a88afd3bd090eec129e99444537a0fb1144951c Mon Sep 17 00:00:00 2001 From: Gregor Gololicic Date: Wed, 21 Feb 2024 23:07:06 +0100 Subject: [PATCH 12/40] nit --- services/ingestion/engine.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/ingestion/engine.go b/services/ingestion/engine.go index 449f147cf..2e1b16a48 100644 --- a/services/ingestion/engine.go +++ b/services/ingestion/engine.go @@ -193,7 +193,7 @@ func (e *Engine) processTransactionEvent(event cadence.Event) error { Str("contract-address", receipt.ContractAddress.String()). Int("log-count", len(receipt.Logs)). Str("receipt-tx-hash", receipt.TxHash.String()). - Str("tx hash", tx.Hash().String()). + Str("tx-hash", tx.Hash().String()). Msg("ingesting new transaction executed event") // todo think if we could introduce batching From 9e43c90f88469a25054f0c354bf392a3abdf9028 Mon Sep 17 00:00:00 2001 From: Gregor Gololicic Date: Wed, 21 Feb 2024 23:25:41 +0100 Subject: [PATCH 13/40] replace imports based on network --- api/api.go | 2 +- config/config.go | 23 +++++++++----- integration/e2e_test.go | 2 +- services/requester/requester.go | 54 ++++++++++++++++++++++++++++++--- 4 files changed, 66 insertions(+), 15 deletions(-) diff --git a/api/api.go b/api/api.go index add6fbd86..d776f02bc 100644 --- a/api/api.go +++ b/api/api.go @@ -72,7 +72,7 @@ func NewBlockChainAPI( // wasn't synced up to a block where EIP-155 is enabled, but this behavior caused issues // in CL clients. func (b *BlockChainAPI) ChainId() *hexutil.Big { - return (*hexutil.Big)(b.config.ChainID) + return (*hexutil.Big)(b.config.EVMNetworkID) } // BlockNumber returns the block number of the chain head. diff --git a/config/config.go b/config/config.go index 042379596..7f9a3011a 100644 --- a/config/config.go +++ b/config/config.go @@ -29,8 +29,10 @@ type Config struct { // InitCadenceHeight provides initial heights for Cadence block height // useful only on a cold-start with an empty database InitCadenceHeight uint64 - // ChainID provides the EVM chain ID. - ChainID *big.Int + // EVMNetworkID provides the EVM chain ID. + EVMNetworkID *big.Int + // FlowNetworkID is the Flow network ID that the EVM is hosted on (mainnet, testnet, emulator...) + FlowNetworkID string // Coinbase is EVM address that collects the EVM operator fees collected // when transactions are being submitted. Coinbase common.Address @@ -47,7 +49,7 @@ type Config struct { func FromFlags() (*Config, error) { cfg := &Config{} - var network, coinbase, gas, coa, key string + var evmNetwork, coinbase, gas, coa, key string // parse from flags flag.StringVar(&cfg.DatabaseDir, "database-dir", "./db", "path to the directory for the database") @@ -55,7 +57,8 @@ func FromFlags() (*Config, error) { flag.IntVar(&cfg.RPCPort, "rpc-port", 3000, "port for the RPC API server") flag.StringVar(&cfg.AccessNodeGRPCHost, "access-node-grpc-host", "localhost:3569", "host to the flow access node gRPC API") flag.Uint64Var(&cfg.InitCadenceHeight, "init-cadence-height", EmptyHeight, "init cadence block height from where the event ingestion will start. WARNING: you should only provide this if there are no existing values in the database") - flag.StringVar(&network, "network-id", "testnet", "EVM network ID (testnet, mainnet)") + flag.StringVar(&evmNetwork, "emv-network-id", "testnet", "EVM network ID (testnet, mainnet)") + flag.StringVar(&cfg.FlowNetworkID, "flow-network-id", "testnet", "EVM network ID (emulator, previewnet)") flag.StringVar(&coinbase, "coinbase", "", "coinbase address to use for fee collection") flag.StringVar(&gas, "gas-price", "1", "static gas price used for EVM transactions") flag.StringVar(&coa, "coa-address", "", "Flow address that holds COA account used for submitting transactions") @@ -82,13 +85,17 @@ func FromFlags() (*Config, error) { } cfg.COAKey = pkey - switch network { + switch evmNetwork { case "testnet": - cfg.ChainID = emulator.FlowEVMTestnetChainID + cfg.EVMNetworkID = emulator.FlowEVMTestnetChainID case "mainnet": - cfg.ChainID = emulator.FlowEVMMainnetChainID + cfg.EVMNetworkID = emulator.FlowEVMMainnetChainID default: - return nil, fmt.Errorf("network ID not supported") + return nil, fmt.Errorf("EVM network ID not supported") + } + + if cfg.FlowNetworkID != "previewnet" && cfg.FlowNetworkID != "emulator" { + return nil, fmt.Errorf("flow network ID is invalid, only allowed to set 'emulator' and 'previewnet'") } // todo validate Config values diff --git a/integration/e2e_test.go b/integration/e2e_test.go index c2531d2b2..c5a897ba2 100644 --- a/integration/e2e_test.go +++ b/integration/e2e_test.go @@ -419,7 +419,7 @@ func TestIntegration_API_DeployEvents(t *testing.T) { RPCPort: 3001, RPCHost: "127.0.0.1", InitCadenceHeight: 0, - ChainID: emulator.FlowEVMTestnetChainID, + EVMNetworkID: emulator.FlowEVMTestnetChainID, Coinbase: fundEOAAddress, COAAddress: gwAddress, COAKey: gwKey, diff --git a/services/requester/requester.go b/services/requester/requester.go index c31d30cb4..0583e3807 100644 --- a/services/requester/requester.go +++ b/services/requester/requester.go @@ -11,6 +11,7 @@ import ( "github.com/onflow/flow-go-sdk/crypto" "github.com/rs/zerolog" "math/big" + "strings" "github.com/ethereum/go-ethereum/common" "github.com/onflow/cadence" @@ -63,12 +64,14 @@ type EVM struct { client access.Client address flow.Address signer crypto.Signer + network string // todo change the type to FVM type once the "previewnet" is added } func NewEVM( client access.Client, address flow.Address, signer crypto.Signer, + network string, createCOA bool, logger zerolog.Logger, ) (*EVM, error) { @@ -94,13 +97,18 @@ func NewEVM( address: address, signer: signer, logger: logger, + network: network, } // create COA on the account // todo improve this to first check if coa exists and only if it doesn't create it, if it doesn't and the flag is false return an error if createCOA { // we ignore errors for now since creation of already existing COA resource will fail, which is fine for now - id, err := evm.signAndSend(context.Background(), createCOAScript, cadence.UFix64(coaFundingBalance)) + id, err := evm.signAndSend( + context.Background(), + evm.replaceImports(createCOAScript), + cadence.UFix64(coaFundingBalance), + ) logger.Info().Err(err).Str("id", id.String()).Msg("COA resource auto-created") } @@ -117,8 +125,11 @@ func (e *EVM) SendRawTransaction(ctx context.Context, data []byte) (common.Hash, ) // todo make sure the gas price is not bellow the configured gas price - - flowID, err := e.signAndSend(ctx, runTxScript, cadenceArrayFromBytes(data)) + flowID, err := e.signAndSend( + ctx, + e.replaceImports(runTxScript), + cadenceArrayFromBytes(data), + ) if err != nil { return common.Hash{}, err } @@ -185,7 +196,11 @@ func (e *EVM) GetBalance(ctx context.Context, address common.Address, height uin // todo make sure provided height is used addr := cadenceArrayFromBytes(address.Bytes()).WithType(addressType) - val, err := e.client.ExecuteScriptAtLatestBlock(ctx, getBalanceScript, []cadence.Value{addr}) + val, err := e.client.ExecuteScriptAtLatestBlock( + ctx, + e.replaceImports(getBalanceScript), + []cadence.Value{addr}, + ) if err != nil { return nil, err } @@ -210,7 +225,11 @@ func (e *EVM) Call(ctx context.Context, address common.Address, data []byte) ([] Str("data", string(data)). Msg("call") - value, err := e.client.ExecuteScriptAtLatestBlock(ctx, callScript, []cadence.Value{txData, toAddress}) + value, err := e.client.ExecuteScriptAtLatestBlock( + ctx, + e.replaceImports(callScript), + []cadence.Value{txData, toAddress}, + ) if err != nil { return nil, fmt.Errorf("failed to execute script: %w", err) } @@ -236,6 +255,31 @@ func (e *EVM) getSignerNetworkInfo(ctx context.Context) (int, uint64, error) { return 0, 0, fmt.Errorf("provided account address and signer keys do not match") } +// replaceImports replace the import addresses based on the network +func (e *EVM) replaceImports(script []byte) []byte { + // todo use the FVM configured addresses once the previewnet is added, this should all be replaced once flow-go is updated + addresses := map[string]map[string]string{ + "previewnet": { + "EVM": "0xb6763b4399a888c8", + "FungibleToken": "0xa0225e7000ac82a9", + "FlowToken": "0x4445e7ad11568276", + }, + "emulator": { + "EVM": "0xf8d6e0586b0a20c7", + "FungibleToken": "0xee82856bf20e2aa6", + "FlowToken": "0x0ae53cb6e3f42a79", + }, + } + + s := string(script) + addr := addresses[e.network] + s = strings.ReplaceAll(s, "import EVM", fmt.Sprintf("import EVM from %s", addr["EVM"])) + s = strings.ReplaceAll(s, "import FungibleToken", fmt.Sprintf("import EVM from %s", addr["FungibleToken"])) + s = strings.ReplaceAll(s, "import FlowToken", fmt.Sprintf("import EVM from %s", addr["FlowToken"])) + + return []byte(s) +} + func cadenceArrayFromBytes(input []byte) cadence.Array { values := make([]cadence.Value, 0) for _, element := range input { From d50d522ba50327514b3eb5c915af5fcfe63c03ff Mon Sep 17 00:00:00 2001 From: Gregor Gololicic Date: Wed, 21 Feb 2024 23:27:02 +0100 Subject: [PATCH 14/40] config network --- bootstrap/bootstrap.go | 2 +- config/config.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/bootstrap/bootstrap.go b/bootstrap/bootstrap.go index 2a6eb6567..88ef4e8ad 100644 --- a/bootstrap/bootstrap.go +++ b/bootstrap/bootstrap.go @@ -157,7 +157,7 @@ func startServer( return fmt.Errorf("failed to create a COA signer: %w", err) } - evm, err := requester.NewEVM(client, cfg.COAAddress, signer, true, logger) + evm, err := requester.NewEVM(client, cfg.COAAddress, signer, cfg.FlowNetworkID, true, logger) if err != nil { return fmt.Errorf("failed to create EVM requester: %w", err) } diff --git a/config/config.go b/config/config.go index 7f9a3011a..76d6378c4 100644 --- a/config/config.go +++ b/config/config.go @@ -58,7 +58,7 @@ func FromFlags() (*Config, error) { flag.StringVar(&cfg.AccessNodeGRPCHost, "access-node-grpc-host", "localhost:3569", "host to the flow access node gRPC API") flag.Uint64Var(&cfg.InitCadenceHeight, "init-cadence-height", EmptyHeight, "init cadence block height from where the event ingestion will start. WARNING: you should only provide this if there are no existing values in the database") flag.StringVar(&evmNetwork, "emv-network-id", "testnet", "EVM network ID (testnet, mainnet)") - flag.StringVar(&cfg.FlowNetworkID, "flow-network-id", "testnet", "EVM network ID (emulator, previewnet)") + flag.StringVar(&cfg.FlowNetworkID, "flow-network-id", "emulator", "EVM network ID (emulator, previewnet)") flag.StringVar(&coinbase, "coinbase", "", "coinbase address to use for fee collection") flag.StringVar(&gas, "gas-price", "1", "static gas price used for EVM transactions") flag.StringVar(&coa, "coa-address", "", "Flow address that holds COA account used for submitting transactions") From bd1c44f52caae45798e599f5277a6ec69a871c4b Mon Sep 17 00:00:00 2001 From: Gregor Gololicic Date: Wed, 21 Feb 2024 23:28:09 +0100 Subject: [PATCH 15/40] remove addresses in scripts --- services/requester/cadence/bridged_account_call.cdc | 3 +-- services/requester/cadence/create_bridged_account.cdc | 6 +++--- services/requester/cadence/evm_address_balance.cdc | 2 +- services/requester/cadence/evm_run.cdc | 3 +-- 4 files changed, 6 insertions(+), 8 deletions(-) diff --git a/services/requester/cadence/bridged_account_call.cdc b/services/requester/cadence/bridged_account_call.cdc index ec8c11fc7..344ec6418 100644 --- a/services/requester/cadence/bridged_account_call.cdc +++ b/services/requester/cadence/bridged_account_call.cdc @@ -1,5 +1,4 @@ -// TODO(m-Peter): Use proper address for each network -import EVM from 0xf8d6e0586b0a20c7 +import EVM access(all) fun main(data: [UInt8], contractAddress: [UInt8; 20]): [UInt8] { diff --git a/services/requester/cadence/create_bridged_account.cdc b/services/requester/cadence/create_bridged_account.cdc index e9801119b..11c007ada 100644 --- a/services/requester/cadence/create_bridged_account.cdc +++ b/services/requester/cadence/create_bridged_account.cdc @@ -1,6 +1,6 @@ -import EVM from 0xf8d6e0586b0a20c7 // todo dynamically set -import FungibleToken from 0xee82856bf20e2aa6 -import FlowToken from 0x0ae53cb6e3f42a79 +import EVM +import FungibleToken +import FlowToken transaction(amount: UFix64) { let sentVault: @FlowToken.Vault diff --git a/services/requester/cadence/evm_address_balance.cdc b/services/requester/cadence/evm_address_balance.cdc index 0af3ec9bc..91ca31155 100644 --- a/services/requester/cadence/evm_address_balance.cdc +++ b/services/requester/cadence/evm_address_balance.cdc @@ -1,4 +1,4 @@ -import EVM from 0xf8d6e0586b0a20c7 +import EVM access(all) fun main(addressBytes: [UInt8; 20]): UInt { diff --git a/services/requester/cadence/evm_run.cdc b/services/requester/cadence/evm_run.cdc index b98b12e5e..55d25c1a2 100644 --- a/services/requester/cadence/evm_run.cdc +++ b/services/requester/cadence/evm_run.cdc @@ -1,5 +1,4 @@ -// TODO(m-Peter): Use proper address for each network -import EVM from 0xf8d6e0586b0a20c7 +import EVM transaction(encodedTx: [UInt8]) { let bridgedAccount: &EVM.BridgedAccount From 9a74a9af1be5b0cc9e3519fe661ac9a6ca7f8401 Mon Sep 17 00:00:00 2001 From: Gregor Gololicic Date: Wed, 21 Feb 2024 23:56:17 +0100 Subject: [PATCH 16/40] clean up scripts --- .../cadence/bridged_account_call.cdc | 19 ------------------- services/requester/cadence/call.cdc | 13 +++++++++++++ ...ate_bridged_account.cdc => create_coa.cdc} | 4 ++-- services/requester/cadence/evm_run.cdc | 15 --------------- ...vm_address_balance.cdc => get_balance.cdc} | 0 services/requester/cadence/run.cdc | 14 ++++++++++++++ 6 files changed, 29 insertions(+), 36 deletions(-) delete mode 100644 services/requester/cadence/bridged_account_call.cdc create mode 100644 services/requester/cadence/call.cdc rename services/requester/cadence/{create_bridged_account.cdc => create_coa.cdc} (80%) delete mode 100644 services/requester/cadence/evm_run.cdc rename services/requester/cadence/{evm_address_balance.cdc => get_balance.cdc} (100%) create mode 100644 services/requester/cadence/run.cdc diff --git a/services/requester/cadence/bridged_account_call.cdc b/services/requester/cadence/bridged_account_call.cdc deleted file mode 100644 index 344ec6418..000000000 --- a/services/requester/cadence/bridged_account_call.cdc +++ /dev/null @@ -1,19 +0,0 @@ -import EVM - -access(all) -fun main(data: [UInt8], contractAddress: [UInt8; 20]): [UInt8] { - // TODO(m-Peter): Pass Flow address of bridged account as script argument - let account = getAuthAccount(Address(0xf8d6e0586b0a20c7)) - let bridgedAccount = account.storage.borrow<&EVM.BridgedAccount>( - from: /storage/evm - ) ?? panic("Could not borrow reference to the bridged account!") - - let evmResult = bridgedAccount.call( - to: EVM.EVMAddress(bytes: contractAddress), - data: data, - gasLimit: 300000, // TODO(m-Peter): Maybe also pass this as script argument - value: EVM.Balance(attoflow: 0) - ) - - return evmResult -} diff --git a/services/requester/cadence/call.cdc b/services/requester/cadence/call.cdc new file mode 100644 index 000000000..1edb9cc83 --- /dev/null +++ b/services/requester/cadence/call.cdc @@ -0,0 +1,13 @@ +import EVM + +access(all) +fun main(data: [UInt8], contractAddress: [UInt8; 20]): [UInt8] { + let coa <- EVM.createCadenceOwnedAccount() + + return coa.call( + to: EVM.EVMAddress(bytes: contractAddress), + data: data, + gasLimit: 300000, // TODO(m-Peter): Maybe also pass this as script argument + value: EVM.Balance(attoflow: 0) + ) +} diff --git a/services/requester/cadence/create_bridged_account.cdc b/services/requester/cadence/create_coa.cdc similarity index 80% rename from services/requester/cadence/create_bridged_account.cdc rename to services/requester/cadence/create_coa.cdc index 11c007ada..b58cf3718 100644 --- a/services/requester/cadence/create_bridged_account.cdc +++ b/services/requester/cadence/create_coa.cdc @@ -16,11 +16,11 @@ transaction(amount: UFix64) { } execute { - let account <- EVM.createBridgedAccount() + let account <- EVM.createCadenceOwnedAccount() log(account.address()) account.deposit(from: <-self.sentVault) log(account.balance()) - self.auth.storage.save<@EVM.BridgedAccount>(<-account, to: StoragePath(identifier: "evm")!) + self.auth.storage.save<@EVM.CadenceOwnedAccount>(<-account, to: StoragePath(identifier: "evm")!) } } diff --git a/services/requester/cadence/evm_run.cdc b/services/requester/cadence/evm_run.cdc deleted file mode 100644 index 55d25c1a2..000000000 --- a/services/requester/cadence/evm_run.cdc +++ /dev/null @@ -1,15 +0,0 @@ -import EVM - -transaction(encodedTx: [UInt8]) { - let bridgedAccount: &EVM.BridgedAccount - - prepare(signer: auth(Storage) &Account) { - self.bridgedAccount = signer.storage.borrow<&EVM.BridgedAccount>( - from: /storage/evm - ) ?? panic("Could not borrow reference to the bridged account!") - } - - execute { - EVM.run(tx: encodedTx, coinbase: self.bridgedAccount.address()) - } -} diff --git a/services/requester/cadence/evm_address_balance.cdc b/services/requester/cadence/get_balance.cdc similarity index 100% rename from services/requester/cadence/evm_address_balance.cdc rename to services/requester/cadence/get_balance.cdc diff --git a/services/requester/cadence/run.cdc b/services/requester/cadence/run.cdc new file mode 100644 index 000000000..09907ab7e --- /dev/null +++ b/services/requester/cadence/run.cdc @@ -0,0 +1,14 @@ +import EVM + +transaction(encodedTx: [UInt8]) { + let coa: &EVM.CadenceOwnedAccount + + prepare(signer: auth(Storage) &Account) { + self.coa = signer.storage.borrow<&EVM.CadenceOwnedAccount>(from: /storage/evm) + ?? panic("Could not borrow reference to the bridged account!") + } + + execute { + EVM.run(tx: encodedTx, coinbase: self.coa.address()) + } +} From fd52f34e43b8884ba34bd6954417b258ef4aae77 Mon Sep 17 00:00:00 2001 From: Gregor Gololicic Date: Wed, 21 Feb 2024 23:56:26 +0100 Subject: [PATCH 17/40] return 0 if not init --- api/api.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/api/api.go b/api/api.go index d776f02bc..fa1482c3d 100644 --- a/api/api.go +++ b/api/api.go @@ -79,8 +79,7 @@ func (b *BlockChainAPI) ChainId() *hexutil.Big { func (b *BlockChainAPI) BlockNumber() (hexutil.Uint64, error) { latestBlockHeight, err := b.blocks.LatestEVMHeight() if err != nil { - b.logger.Error().Err(err).Msg("failed to get latest height") - return 0, err + return 0, nil } return hexutil.Uint64(latestBlockHeight), nil From b88f44764d826872d2384d1d7f915d71a51de19a Mon Sep 17 00:00:00 2001 From: Gregor Gololicic Date: Wed, 21 Feb 2024 23:56:31 +0100 Subject: [PATCH 18/40] typo --- config/config.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/config.go b/config/config.go index 76d6378c4..eda36f3cc 100644 --- a/config/config.go +++ b/config/config.go @@ -57,7 +57,7 @@ func FromFlags() (*Config, error) { flag.IntVar(&cfg.RPCPort, "rpc-port", 3000, "port for the RPC API server") flag.StringVar(&cfg.AccessNodeGRPCHost, "access-node-grpc-host", "localhost:3569", "host to the flow access node gRPC API") flag.Uint64Var(&cfg.InitCadenceHeight, "init-cadence-height", EmptyHeight, "init cadence block height from where the event ingestion will start. WARNING: you should only provide this if there are no existing values in the database") - flag.StringVar(&evmNetwork, "emv-network-id", "testnet", "EVM network ID (testnet, mainnet)") + flag.StringVar(&evmNetwork, "evm-network-id", "testnet", "EVM network ID (testnet, mainnet)") flag.StringVar(&cfg.FlowNetworkID, "flow-network-id", "emulator", "EVM network ID (emulator, previewnet)") flag.StringVar(&coinbase, "coinbase", "", "coinbase address to use for fee collection") flag.StringVar(&gas, "gas-price", "1", "static gas price used for EVM transactions") From 08f25e3552f7e7d8c41d511e838be6c2b4f85fe2 Mon Sep 17 00:00:00 2001 From: Gregor Gololicic Date: Wed, 21 Feb 2024 23:57:05 +0100 Subject: [PATCH 19/40] names changes --- services/requester/requester.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/services/requester/requester.go b/services/requester/requester.go index 0583e3807..a582a08bd 100644 --- a/services/requester/requester.go +++ b/services/requester/requester.go @@ -19,16 +19,16 @@ import ( ) var ( - //go:embed cadence/bridged_account_call.cdc + //go:embed cadence/call.cdc callScript []byte - //go:embed cadence/evm_run.cdc + //go:embed cadence/run.cdc runTxScript []byte - //go:embed cadence/evm_address_balance.cdc + //go:embed cadence/get_balance.cdc getBalanceScript []byte - //go:embed cadence/create_bridged_account.cdc + //go:embed cadence/create_coa.cdc createCOAScript []byte byteArrayType = cadence.NewVariableSizedArrayType(cadence.UInt8Type) From 9a4c5b8c23363bfb40d2561681cde9fff330a419 Mon Sep 17 00:00:00 2001 From: Gregor Gololicic Date: Thu, 22 Feb 2024 00:05:27 +0100 Subject: [PATCH 20/40] fix import replace --- services/requester/requester.go | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/services/requester/requester.go b/services/requester/requester.go index a582a08bd..3ee0f7fa3 100644 --- a/services/requester/requester.go +++ b/services/requester/requester.go @@ -272,10 +272,13 @@ func (e *EVM) replaceImports(script []byte) []byte { } s := string(script) - addr := addresses[e.network] - s = strings.ReplaceAll(s, "import EVM", fmt.Sprintf("import EVM from %s", addr["EVM"])) - s = strings.ReplaceAll(s, "import FungibleToken", fmt.Sprintf("import EVM from %s", addr["FungibleToken"])) - s = strings.ReplaceAll(s, "import FlowToken", fmt.Sprintf("import EVM from %s", addr["FlowToken"])) + // iterate over all the import name and address pairs and replace them in script + for imp, addr := range addresses[e.network] { + s = strings.ReplaceAll(s, + fmt.Sprintf("import %s", imp), + fmt.Sprintf("import %s from %s", imp, addr), + ) + } return []byte(s) } From b6373d918006b927200aa08395edd0003d0edc46 Mon Sep 17 00:00:00 2001 From: Alex Ni <12097569+nialexsan@users.noreply.github.com> Date: Wed, 21 Feb 2024 18:40:15 -0500 Subject: [PATCH 21/40] build and deploy --- .github/workflows/deploy.yml | 4 ++-- Dockerfile | 8 +------- go.mod | 8 ++------ go.sum | 14 +++----------- scripts/run.sh | 29 +++++++---------------------- 5 files changed, 15 insertions(+), 48 deletions(-) mode change 100644 => 100755 scripts/run.sh diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 018f25416..22cf39990 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -2,8 +2,8 @@ name: "Deploy EVM Gateway to Cloud Run" on: push: - branches: - - main + # branches: + # - main env: DOCKER_IMAGE_URL: ${{ vars.GCP_DOCKER_IMAGE_URL }}:${{ github.sha }} diff --git a/Dockerfile b/Dockerfile index cbf917ffc..665a40127 100644 --- a/Dockerfile +++ b/Dockerfile @@ -8,20 +8,14 @@ COPY go.* ./ COPY . ./ RUN go mod download RUN go mod verify -RUN CGO_ENABLED=0 go build -o evm-gateway ./cmd/server/main.go +RUN CGO_ENABLED=0 go build -o evm-gateway ./cmd/main/main.go RUN chmod a+x evm-gateway RUN chmod a+x ./scripts/run.sh -RUN wget https://github.com/m-Peter/flow-cli-custom-builds/raw/main/flow-x86_64-linux- -RUN chmod a+x flow-x86_64-linux- - # RUN APP FROM debian:latest WORKDIR /flow-evm-gateway COPY --from=builder /flow-evm-gateway/evm-gateway /flow-evm-gateway/evm-gateway -COPY --from=builder /flow-evm-gateway/flow-x86_64-linux- /flow-evm-gateway/flow-x86_64-linux- -COPY --from=builder /flow-evm-gateway/flow.json /flow-evm-gateway/flow.json -COPY --from=builder /flow-evm-gateway/api/cadence/transactions/create_bridged_account.cdc /flow-evm-gateway/create_bridged_account.cdc COPY --from=builder /flow-evm-gateway/scripts/run.sh /flow-evm-gateway/run.sh EXPOSE 8545 CMD cd /flow-evm-gateway && ./run.sh diff --git a/go.mod b/go.mod index 27da3a4ca..ffafe9ab0 100644 --- a/go.mod +++ b/go.mod @@ -24,7 +24,6 @@ require ( github.com/cenkalti/backoff/v4 v4.2.1 // indirect github.com/cespare/xxhash v1.1.0 // indirect github.com/cespare/xxhash/v2 v2.2.0 // indirect - github.com/chigopher/pathlib v0.12.0 // indirect github.com/cockroachdb/errors v1.9.1 // indirect github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b // indirect github.com/cockroachdb/redact v1.1.3 // indirect @@ -81,7 +80,6 @@ require ( github.com/ipfs/go-metrics-interface v0.0.1 // indirect github.com/jackpal/go-nat-pmp v1.0.2 // indirect github.com/jbenet/goprocess v0.1.4 // indirect - github.com/jinzhu/copier v0.3.5 // indirect github.com/k0kubun/pp v3.0.1+incompatible // indirect github.com/kevinburke/go-bindata v3.24.0+incompatible // indirect github.com/klauspost/compress v1.16.5 // indirect @@ -97,7 +95,6 @@ require ( github.com/mattn/go-runewidth v0.0.15 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect github.com/minio/sha256-simd v1.0.1 // indirect - github.com/mitchellh/go-homedir v1.1.0 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/mmcloughlin/addchain v0.4.0 // indirect github.com/mr-tron/base58 v1.2.0 // indirect @@ -151,7 +148,6 @@ require ( github.com/tklauser/numcpus v0.6.1 // indirect github.com/turbolent/prettier v0.0.0-20220320183459-661cc755135d // indirect github.com/tyler-smith/go-bip39 v1.1.0 // indirect - github.com/vektra/mockery/v2 v2.21.4 // indirect github.com/vmihailenco/msgpack v4.0.4+incompatible // indirect github.com/vmihailenco/msgpack/v4 v4.3.11 // indirect github.com/vmihailenco/tagparser v0.1.1 // indirect @@ -174,7 +170,6 @@ require ( golang.org/x/net v0.19.0 // indirect golang.org/x/sync v0.5.0 // indirect golang.org/x/sys v0.15.0 // indirect - golang.org/x/term v0.15.0 // indirect golang.org/x/text v0.14.0 // indirect golang.org/x/tools v0.16.1 // indirect golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 // indirect @@ -186,8 +181,9 @@ require ( google.golang.org/grpc v1.59.0 // indirect google.golang.org/protobuf v1.31.0 // indirect gopkg.in/ini.v1 v1.67.0 // indirect - gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect lukechampine.com/blake3 v1.2.1 // indirect rsc.io/tmplfunc v0.0.3 // indirect ) + +replace github.com/onflow/crypto => github.com/onflow/crypto v0.24.9 diff --git a/go.sum b/go.sum index cfdb94fff..0c8b259f5 100644 --- a/go.sum +++ b/go.sum @@ -1065,8 +1065,6 @@ github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XL github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/chigopher/pathlib v0.12.0 h1:1GM7fN/IwXXmOHbd1jkMqHD2wUhYqUvafgxTwmLT/q8= -github.com/chigopher/pathlib v0.12.0/go.mod h1:EJ5UtJ/sK8Nt6q3VWN+EwZLZ3g0afJiG8NegYiQQ/gQ= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/logex v1.2.0/go.mod h1:9+9sk7u7pGNWYMkh0hdiL++6OeibzJccyQU4p4MedaY= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= @@ -1580,8 +1578,6 @@ github.com/jbenet/goprocess v0.1.4 h1:DRGOFReOMqqDNXwW70QkacFW0YN9QnwLV0Vqk+3oU0 github.com/jbenet/goprocess v0.1.4/go.mod h1:5yspPrukOVuOLORacaBi858NqyClJPQxYZlqdZVfqY4= github.com/jedisct1/go-minisign v0.0.0-20190909160543-45766022959e/go.mod h1:G1CVv03EnqU1wYL2dFwXxW2An0az9JTl/ZsqXQeBlkU= github.com/jedisct1/go-minisign v0.0.0-20230811132847-661be99b8267/go.mod h1:h1nSAbGFqGVzn6Jyl1R/iCcBUHN4g+gW1u9CoBTrb9E= -github.com/jinzhu/copier v0.3.5 h1:GlvfUwHk62RokgqVNvYsku0TATCF7bAHVwEXoBh3iJg= -github.com/jinzhu/copier v0.3.5/go.mod h1:DfbEm0FYsaqBcKcFuvmOZb218JkPGtvSHsKg8S8hyyg= github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= @@ -1745,7 +1741,6 @@ github.com/minio/sha256-simd v0.1.1-0.20190913151208-6de447530771/go.mod h1:B5e1 github.com/minio/sha256-simd v1.0.1 h1:6kaan5IFmwTNynnKKpDHe6FWHohJOHhCPchzK49dzMM= github.com/minio/sha256-simd v1.0.1/go.mod h1:Pz6AKMiUdngCLpeTL/RJY1M9rUuPMYujV5xJjtbRSN8= github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db/go.mod h1:l0dey0ia/Uv7NcFFVbCLtqEBQbrT4OCwCSKTEv6enCw= -github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= @@ -1812,8 +1807,8 @@ github.com/onflow/atree v0.6.1-0.20230711151834-86040b30171f/go.mod h1:xvP61FoOs github.com/onflow/cadence v1.0.0-M3/go.mod h1:odXGZZ/wGNA5mwT8bC9v8u8EXACHllB2ABSZK65TGL8= github.com/onflow/cadence v1.0.0-M7 h1:hb4LK9lUbFdnDdfU8oqJGSVJtmkOzxwKomhmDcP/Faw= github.com/onflow/cadence v1.0.0-M7/go.mod h1:a4mccDU90hmuxCLUFzs9J/ANG/rYbFa36h4Z0bBAqNU= -github.com/onflow/crypto v0.25.0 h1:BeWbLsh3ZD13Ej+Uky6kg1PL1ZIVBDVX+2MVBNwqddg= -github.com/onflow/crypto v0.25.0/go.mod h1:C8FbaX0x8y+FxWjbkHy0Q4EASCDR9bSPWZqlpCLYyVI= +github.com/onflow/crypto v0.24.9 h1:jYP1qdwid0qCineFzBFlxBchg710A7RuSWpTqxaOdog= +github.com/onflow/crypto v0.24.9/go.mod h1:J/V7IEVaqjDajvF8K0B/SJPJDgAOP2G+LVLeb0hgmbg= github.com/onflow/flow-core-contracts/lib/go/contracts v0.15.2-0.20240206003101-928bf99024d7 h1:OI/4F2NK/X/4x3dTUFFDGtuOsSa9pX+jjBeSEcBrY/M= github.com/onflow/flow-core-contracts/lib/go/contracts v0.15.2-0.20240206003101-928bf99024d7/go.mod h1:GK+Ik1K3L3v8xmHmRQv5yxJz81lYhdYSNm0PQ63Xrws= github.com/onflow/flow-core-contracts/lib/go/templates v0.15.2-0.20240206003101-928bf99024d7 h1:WAx8ftVz1BeXiKvQ9gLKEf1J3NBWK26Pbczd0iH4C6I= @@ -1970,7 +1965,6 @@ github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0b github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= github.com/spf13/afero v1.3.3/go.mod h1:5KUK8ByomD5Ti5Artl0RtHeI5pTF7MIDuXL3yY520V4= -github.com/spf13/afero v1.4.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= github.com/spf13/afero v1.9.2/go.mod h1:iUV7ddyEEZPO5gA3zD4fJt6iStLlL+Lg4m2cihcDf8Y= github.com/spf13/afero v1.10.0 h1:EaGW2JJh15aKOejeuJ+wpFSHnbd7GE6Wvp3TsNhb6LY= @@ -2020,6 +2014,7 @@ github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXl github.com/subosito/gotenv v1.4.2 h1:X1TuBLAMDFbaTAChgCBLu3DU3UPyELpnF2jjJ2cz/S8= github.com/subosito/gotenv v1.4.2/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0= github.com/supranational/blst v0.3.8-0.20220526154634-513d2456b344/go.mod h1:jZJtfjgudtNl4en1tzwPIV3KjUnQUvG3/j+w+fVonLw= +github.com/supranational/blst v0.3.10/go.mod h1:jZJtfjgudtNl4en1tzwPIV3KjUnQUvG3/j+w+fVonLw= github.com/supranational/blst v0.3.11 h1:LyU6FolezeWAhvQk0k6O/d49jqgO52MSDDfYgbeoEm4= github.com/supranational/blst v0.3.11/go.mod h1:jZJtfjgudtNl4en1tzwPIV3KjUnQUvG3/j+w+fVonLw= github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 h1:epCh84lMvA70Z7CTTCmYQn2CKbY8j86K7/FAIr141uY= @@ -2055,8 +2050,6 @@ github.com/valyala/fasthttp v1.6.0/go.mod h1:FstJa9V+Pj9vQ7OJie2qMHdwemEDaDiSdBn github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8= github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a/go.mod h1:v3UYOV9WzVtRmSR+PDvWpU/qWl4Wa5LApYYX4ZtKbio= -github.com/vektra/mockery/v2 v2.21.4 h1:QInaL4ma4Bz/cVipKeBLggB5XyPenpksiz8sJpgBZSE= -github.com/vektra/mockery/v2 v2.21.4/go.mod h1:le9nnD50TkeV8QGwrkza7v/xd3DA7YbSZYBEyiuAlsI= github.com/vmihailenco/msgpack v4.0.4+incompatible h1:dSLoQfGFAo3F6OoNhwUmLwVgaUXK79GlxNBwueZn0xI= github.com/vmihailenco/msgpack v4.0.4+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk= github.com/vmihailenco/msgpack/v4 v4.3.11 h1:Q47CePddpNGNhk4GCnAx9DDtASi2rasatE0cd26cZoE= @@ -2531,7 +2524,6 @@ golang.org/x/term v0.10.0/go.mod h1:lpqdcUyK/oCiQxvxVrppt5ggO2KCZ5QblwqPnfZ6d5o= golang.org/x/term v0.11.0/go.mod h1:zC9APTIj3jG3FdV/Ons+XE1riIZXG4aZ4GTHiPZJPIU= golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU= golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U= -golang.org/x/term v0.15.0 h1:y/Oo/a/q3IXu26lQgl04j/gjuBDOBlx7X6Om1j2CPW4= golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= diff --git a/scripts/run.sh b/scripts/run.sh old mode 100644 new mode 100755 index 6d27a091b..47578c7a7 --- a/scripts/run.sh +++ b/scripts/run.sh @@ -1,23 +1,8 @@ #!/bin/bash - -# Start the first process & redirect output to a temporary file -./flow-x86_64-linux- emulator --evm-enabled > temp_output.txt & - -# PID of the first process -FIRST_PROCESS_PID=$! - -# Monitor the temporary file for a specific output -PATTERN="3569" -while ! grep -q "$PATTERN" temp_output.txt; do - sleep 1 -done - -# Once the pattern is found, you can kill the first process if needed -# kill $FIRST_PROCESS_PID - -# Run the second process - -./flow-x86_64-linux- transactions send /flow-evm-gateway/create_bridged_account.cdc 1500.0 --network=emulator --signer=emulator-account && ./evm-gateway - -# Clean up temporary file -rm temp_output.txt +./evm-gateway --access-node-grpc-host access-001.previewnet1.nodes.onflow.org:9000 \ + --init-cadence-height 93680 \ + --flow-network-id previewnet \ + --coinbase FACF71692421039876a5BB4F10EF7A439D8ef61E \ + --coa-address 0xa8a7a61c869b028a \ + --coa-key 290139481555cd366e1bc594c2981941af29e8ea7fbc4e21796ff62db415df1c \ + --coa-resource-create From 9b93bb873277ea6c3cdc8ab17ef34ccd5391a252 Mon Sep 17 00:00:00 2001 From: Alex Ni <12097569+nialexsan@users.noreply.github.com> Date: Wed, 21 Feb 2024 18:48:59 -0500 Subject: [PATCH 22/40] fix port --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 665a40127..d8f48fc3e 100644 --- a/Dockerfile +++ b/Dockerfile @@ -17,5 +17,5 @@ FROM debian:latest WORKDIR /flow-evm-gateway COPY --from=builder /flow-evm-gateway/evm-gateway /flow-evm-gateway/evm-gateway COPY --from=builder /flow-evm-gateway/scripts/run.sh /flow-evm-gateway/run.sh -EXPOSE 8545 +EXPOSE 3000 CMD cd /flow-evm-gateway && ./run.sh From 7dc98be27f7c5a251b2a6fadbaf72ff36835263e Mon Sep 17 00:00:00 2001 From: Alex Ni <12097569+nialexsan@users.noreply.github.com> Date: Wed, 21 Feb 2024 18:57:12 -0500 Subject: [PATCH 23/40] fix host --- config/config.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/config/config.go b/config/config.go index eda36f3cc..b23c49360 100644 --- a/config/config.go +++ b/config/config.go @@ -3,12 +3,13 @@ package config import ( "flag" "fmt" + "math/big" + "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/math" "github.com/onflow/flow-go-sdk" "github.com/onflow/flow-go-sdk/crypto" "github.com/onflow/flow-go/fvm/evm/emulator" - "math/big" ) const ( @@ -53,7 +54,7 @@ func FromFlags() (*Config, error) { // parse from flags flag.StringVar(&cfg.DatabaseDir, "database-dir", "./db", "path to the directory for the database") - flag.StringVar(&cfg.RPCHost, "rpc-host", "localhost", "host for the RPC API server") + flag.StringVar(&cfg.RPCHost, "rpc-host", "", "host for the RPC API server") flag.IntVar(&cfg.RPCPort, "rpc-port", 3000, "port for the RPC API server") flag.StringVar(&cfg.AccessNodeGRPCHost, "access-node-grpc-host", "localhost:3569", "host to the flow access node gRPC API") flag.Uint64Var(&cfg.InitCadenceHeight, "init-cadence-height", EmptyHeight, "init cadence block height from where the event ingestion will start. WARNING: you should only provide this if there are no existing values in the database") From fd26c59812975282953a958f33712e533407eeed Mon Sep 17 00:00:00 2001 From: Alex Ni <12097569+nialexsan@users.noreply.github.com> Date: Thu, 22 Feb 2024 08:03:23 -0500 Subject: [PATCH 24/40] restore branches --- .github/workflows/deploy.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 22cf39990..3e53c0b04 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -2,8 +2,9 @@ name: "Deploy EVM Gateway to Cloud Run" on: push: - # branches: - # - main + branches: + - main + - gregor/index/previewnet-testing-fixes env: DOCKER_IMAGE_URL: ${{ vars.GCP_DOCKER_IMAGE_URL }}:${{ github.sha }} From a43380fd4971889f2369a7c4cf0758ed418d8fb6 Mon Sep 17 00:00:00 2001 From: Gregor Gololicic Date: Thu, 22 Feb 2024 15:06:20 +0100 Subject: [PATCH 25/40] update flow-go --- go.mod | 2 +- go.sum | 4 ++-- integration/go.mod | 2 +- integration/go.sum | 1 + 4 files changed, 5 insertions(+), 4 deletions(-) diff --git a/go.mod b/go.mod index ffafe9ab0..acc26e77a 100644 --- a/go.mod +++ b/go.mod @@ -6,7 +6,7 @@ require ( github.com/cockroachdb/pebble v0.0.0-20230928194634-aa077af62593 github.com/ethereum/go-ethereum v1.13.5 github.com/onflow/cadence v1.0.0-M7 - github.com/onflow/flow-go v0.33.2-0.20240220142056-d1573c75f26c + github.com/onflow/flow-go v0.34.0-crescendo-preview.1 github.com/onflow/flow-go-sdk v1.0.0-M4 github.com/rs/zerolog v1.31.0 github.com/stretchr/testify v1.8.4 diff --git a/go.sum b/go.sum index 0c8b259f5..19f627e29 100644 --- a/go.sum +++ b/go.sum @@ -1815,8 +1815,8 @@ github.com/onflow/flow-core-contracts/lib/go/templates v0.15.2-0.20240206003101- github.com/onflow/flow-core-contracts/lib/go/templates v0.15.2-0.20240206003101-928bf99024d7/go.mod h1:MZ2j5YVTQiSE0B99zuaYhxvGG5GcvimWpQK1Fw/1QBg= github.com/onflow/flow-ft/lib/go/contracts v0.7.1-0.20240205224107-320aa3cf09e0 h1:u6/YcUvO8jU0f3Evb/6agzXqeOo+VbL2a3mmj/5ifRs= github.com/onflow/flow-ft/lib/go/contracts v0.7.1-0.20240205224107-320aa3cf09e0/go.mod h1:PwsL8fC81cjnUnTfmyL/HOIyHnyaw/JA474Wfj2tl6A= -github.com/onflow/flow-go v0.33.2-0.20240220142056-d1573c75f26c h1:CTZt67woYFum9JkBlCUI8bRh3hUElkzp5HveP3Q6/aE= -github.com/onflow/flow-go v0.33.2-0.20240220142056-d1573c75f26c/go.mod h1:ClmIYh0WtY5Ait0en/F4p/Ha9s3DyFryu7AdpN0aoM0= +github.com/onflow/flow-go v0.34.0-crescendo-preview.1 h1:fjl5hzpgxQWqCtpAohtdIV5enF9L/Oh407HpeS4vjnk= +github.com/onflow/flow-go v0.34.0-crescendo-preview.1/go.mod h1:ClmIYh0WtY5Ait0en/F4p/Ha9s3DyFryu7AdpN0aoM0= github.com/onflow/flow-go-sdk v1.0.0-M1/go.mod h1:TDW0MNuCs4SvqYRUzkbRnRmHQL1h4X8wURsCw9P9beo= github.com/onflow/flow-go-sdk v1.0.0-M4 h1:dGtgZvaIfBR5/9I9nm5nLm69V5pLcSz9vXmzCb1lyzM= github.com/onflow/flow-go-sdk v1.0.0-M4/go.mod h1:HIGOKJVR47QNs81sPHmZDVcLr+syNkmbgEMLQGDmmEo= diff --git a/integration/go.mod b/integration/go.mod index f07f70b11..43915df1e 100644 --- a/integration/go.mod +++ b/integration/go.mod @@ -8,7 +8,7 @@ require ( github.com/onflow/cadence v1.0.0-M7 github.com/onflow/flow-emulator v1.0.0-M5 github.com/onflow/flow-evm-gateway v0.0.0-20240201154855-4d4d3d3f19c7 - github.com/onflow/flow-go v0.33.2-0.20240220142056-d1573c75f26c + github.com/onflow/flow-go v0.34.0-crescendo-preview.1 github.com/onflow/flow-go-sdk v1.0.0-M4 github.com/rs/zerolog v1.31.0 github.com/stretchr/testify v1.8.4 diff --git a/integration/go.sum b/integration/go.sum index 6ed304e9c..7a2a72e93 100644 --- a/integration/go.sum +++ b/integration/go.sum @@ -2043,6 +2043,7 @@ github.com/onflow/flow-ft/lib/go/contracts v0.7.1-0.20240205224107-320aa3cf09e0 github.com/onflow/flow-ft/lib/go/contracts v0.7.1-0.20240205224107-320aa3cf09e0/go.mod h1:PwsL8fC81cjnUnTfmyL/HOIyHnyaw/JA474Wfj2tl6A= github.com/onflow/flow-go v0.33.2-0.20240220142056-d1573c75f26c h1:CTZt67woYFum9JkBlCUI8bRh3hUElkzp5HveP3Q6/aE= github.com/onflow/flow-go v0.33.2-0.20240220142056-d1573c75f26c/go.mod h1:ClmIYh0WtY5Ait0en/F4p/Ha9s3DyFryu7AdpN0aoM0= +github.com/onflow/flow-go v0.34.0-crescendo-preview.1/go.mod h1:ClmIYh0WtY5Ait0en/F4p/Ha9s3DyFryu7AdpN0aoM0= github.com/onflow/flow-go-sdk v1.0.0-M1/go.mod h1:TDW0MNuCs4SvqYRUzkbRnRmHQL1h4X8wURsCw9P9beo= github.com/onflow/flow-go-sdk v1.0.0-M4 h1:dGtgZvaIfBR5/9I9nm5nLm69V5pLcSz9vXmzCb1lyzM= github.com/onflow/flow-go-sdk v1.0.0-M4/go.mod h1:HIGOKJVR47QNs81sPHmZDVcLr+syNkmbgEMLQGDmmEo= From c2cc5335b77d0b319e67c901917e8f16525f3ce2 Mon Sep 17 00:00:00 2001 From: Gregor Gololicic Date: Thu, 22 Feb 2024 15:07:50 +0100 Subject: [PATCH 26/40] update flow-go --- integration/go.mod | 2 +- integration/go.sum | 7 +++---- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/integration/go.mod b/integration/go.mod index 43915df1e..c94122e45 100644 --- a/integration/go.mod +++ b/integration/go.mod @@ -6,7 +6,7 @@ require ( github.com/ethereum/go-ethereum v1.13.5 github.com/goccy/go-json v0.10.2 github.com/onflow/cadence v1.0.0-M7 - github.com/onflow/flow-emulator v1.0.0-M5 + github.com/onflow/flow-emulator v1.0.0-M6 github.com/onflow/flow-evm-gateway v0.0.0-20240201154855-4d4d3d3f19c7 github.com/onflow/flow-go v0.34.0-crescendo-preview.1 github.com/onflow/flow-go-sdk v1.0.0-M4 diff --git a/integration/go.sum b/integration/go.sum index 7a2a72e93..222370922 100644 --- a/integration/go.sum +++ b/integration/go.sum @@ -2037,12 +2037,11 @@ github.com/onflow/flow-core-contracts/lib/go/contracts v0.15.2-0.20240206003101- github.com/onflow/flow-core-contracts/lib/go/contracts v0.15.2-0.20240206003101-928bf99024d7/go.mod h1:GK+Ik1K3L3v8xmHmRQv5yxJz81lYhdYSNm0PQ63Xrws= github.com/onflow/flow-core-contracts/lib/go/templates v0.15.2-0.20240206003101-928bf99024d7 h1:WAx8ftVz1BeXiKvQ9gLKEf1J3NBWK26Pbczd0iH4C6I= github.com/onflow/flow-core-contracts/lib/go/templates v0.15.2-0.20240206003101-928bf99024d7/go.mod h1:MZ2j5YVTQiSE0B99zuaYhxvGG5GcvimWpQK1Fw/1QBg= -github.com/onflow/flow-emulator v1.0.0-M5 h1:yPx6je/ahdYvd4b80bMXJ0AT4k2eogEGsQCOI+83V2I= -github.com/onflow/flow-emulator v1.0.0-M5/go.mod h1:CBp/YrgR2PeOdqhQMbfAiznGayNoJuFQTfLV9EJFtEc= +github.com/onflow/flow-emulator v1.0.0-M6 h1:UQpDbaBVs/4yx6zDcnqtsVhQVCNDeW5DnrpduMr7utI= +github.com/onflow/flow-emulator v1.0.0-M6/go.mod h1:8B/xTcOkp9JTdBYversiuDcEkcjI+f9NK/mJPVQ/3tQ= github.com/onflow/flow-ft/lib/go/contracts v0.7.1-0.20240205224107-320aa3cf09e0 h1:u6/YcUvO8jU0f3Evb/6agzXqeOo+VbL2a3mmj/5ifRs= github.com/onflow/flow-ft/lib/go/contracts v0.7.1-0.20240205224107-320aa3cf09e0/go.mod h1:PwsL8fC81cjnUnTfmyL/HOIyHnyaw/JA474Wfj2tl6A= -github.com/onflow/flow-go v0.33.2-0.20240220142056-d1573c75f26c h1:CTZt67woYFum9JkBlCUI8bRh3hUElkzp5HveP3Q6/aE= -github.com/onflow/flow-go v0.33.2-0.20240220142056-d1573c75f26c/go.mod h1:ClmIYh0WtY5Ait0en/F4p/Ha9s3DyFryu7AdpN0aoM0= +github.com/onflow/flow-go v0.34.0-crescendo-preview.1 h1:fjl5hzpgxQWqCtpAohtdIV5enF9L/Oh407HpeS4vjnk= github.com/onflow/flow-go v0.34.0-crescendo-preview.1/go.mod h1:ClmIYh0WtY5Ait0en/F4p/Ha9s3DyFryu7AdpN0aoM0= github.com/onflow/flow-go-sdk v1.0.0-M1/go.mod h1:TDW0MNuCs4SvqYRUzkbRnRmHQL1h4X8wURsCw9P9beo= github.com/onflow/flow-go-sdk v1.0.0-M4 h1:dGtgZvaIfBR5/9I9nm5nLm69V5pLcSz9vXmzCb1lyzM= From dbf0e5a70d45565a23cfa1a194725c5b8a03bccd Mon Sep 17 00:00:00 2001 From: Gregor Gololicic Date: Thu, 22 Feb 2024 15:08:38 +0100 Subject: [PATCH 27/40] ignore flow.json --- .gitignore | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 0e1c7794c..b0db85a54 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ integration/db-test -db \ No newline at end of file +db +flow.json \ No newline at end of file From f49dde5ffca5d4fff2eee36b1491c51aea286b4e Mon Sep 17 00:00:00 2001 From: Gregor Gololicic Date: Thu, 22 Feb 2024 15:09:55 +0100 Subject: [PATCH 28/40] not needed --- flow.json | 18 ------------------ 1 file changed, 18 deletions(-) delete mode 100644 flow.json diff --git a/flow.json b/flow.json deleted file mode 100644 index 42683fe87..000000000 --- a/flow.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "contracts": {}, - "networks": { - "emulator": "127.0.0.1:3569", - "testing": "127.0.0.1:3569" - }, - "accounts": { - "emulator-account": { - "address": "0xf8d6e0586b0a20c7", - "key": "2619878f0e2ff438d17835c2a4561cb87b4d24d72d12ec34569acd0dd4af7c21" - } - }, - "deployments": { - "emulator": { - "emulator-account": [] - } - } -} \ No newline at end of file From 731dd593902371f84ddd09e9b91b4006f80efb5b Mon Sep 17 00:00:00 2001 From: Gregor Gololicic Date: Thu, 22 Feb 2024 15:28:31 +0100 Subject: [PATCH 29/40] loss of resource --- services/requester/cadence/call.cdc | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/services/requester/cadence/call.cdc b/services/requester/cadence/call.cdc index 1edb9cc83..58139a860 100644 --- a/services/requester/cadence/call.cdc +++ b/services/requester/cadence/call.cdc @@ -4,10 +4,12 @@ access(all) fun main(data: [UInt8], contractAddress: [UInt8; 20]): [UInt8] { let coa <- EVM.createCadenceOwnedAccount() - return coa.call( + let res = coa.call( to: EVM.EVMAddress(bytes: contractAddress), data: data, gasLimit: 300000, // TODO(m-Peter): Maybe also pass this as script argument value: EVM.Balance(attoflow: 0) ) + destroy coa + return res } From e62b9193393b9fcd292c11034af3955b2b3c61c6 Mon Sep 17 00:00:00 2001 From: Gregor Gololicic Date: Thu, 22 Feb 2024 15:41:41 +0100 Subject: [PATCH 30/40] replace call address and gas --- services/requester/cadence/call.cdc | 11 ++++++----- services/requester/requester.go | 30 +++++++++++++++++++---------- 2 files changed, 26 insertions(+), 15 deletions(-) diff --git a/services/requester/cadence/call.cdc b/services/requester/cadence/call.cdc index 58139a860..4538f231b 100644 --- a/services/requester/cadence/call.cdc +++ b/services/requester/cadence/call.cdc @@ -2,14 +2,15 @@ import EVM access(all) fun main(data: [UInt8], contractAddress: [UInt8; 20]): [UInt8] { - let coa <- EVM.createCadenceOwnedAccount() + let account = getAuthAccount(Address(0xCOA)) - let res = coa.call( + let coa = account.storage.borrow<&EVM.CadenceOwnedAccount>(from: /storage/evm) + ?? panic("Could not borrow reference to the COA!") + + return coa.call( to: EVM.EVMAddress(bytes: contractAddress), data: data, - gasLimit: 300000, // TODO(m-Peter): Maybe also pass this as script argument + gasLimit: 0xGAS, value: EVM.Balance(attoflow: 0) ) - destroy coa - return res } diff --git a/services/requester/requester.go b/services/requester/requester.go index 3ee0f7fa3..e418fc2ba 100644 --- a/services/requester/requester.go +++ b/services/requester/requester.go @@ -106,7 +106,7 @@ func NewEVM( // we ignore errors for now since creation of already existing COA resource will fail, which is fine for now id, err := evm.signAndSend( context.Background(), - evm.replaceImports(createCOAScript), + evm.replaceAddresses(createCOAScript), cadence.UFix64(coaFundingBalance), ) logger.Info().Err(err).Str("id", id.String()).Msg("COA resource auto-created") @@ -127,7 +127,7 @@ func (e *EVM) SendRawTransaction(ctx context.Context, data []byte) (common.Hash, // todo make sure the gas price is not bellow the configured gas price flowID, err := e.signAndSend( ctx, - e.replaceImports(runTxScript), + e.replaceAddresses(runTxScript), cadenceArrayFromBytes(data), ) if err != nil { @@ -198,7 +198,7 @@ func (e *EVM) GetBalance(ctx context.Context, address common.Address, height uin val, err := e.client.ExecuteScriptAtLatestBlock( ctx, - e.replaceImports(getBalanceScript), + e.replaceAddresses(getBalanceScript), []cadence.Value{addr}, ) if err != nil { @@ -225,11 +225,18 @@ func (e *EVM) Call(ctx context.Context, address common.Address, data []byte) ([] Str("data", string(data)). Msg("call") - value, err := e.client.ExecuteScriptAtLatestBlock( - ctx, - e.replaceImports(callScript), - []cadence.Value{txData, toAddress}, - ) + tx := &types.Transaction{} + if err := tx.UnmarshalBinary(data); err != nil { + return nil, fmt.Errorf("failed to decode tx in call: %w", err) + } + + // replace gas limit with the one set on tx + script := strings.ReplaceAll(string(callScript), "0xGAS", fmt.Sprintf("%d", tx.Gas())) + + // replace all the addresses + replaced := e.replaceAddresses([]byte(script)) + + value, err := e.client.ExecuteScriptAtLatestBlock(ctx, replaced, []cadence.Value{txData, toAddress}) if err != nil { return nil, fmt.Errorf("failed to execute script: %w", err) } @@ -255,8 +262,8 @@ func (e *EVM) getSignerNetworkInfo(ctx context.Context) (int, uint64, error) { return 0, 0, fmt.Errorf("provided account address and signer keys do not match") } -// replaceImports replace the import addresses based on the network -func (e *EVM) replaceImports(script []byte) []byte { +// replaceAddresses replace the addresses based on the network +func (e *EVM) replaceAddresses(script []byte) []byte { // todo use the FVM configured addresses once the previewnet is added, this should all be replaced once flow-go is updated addresses := map[string]map[string]string{ "previewnet": { @@ -280,6 +287,9 @@ func (e *EVM) replaceImports(script []byte) []byte { ) } + // also replace COA address if used (in scripts) + s = strings.ReplaceAll(s, "0xCOA", e.address.HexWithPrefix()) + return []byte(s) } From 4721db5d9b1159b2cf845f7f2eae4481f6bba2f0 Mon Sep 17 00:00:00 2001 From: Gregor Gololicic Date: Thu, 22 Feb 2024 15:48:26 +0100 Subject: [PATCH 31/40] update call --- services/requester/cadence/call.cdc | 2 +- services/requester/requester.go | 33 ++++++++++++++++------------- 2 files changed, 19 insertions(+), 16 deletions(-) diff --git a/services/requester/cadence/call.cdc b/services/requester/cadence/call.cdc index 4538f231b..4753ce881 100644 --- a/services/requester/cadence/call.cdc +++ b/services/requester/cadence/call.cdc @@ -10,7 +10,7 @@ fun main(data: [UInt8], contractAddress: [UInt8; 20]): [UInt8] { return coa.call( to: EVM.EVMAddress(bytes: contractAddress), data: data, - gasLimit: 0xGAS, + gasLimit: 15000000, // todo make it configurable, max for now value: EVM.Balance(attoflow: 0) ) } diff --git a/services/requester/requester.go b/services/requester/requester.go index e418fc2ba..291b1588d 100644 --- a/services/requester/requester.go +++ b/services/requester/requester.go @@ -116,6 +116,10 @@ func NewEVM( } func (e *EVM) SendRawTransaction(ctx context.Context, data []byte) (common.Hash, error) { + e.logger.Debug(). + Str("data", fmt.Sprintf("%x", data)). + Msg("send raw transaction") + tx := &types.Transaction{} err := tx.DecodeRLP( rlp.NewStream( @@ -144,7 +148,7 @@ func (e *EVM) SendRawTransaction(ctx context.Context, data []byte) (common.Hash, Str("to", to). Str("value", tx.Value().String()). Str("data", fmt.Sprintf("%x", tx.Data())). - Msg("raw transaction submitted") + Msg("raw transaction sent") return tx.Hash(), nil } @@ -220,27 +224,26 @@ func (e *EVM) Call(ctx context.Context, address common.Address, data []byte) ([] txData := cadenceArrayFromBytes(data).WithType(byteArrayType) toAddress := cadenceArrayFromBytes(address.Bytes()).WithType(addressType) - e.logger.Info(). + e.logger.Debug(). Str("address", address.Hex()). - Str("data", string(data)). + Str("data", fmt.Sprintf("%x", data)). Msg("call") - tx := &types.Transaction{} - if err := tx.UnmarshalBinary(data); err != nil { - return nil, fmt.Errorf("failed to decode tx in call: %w", err) - } - - // replace gas limit with the one set on tx - script := strings.ReplaceAll(string(callScript), "0xGAS", fmt.Sprintf("%d", tx.Gas())) - - // replace all the addresses - replaced := e.replaceAddresses([]byte(script)) - - value, err := e.client.ExecuteScriptAtLatestBlock(ctx, replaced, []cadence.Value{txData, toAddress}) + value, err := e.client.ExecuteScriptAtLatestBlock( + ctx, + e.replaceAddresses(data), + []cadence.Value{txData, toAddress}, + ) if err != nil { return nil, fmt.Errorf("failed to execute script: %w", err) } + e.logger.Info(). + Str("address", address.Hex()). + Str("data", fmt.Sprintf("%x", data)). + Str("result", value.String()). + Msg("call executed") + return bytesFromCadenceArray(value) } From 6046ac6b26746e9566583ccc90b347a7e5d9987a Mon Sep 17 00:00:00 2001 From: Gregor Gololicic Date: Thu, 22 Feb 2024 15:51:11 +0100 Subject: [PATCH 32/40] update call --- services/requester/requester.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/requester/requester.go b/services/requester/requester.go index 291b1588d..2aa18ec47 100644 --- a/services/requester/requester.go +++ b/services/requester/requester.go @@ -231,7 +231,7 @@ func (e *EVM) Call(ctx context.Context, address common.Address, data []byte) ([] value, err := e.client.ExecuteScriptAtLatestBlock( ctx, - e.replaceAddresses(data), + e.replaceAddresses(callScript), []cadence.Value{txData, toAddress}, ) if err != nil { From 8f757ad3436f67da38902dfcc9609f24515ec2ae Mon Sep 17 00:00:00 2001 From: Gregor Gololicic Date: Thu, 22 Feb 2024 15:58:11 +0100 Subject: [PATCH 33/40] improve engine code --- services/ingestion/engine.go | 48 +++++++++++++++++------------------- 1 file changed, 22 insertions(+), 26 deletions(-) diff --git a/services/ingestion/engine.go b/services/ingestion/engine.go index 2e1b16a48..e06b9933a 100644 --- a/services/ingestion/engine.go +++ b/services/ingestion/engine.go @@ -123,19 +123,19 @@ func (e *Engine) processEvents(events flow.BlockEvents) error { Msg("received new cadence evm events") for _, event := range events.Events { - if models.IsBlockExecutedEvent(event.Value) { - err := e.processBlockEvent(events.Height, event.Value) - if err != nil { - return err - } - } else if models.IsTransactionExecutedEvent(event.Value) { - err := e.processTransactionEvent(event.Value) - if err != nil { - return err - } - } else { + var err error + switch { + case models.IsBlockExecutedEvent(event.Value): + err = e.processBlockEvent(events.Height, event.Value) + case models.IsTransactionExecutedEvent(event.Value): + err = e.processTransactionEvent(event.Value) + default: return fmt.Errorf("invalid event type") // should never happen } + + if err != nil { + return fmt.Errorf("failed to process event: %w", err) + } } return nil @@ -144,17 +144,17 @@ func (e *Engine) processEvents(events flow.BlockEvents) error { func (e *Engine) processBlockEvent(cadenceHeight uint64, event cadence.Event) error { block, err := models.DecodeBlock(event) if err != nil { - return err + return fmt.Errorf("could not decode block event: %w", err) } // only init latest height if not set if e.evmLastHeight == nil { e.evmLastHeight = models.NewSequentialHeight(block.Height) - } else { // otherwise make sure the latest height is same as the one set on the engine - err = e.evmLastHeight.Increment(block.Height) - if err != nil { - return err - } + } + + // make sure the latest height is increasing sequentially or is same as latest + if err = e.evmLastHeight.Increment(block.Height); err != nil { + return fmt.Errorf("invalid block height, expected %d, got %d: %w", e.evmLastHeight.Load(), block.Height, err) } h, _ := block.Hash() @@ -165,17 +165,13 @@ func (e *Engine) processBlockEvent(cadenceHeight uint64, event cadence.Event) er Str("tx-hash", block.TransactionHashes[0].Hex()). // now we only have 1 tx per block Msg("new evm block executed event") - if err = e.evmLastHeight.Increment(block.Height); err != nil { - return fmt.Errorf("invalid block height, expected %d, got %d: %w", e.evmLastHeight.Load(), block.Height, err) - } - return e.blocks.Store(cadenceHeight, block) } func (e *Engine) processTransactionEvent(event cadence.Event) error { tx, err := models.DecodeTransaction(event) if err != nil { - return err + return fmt.Errorf("could not decode transaction event: %w", err) } // in case we have a direct call transaction we ignore it for now @@ -186,7 +182,7 @@ func (e *Engine) processTransactionEvent(event cadence.Event) error { receipt, err := models.DecodeReceipt(event) if err != nil { - return err + return fmt.Errorf("failed to decode receipt: %w", err) } e.log.Info(). @@ -198,15 +194,15 @@ func (e *Engine) processTransactionEvent(event cadence.Event) error { // todo think if we could introduce batching if err := e.transactions.Store(tx); err != nil { - return err + return fmt.Errorf("failed to store tx: %w", err) } if err := e.accounts.Update(tx, receipt); err != nil { - return err + return fmt.Errorf("failed to update accounts: %w", err) } if err := e.receipts.Store(receipt); err != nil { - return err + return fmt.Errorf("failed to store receipt: %w", err) } return nil From 3ecc891f903378402952011f09308e8ee22d57dc Mon Sep 17 00:00:00 2001 From: Gregor Gololicic Date: Thu, 22 Feb 2024 19:57:41 +0100 Subject: [PATCH 34/40] add response logger --- api/server.go | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/api/server.go b/api/server.go index fe75ea165..8811049f4 100644 --- a/api/server.go +++ b/api/server.go @@ -225,11 +225,17 @@ func (h *httpServer) ServeHTTP(w http.ResponseWriter, r *http.Request) { } } + // enable logging responses + logW := &loggingResponseWriter{ + ResponseWriter: w, + logger: h.log, + } + // If JSON-RPC over HTTP is enabled, try to serve the request rpc := recoverHandler(h.log, h.httpHandler) if rpc != nil { if checkPath(r, "") { - rpc.ServeHTTP(w, r) + rpc.ServeHTTP(logW, r) return } } @@ -364,3 +370,19 @@ func recoverHandler(logger zerolog.Logger, h http.Handler) http.Handler { h.ServeHTTP(w, r) }) } + +var _ http.ResponseWriter = &loggingResponseWriter{} + +type loggingResponseWriter struct { + http.ResponseWriter + logger zerolog.Logger +} + +func (w *loggingResponseWriter) Write(data []byte) (int, error) { + w.logger.Debug().Str("data", string(data)).Msg("API response") + return w.ResponseWriter.Write(data) +} + +func (w *loggingResponseWriter) WriteHeader(statusCode int) { + w.ResponseWriter.WriteHeader(statusCode) +} From 276b32282984cec725e3501000decebebf88346c Mon Sep 17 00:00:00 2001 From: Gregor Gololicic Date: Thu, 22 Feb 2024 19:57:52 +0100 Subject: [PATCH 35/40] return block model --- api/api.go | 62 +++++++++++++++++++++++++-------------------------- api/models.go | 9 ++++++++ 2 files changed, 40 insertions(+), 31 deletions(-) diff --git a/api/api.go b/api/api.go index fa1482c3d..6e72ca23e 100644 --- a/api/api.go +++ b/api/api.go @@ -79,7 +79,7 @@ func (b *BlockChainAPI) ChainId() *hexutil.Big { func (b *BlockChainAPI) BlockNumber() (hexutil.Uint64, error) { latestBlockHeight, err := b.blocks.LatestEVMHeight() if err != nil { - return 0, nil + return handleError[hexutil.Uint64](b.logger, err) } return hexutil.Uint64(latestBlockHeight), nil @@ -244,6 +244,11 @@ func (b *BlockChainAPI) GetTransactionReceipt( return handleError[*types.Receipt](b.logger, err) } + // todo workaround until new version of flow-go is released + blk, _ := b.blocks.GetByHeight(rcp.BlockNumber.Uint64()) + h, _ := blk.Hash() + rcp.BlockHash = h + return rcp, nil } @@ -258,12 +263,10 @@ func (b *BlockChainAPI) GetBlockByHash( ctx context.Context, hash common.Hash, fullTx bool, -) (map[string]interface{}, error) { // todo change return type to return block type - block := map[string]interface{}{} - +) (*Block, error) { bl, err := b.blocks.GetByID(hash) if err != nil { - return handleError[map[string]any](b.logger, err) + return handleError[*Block](b.logger, err) } h, err := bl.Hash() @@ -272,13 +275,13 @@ func (b *BlockChainAPI) GetBlockByHash( return nil, errs.ErrInternal } - block["hash"] = h - block["number"] = hexutil.Uint64(bl.Height) - block["parentHash"] = bl.ParentBlockHash - block["receiptsRoot"] = bl.ReceiptRoot - block["transactions"] = bl.TransactionHashes - - return block, nil + return &Block{ + Hash: h, + Number: hexutil.Uint64(bl.Height), + ParentHash: bl.ParentBlockHash, + ReceiptsRoot: bl.ReceiptRoot, + Transactions: bl.TransactionHashes, + }, nil } // GetBlockByNumber returns the requested canonical block. @@ -292,27 +295,24 @@ func (b *BlockChainAPI) GetBlockByNumber( ctx context.Context, blockNumber rpc.BlockNumber, fullTx bool, -) (map[string]interface{}, error) { // todo change return type to return block type - if fullTx { - return nil, errs.ErrNotSupported +) (*Block, error) { + if fullTx { // todo support full tx + b.logger.Warn().Msg("not supported getting full txs") } - block := map[string]interface{}{} - height := uint64(blockNumber) var err error - if blockNumber == -2 { + // todo for now we treat all special blocks as latest, think which special statuses we can even support on Flow + if blockNumber < 0 { height, err = b.blocks.LatestEVMHeight() if err != nil { - return handleError[map[string]any](b.logger, err) + return handleError[*Block](b.logger, err) } - } else if blockNumber < 0 { - return nil, errs.ErrNotSupported } bl, err := b.blocks.GetByHeight(height) if err != nil { - return handleError[map[string]any](b.logger, err) + return handleError[*Block](b.logger, err) } h, err := bl.Hash() @@ -321,13 +321,13 @@ func (b *BlockChainAPI) GetBlockByNumber( return nil, errs.ErrInternal } - block["hash"] = h - block["number"] = hexutil.Uint64(bl.Height) - block["parentHash"] = bl.ParentBlockHash - block["receiptsRoot"] = bl.ReceiptRoot - block["transactions"] = bl.TransactionHashes - - return block, nil + return &Block{ + Hash: h, + Number: hexutil.Uint64(bl.Height), + ParentHash: bl.ParentBlockHash, + ReceiptsRoot: bl.ReceiptRoot, + Transactions: bl.TransactionHashes, + }, nil } // GetBlockReceipts returns the block receipts for the given block hash or number or tag. @@ -471,9 +471,9 @@ func (b *BlockChainAPI) GetTransactionCount( address common.Address, blockNumberOrHash *rpc.BlockNumberOrHash, ) (*hexutil.Uint64, error) { - // for now we only support indexing at latest block + // todo support previous nonce if blockNumberOrHash != nil && *blockNumberOrHash.BlockNumber != rpc.LatestBlockNumber { - return nil, errs.ErrNotSupported + b.logger.Warn().Msg("transaction count for special blocks not supported") // but still return latest for now } nonce, err := b.accounts.GetNonce(&address) diff --git a/api/models.go b/api/models.go index 41fcf3471..3fba2d37a 100644 --- a/api/models.go +++ b/api/models.go @@ -132,3 +132,12 @@ type BlockOverrides struct { BaseFee *hexutil.Big BlobBaseFee *hexutil.Big } + +type Block struct { + Hash common.Hash + Number hexutil.Uint64 + ParentHash common.Hash + ReceiptsRoot common.Hash + Transactions []common.Hash + // todo add more fields needed +} From 375d8e9ed76f6aacd77c301a32dd30527a6612db Mon Sep 17 00:00:00 2001 From: Alex Ni <12097569+nialexsan@users.noreply.github.com> Date: Thu, 22 Feb 2024 16:32:46 -0500 Subject: [PATCH 36/40] mount point --- scripts/run.sh | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/scripts/run.sh b/scripts/run.sh index 47578c7a7..4abadf13f 100755 --- a/scripts/run.sh +++ b/scripts/run.sh @@ -1,4 +1,17 @@ #!/bin/bash + +## from https://cloud.google.com/run/docs/tutorials/network-filesystems-filestore +set -eo pipefail + +MNT_DIR='./db' + +# Create mount directory for service. +mkdir -p $MNT_DIR + +echo "Mounting Cloud Filestore." +mount -o nolock $FILESTORE_MOUNT_POINT $MNT_DIR +echo "Mounting completed." + ./evm-gateway --access-node-grpc-host access-001.previewnet1.nodes.onflow.org:9000 \ --init-cadence-height 93680 \ --flow-network-id previewnet \ From 8c9899870390100c485f599ee39ae801891e4a7e Mon Sep 17 00:00:00 2001 From: Alex Ni <12097569+nialexsan@users.noreply.github.com> Date: Thu, 22 Feb 2024 16:58:31 -0500 Subject: [PATCH 37/40] add nfs --- Dockerfile | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Dockerfile b/Dockerfile index d8f48fc3e..f82502280 100644 --- a/Dockerfile +++ b/Dockerfile @@ -15,6 +15,8 @@ RUN chmod a+x ./scripts/run.sh # RUN APP FROM debian:latest WORKDIR /flow-evm-gateway +RUN sudo apt-get update +RUN sudo apt-get install nfs-common COPY --from=builder /flow-evm-gateway/evm-gateway /flow-evm-gateway/evm-gateway COPY --from=builder /flow-evm-gateway/scripts/run.sh /flow-evm-gateway/run.sh EXPOSE 3000 From e56b5f6409e2ae17c7cbefb1be4a93ef1bcec767 Mon Sep 17 00:00:00 2001 From: Alex Ni <12097569+nialexsan@users.noreply.github.com> Date: Thu, 22 Feb 2024 17:03:32 -0500 Subject: [PATCH 38/40] fix command --- Dockerfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index f82502280..81dea933f 100644 --- a/Dockerfile +++ b/Dockerfile @@ -15,8 +15,8 @@ RUN chmod a+x ./scripts/run.sh # RUN APP FROM debian:latest WORKDIR /flow-evm-gateway -RUN sudo apt-get update -RUN sudo apt-get install nfs-common +RUN apt-get update +RUN apt-get install nfs-common COPY --from=builder /flow-evm-gateway/evm-gateway /flow-evm-gateway/evm-gateway COPY --from=builder /flow-evm-gateway/scripts/run.sh /flow-evm-gateway/run.sh EXPOSE 3000 From de68836142b811eb73c9c9b26b5181c114b7534e Mon Sep 17 00:00:00 2001 From: Alex Ni <12097569+nialexsan@users.noreply.github.com> Date: Thu, 22 Feb 2024 17:07:18 -0500 Subject: [PATCH 39/40] install yes --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 81dea933f..9e80df9f7 100644 --- a/Dockerfile +++ b/Dockerfile @@ -16,7 +16,7 @@ RUN chmod a+x ./scripts/run.sh FROM debian:latest WORKDIR /flow-evm-gateway RUN apt-get update -RUN apt-get install nfs-common +RUN apt-get install -y nfs-common COPY --from=builder /flow-evm-gateway/evm-gateway /flow-evm-gateway/evm-gateway COPY --from=builder /flow-evm-gateway/scripts/run.sh /flow-evm-gateway/run.sh EXPOSE 3000 From 716d1fe724cb0c9fdd4ec8c0e79f6486aa32570a Mon Sep 17 00:00:00 2001 From: Gregor Gololicic Date: Thu, 22 Feb 2024 23:16:03 +0100 Subject: [PATCH 40/40] update readme --- README.md | 100 +++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 77 insertions(+), 23 deletions(-) diff --git a/README.md b/README.md index b85c164f7..888ca6a6e 100644 --- a/README.md +++ b/README.md @@ -1,23 +1,77 @@ -# Repository template -A template enabled repository, including all necesary files to open source. -(create an issue with the following content if you want to track the repo configuration) - -## Repository settings and configuration -- [ ] Repository info - - [ ] Add repo description - - [ ] Update website to https://onflow.org - - [ ] Add relevant repository topics (i.e. `blockchain` `onflow`, etc) - - [ ] Check issue labels on `.github/labels.yml` and do any commit to main to get them synced -- [ ] Define merge workflow (create new branch protection rule) - - [ ] `main` branch rule: - - [ ] **Require pull request reviews before merging (2 approving reviews)** - - [ ] **Require review from Code Owners** - - [ ] **Require status checks to pass before merging** - - [ ] **Require branches to be up to date before merging** - - [ ] **Require linear history** - - [ ] **Restrict who can push to matching branches** - - [ ] Choose `onflow/flow` team - -- [ ] Add necessary team members, adjust access levels - - [ ] `onflow/flow-admin` ⇒ Admin access - - [ ] `onflow/flow-engineering ` ⇒ Write access +# EVM Gateway + + +**EVM Gateway enables seamless interaction with the Flow EVM, mirroring the experience of engaging with any other EVM blockchain.** + +EVM Gateway serves as a powerful API gateway, designed specifically to bridge the Ethereum Virtual Machine (EVM) with the Flow blockchain ecosystem. By exposing a JSON RPC API, it enables seamless access to EVM functionalities on Flow, creating a unified platform for developers to interact with and build upon. + +At its core, EVM Gateway is engineered to index EVM transactions and blocks produced within the EVM network hosted on Flow. This capability ensures that all relevant data—ranging from transaction details to block information—is accurately captured and made readily accessible to clients. Through its comprehensive indexing system, EVM Gateway provides an essential service for applications requiring up-to-date and historical EVM data. + +Beyond data provision, EVM Gateway plays a crucial role in transaction management. It accepts EVM-compatible transactions from clients, transforming them into Cadence transactions before submitting them to the Flow network. This process not only simplifies the transaction submission for users familiar with EVM ecosystems but also leverages Flow's unique features and benefits, thereby enhancing transaction efficiency and security. + +EVM Gateway stands as a testament to the collaborative potential of blockchain technologies. By integrating EVM's robust capabilities with Flow's innovative blockchain platform, it offers developers a versatile and powerful toolset. Whether you're building decentralized applications, conducting blockchain analysis, or integrating blockchain functionalities into existing systems, EVM Gateway provides the necessary infrastructure to bridge these two pioneering technologies. + + +# Running +Operating an EVM Gateway is straightforward. It can either be deployed locally alongside the Flow emulator or configured to connect with any active Flow networks supporting EVM. Given that the EVM Gateway depends solely on [Access Node APIs](https://developers.flow.com/networks/node-ops/access-onchain-data/access-nodes/accessing-data/access-api), it is compatible with any networks offering this API access. + +### Running Locally +**Start Emulator** + +In order to run the gateway locally you need to start the emulator with EVM enabled: +``` +flow emulator --evm-enabled +``` +_Make sure flow.json has the emulator account configured to address and private key we will use for starting gateway bellow._ + +Then you need to start the gateway: +``` +go run cmd/main/main.go + --init-cadence-height 0 + --coinbase FACF71692421039876a5BB4F10EF7A439D8ef61E + --coa-address f8d6e0586b0a20c7 + --coa-key 2619878f0e2ff438d17835c2a4561cb87b4d24d72d12ec34569acd0dd4af7c21 + --coa-resource-create + --gas-price 0 +``` + +_In this example we use `coa-address` value set to service account of the emulator, same as `coa-key`. +This account will by default be funded with Flow which is a requirement. For `coinbase` we can +use whichever valid EVM address. It's not really useful for local running beside collecting fees. We provide also the +`coa-resource-create` to auto-create resources needed on start-up on the `coa` account in order to operate gateway. +`gas-price` is set at 0 so we don't have to fund EOA accounts. We can set it higher but keep in mind you will then +need funded accounts for interacting with EVM._ + +## Configuration Flags + +The application can be configured using the following flags at runtime: + +| Flag | Default Value | Description | +|----------------------------|------------------|------------------------------------------------------------------------------------------------------------------------| +| `--database-dir` | `./db` | Path to the directory for the database. | +| `--rpc-host` | (empty) | Host for the JSON RPC API server. | +| `--rpc-port` | `3000` | Port for the JSON RPC API server. | +| `--access-node-grpc-host` | `localhost:3569` | Host to the Flow access node (AN) gRPC API. | +| `--init-cadence-height` | `EmptyHeight` | Init cadence block height from where the event ingestion will start. *WARNING*: Used only if no existing DB values. | +| `--evm-network-id` | `testnet` | EVM network ID (options: `testnet`, `mainnet`). | +| `--flow-network-id` | `emulator` | Flow network ID (options: `emulator`, `previewnet`). | +| `--coinbase` | (required) | Coinbase address to use for fee collection. | +| `--gas-price` | `1` | Static gas price used for EVM transactions. | +| `--coa-address` | (required) | Flow address that holds COA account used for submitting transactions. | +| `--coa-key` | (required) | *WARNING*: Do not use this flag in production! Private key value for the COA address used for submitting transactions. | +| `--coa-resource-create` | `false` | Auto-create the COA resource in the Flow COA account provided if one doesn't exist. | + +## Getting Started + +To start using EVM Gateway, ensure you have the required dependencies installed and then run the application with your desired configuration flags. For example: + +```bash +./evm-gateway --rpc-host "127.0.0.1" --rpc-port 3000 --database-dir "/path/to/database" +```` +For more detailed information on configuration and deployment, refer to the Configuration and Deployment sections. + +## Contributing +We welcome contributions from the community! Please read our Contributing Guide for information on how to get involved. + +## License +EVM Gateway is released under the Apache License 2.0. See the LICENSE file for more details. \ No newline at end of file