diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 018f25416..3e53c0b04 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -4,6 +4,7 @@ on: push: branches: - main + - gregor/index/previewnet-testing-fixes env: DOCKER_IMAGE_URL: ${{ vars.GCP_DOCKER_IMAGE_URL }}:${{ github.sha }} 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 diff --git a/Dockerfile b/Dockerfile index cbf917ffc..9e80df9f7 100644 --- a/Dockerfile +++ b/Dockerfile @@ -8,20 +8,16 @@ 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 +RUN apt-get update +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/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 +EXPOSE 3000 CMD cd /flow-evm-gateway && ./run.sh 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 diff --git a/api/api.go b/api/api.go index 4e4549739..6e72ca23e 100644 --- a/api/api.go +++ b/api/api.go @@ -72,18 +72,17 @@ 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. -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 handleError[hexutil.Uint64](b.logger, 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 +142,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 +184,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 +209,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,16 +234,21 @@ 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) } + // 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 } @@ -276,16 +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 { - 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[*Block](b.logger, err) } h, err := bl.Hash() @@ -294,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. @@ -314,35 +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 { - 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[*Block](b.logger, err) } - } else if blockNumber < 0 { - return nil, fmt.Errorf("not supported") } 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[*Block](b.logger, err) } h, err := bl.Hash() @@ -351,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. @@ -380,19 +350,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 +369,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 +422,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 @@ -509,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) @@ -538,6 +500,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/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 +} diff --git a/api/server.go b/api/server.go index 4a470e947..8811049f4 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() } @@ -220,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 } } @@ -359,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) +} diff --git a/bootstrap/bootstrap.go b/bootstrap/bootstrap.go index e125b4271..88ef4e8ad 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 { @@ -156,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 042379596..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 ( @@ -29,8 +30,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,15 +50,16 @@ 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") - 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") - flag.StringVar(&network, "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") flag.StringVar(&coa, "coa-address", "", "Flow address that holds COA account used for submitting transactions") @@ -82,13 +86,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/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 diff --git a/go.mod b/go.mod index 27da3a4ca..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 @@ -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..19f627e29 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,16 +1807,16 @@ 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= 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= @@ -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/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/integration/go.mod b/integration/go.mod index f07f70b11..c94122e45 100644 --- a/integration/go.mod +++ b/integration/go.mod @@ -6,9 +6,9 @@ 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.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..222370922 100644 --- a/integration/go.sum +++ b/integration/go.sum @@ -2037,12 +2037,12 @@ 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= github.com/onflow/flow-go-sdk v1.0.0-M4/go.mod h1:HIGOKJVR47QNs81sPHmZDVcLr+syNkmbgEMLQGDmmEo= diff --git a/scripts/run.sh b/scripts/run.sh old mode 100644 new mode 100755 index 6d27a091b..4abadf13f --- a/scripts/run.sh +++ b/scripts/run.sh @@ -1,23 +1,21 @@ #!/bin/bash -# Start the first process & redirect output to a temporary file -./flow-x86_64-linux- emulator --evm-enabled > temp_output.txt & +## from https://cloud.google.com/run/docs/tutorials/network-filesystems-filestore +set -eo pipefail -# PID of the first process -FIRST_PROCESS_PID=$! +MNT_DIR='./db' -# Monitor the temporary file for a specific output -PATTERN="3569" -while ! grep -q "$PATTERN" temp_output.txt; do - sleep 1 -done +# Create mount directory for service. +mkdir -p $MNT_DIR -# Once the pattern is found, you can kill the first process if needed -# kill $FIRST_PROCESS_PID +echo "Mounting Cloud Filestore." +mount -o nolock $FILESTORE_MOUNT_POINT $MNT_DIR +echo "Mounting completed." -# 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 diff --git a/services/ingestion/engine.go b/services/ingestion/engine.go index 19329595c..e06b9933a 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,21 +73,11 @@ 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) if err != nil { - return err + return fmt.Errorf("failed to subscribe to events: %w", err) } e.status.Ready() @@ -133,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 @@ -154,7 +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) + } + + // 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.lastHeight.Increment(block.Height); err != nil { - return fmt.Errorf("invalid block height, expected %d, got %d: %w", e.lastHeight.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,27 +182,27 @@ 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(). 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 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); err != nil { - return err + if err := e.accounts.Update(tx, receipt); err != nil { + 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 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) diff --git a/services/requester/cadence/bridged_account_call.cdc b/services/requester/cadence/bridged_account_call.cdc deleted file mode 100644 index ec8c11fc7..000000000 --- a/services/requester/cadence/bridged_account_call.cdc +++ /dev/null @@ -1,20 +0,0 @@ -// TODO(m-Peter): Use proper address for each network -import EVM from 0xf8d6e0586b0a20c7 - -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..4753ce881 --- /dev/null +++ b/services/requester/cadence/call.cdc @@ -0,0 +1,16 @@ +import EVM + +access(all) +fun main(data: [UInt8], contractAddress: [UInt8; 20]): [UInt8] { + let account = getAuthAccount(Address(0xCOA)) + + 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: 15000000, // todo make it configurable, max for now + value: EVM.Balance(attoflow: 0) + ) +} diff --git a/services/requester/cadence/create_bridged_account.cdc b/services/requester/cadence/create_coa.cdc similarity index 67% rename from services/requester/cadence/create_bridged_account.cdc rename to services/requester/cadence/create_coa.cdc index e9801119b..b58cf3718 100644 --- a/services/requester/cadence/create_bridged_account.cdc +++ b/services/requester/cadence/create_coa.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 @@ -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 b98b12e5e..000000000 --- a/services/requester/cadence/evm_run.cdc +++ /dev/null @@ -1,16 +0,0 @@ -// TODO(m-Peter): Use proper address for each network -import EVM from 0xf8d6e0586b0a20c7 - -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 81% rename from services/requester/cadence/evm_address_balance.cdc rename to services/requester/cadence/get_balance.cdc index 0af3ec9bc..91ca31155 100644 --- a/services/requester/cadence/evm_address_balance.cdc +++ b/services/requester/cadence/get_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/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()) + } +} diff --git a/services/requester/requester.go b/services/requester/requester.go index d09a32d64..2aa18ec47 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" @@ -18,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) @@ -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.replaceAddresses(createCOAScript), + cadence.UFix64(coaFundingBalance), + ) logger.Info().Err(err).Str("id", id.String()).Msg("COA resource auto-created") } @@ -108,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( @@ -117,8 +129,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.replaceAddresses(runTxScript), + cadenceArrayFromBytes(data), + ) if err != nil { return common.Hash{}, err } @@ -133,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 } @@ -174,6 +189,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 } @@ -182,7 +200,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.replaceAddresses(getBalanceScript), + []cadence.Value{addr}, + ) if err != nil { return nil, err } @@ -202,16 +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") - value, err := e.client.ExecuteScriptAtLatestBlock(ctx, callScript, []cadence.Value{txData, toAddress}) + value, err := e.client.ExecuteScriptAtLatestBlock( + ctx, + e.replaceAddresses(callScript), + []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) } @@ -233,6 +265,37 @@ func (e *EVM) getSignerNetworkInfo(ctx context.Context) (int, uint64, error) { return 0, 0, fmt.Errorf("provided account address and signer keys do not match") } +// 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": { + "EVM": "0xb6763b4399a888c8", + "FungibleToken": "0xa0225e7000ac82a9", + "FlowToken": "0x4445e7ad11568276", + }, + "emulator": { + "EVM": "0xf8d6e0586b0a20c7", + "FungibleToken": "0xee82856bf20e2aa6", + "FlowToken": "0x0ae53cb6e3f42a79", + }, + } + + s := string(script) + // 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), + ) + } + + // also replace COA address if used (in scripts) + s = strings.ReplaceAll(s, "0xCOA", e.address.HexWithPrefix()) + + return []byte(s) +} + func cadenceArrayFromBytes(input []byte) cadence.Array { values := make([]cadence.Value, 0) for _, element := range input { 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/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) 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) } 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 +} 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()