package commands import ( "bytes" "context" "math/big" "sync" "github.com/ledgerwatch/erigon/cmd/rpcdaemon/filters" "github.com/ledgerwatch/erigon/common" "github.com/ledgerwatch/erigon/common/hexutil" "github.com/ledgerwatch/erigon/common/math" "github.com/ledgerwatch/erigon/core" "github.com/ledgerwatch/erigon/core/rawdb" "github.com/ledgerwatch/erigon/core/types" ethFilters "github.com/ledgerwatch/erigon/eth/filters" "github.com/ledgerwatch/erigon/ethdb" "github.com/ledgerwatch/erigon/gointerfaces/txpool" "github.com/ledgerwatch/erigon/internal/ethapi" "github.com/ledgerwatch/erigon/params" "github.com/ledgerwatch/erigon/rpc" ) // EthAPI is a collection of functions that are exposed in the type EthAPI interface { // Block related (proposed file: ./eth_blocks.go) GetBlockByNumber(ctx context.Context, number rpc.BlockNumber, fullTx bool) (map[string]interface{}, error) GetBlockByHash(ctx context.Context, hash rpc.BlockNumberOrHash, fullTx bool) (map[string]interface{}, error) GetBlockTransactionCountByNumber(ctx context.Context, blockNr rpc.BlockNumber) (*hexutil.Uint, error) GetBlockTransactionCountByHash(ctx context.Context, blockHash common.Hash) (*hexutil.Uint, error) // Transaction related (see ./eth_txs.go) GetTransactionByHash(ctx context.Context, hash common.Hash) (*RPCTransaction, error) GetTransactionByBlockHashAndIndex(ctx context.Context, blockHash common.Hash, txIndex hexutil.Uint64) (*RPCTransaction, error) GetTransactionByBlockNumberAndIndex(ctx context.Context, blockNr rpc.BlockNumber, txIndex hexutil.Uint) (*RPCTransaction, error) GetRawTransactionByBlockNumberAndIndex(ctx context.Context, blockNr rpc.BlockNumber, index hexutil.Uint) (hexutil.Bytes, error) GetRawTransactionByBlockHashAndIndex(ctx context.Context, blockHash common.Hash, index hexutil.Uint) (hexutil.Bytes, error) GetRawTransactionByHash(ctx context.Context, hash common.Hash) (hexutil.Bytes, error) // Receipt related (see ./eth_receipts.go) GetTransactionReceipt(ctx context.Context, hash common.Hash) (map[string]interface{}, error) GetLogs(ctx context.Context, crit ethFilters.FilterCriteria) ([]*types.Log, error) GetBlockReceipts(ctx context.Context, number rpc.BlockNumber) ([]map[string]interface{}, error) // Uncle related (see ./eth_uncles.go) GetUncleByBlockNumberAndIndex(ctx context.Context, blockNr rpc.BlockNumber, index hexutil.Uint) (map[string]interface{}, error) GetUncleByBlockHashAndIndex(ctx context.Context, hash common.Hash, index hexutil.Uint) (map[string]interface{}, error) GetUncleCountByBlockNumber(ctx context.Context, number rpc.BlockNumber) (*hexutil.Uint, error) GetUncleCountByBlockHash(ctx context.Context, hash common.Hash) (*hexutil.Uint, error) // Filter related (see ./eth_filters.go) NewPendingTransactionFilter(_ context.Context) (hexutil.Uint64, error) NewBlockFilter(_ context.Context) (hexutil.Uint64, error) NewFilter(_ context.Context, filter interface{}) (hexutil.Uint64, error) UninstallFilter(_ context.Context, index hexutil.Uint64) (bool, error) GetFilterChanges(_ context.Context, index hexutil.Uint64) ([]interface{}, error) // Account related (see ./eth_accounts.go) Accounts(ctx context.Context) ([]common.Address, error) GetBalance(ctx context.Context, address common.Address, blockNrOrHash rpc.BlockNumberOrHash) (*hexutil.Big, error) GetTransactionCount(ctx context.Context, address common.Address, blockNrOrHash rpc.BlockNumberOrHash) (*hexutil.Uint64, error) GetStorageAt(ctx context.Context, address common.Address, index string, blockNrOrHash rpc.BlockNumberOrHash) (string, error) GetCode(ctx context.Context, address common.Address, blockNrOrHash rpc.BlockNumberOrHash) (hexutil.Bytes, error) // System related (see ./eth_system.go) BlockNumber(ctx context.Context) (hexutil.Uint64, error) Syncing(ctx context.Context) (interface{}, error) ChainId(ctx context.Context) (hexutil.Uint64, error) /* called eth_protocolVersion elsewhere */ ProtocolVersion(_ context.Context) (hexutil.Uint, error) GasPrice(_ context.Context) (*hexutil.Big, error) // Sending related (see ./eth_call.go) Call(ctx context.Context, args ethapi.CallArgs, blockNrOrHash rpc.BlockNumberOrHash, overrides *map[common.Address]ethapi.Account) (hexutil.Bytes, error) EstimateGas(ctx context.Context, args ethapi.CallArgs, blockNrOrHash *rpc.BlockNumberOrHash) (hexutil.Uint64, error) SendRawTransaction(ctx context.Context, encodedTx hexutil.Bytes) (common.Hash, error) SendTransaction(_ context.Context, txObject interface{}) (common.Hash, error) Sign(ctx context.Context, _ common.Address, _ hexutil.Bytes) (hexutil.Bytes, error) SignTransaction(_ context.Context, txObject interface{}) (common.Hash, error) GetProof(ctx context.Context, address common.Address, storageKeys []string, blockNr rpc.BlockNumber) (*interface{}, error) // Mining related (see ./eth_mining.go) Coinbase(ctx context.Context) (common.Address, error) Hashrate(ctx context.Context) (uint64, error) Mining(ctx context.Context) (bool, error) GetWork(ctx context.Context) ([4]string, error) SubmitWork(ctx context.Context, nonce types.BlockNonce, powHash, digest common.Hash) (bool, error) SubmitHashrate(ctx context.Context, hashRate hexutil.Uint64, id common.Hash) (bool, error) // Deprecated commands in eth_ (proposed file: ./eth_deprecated.go) GetCompilers(_ context.Context) ([]string, error) CompileLLL(_ context.Context, _ string) (hexutil.Bytes, error) CompileSolidity(ctx context.Context, _ string) (hexutil.Bytes, error) CompileSerpent(ctx context.Context, _ string) (hexutil.Bytes, error) } type BaseAPI struct { filters *filters.Filters _chainConfig *params.ChainConfig _genesis *types.Block _genesisSetOnce sync.Once } func NewBaseApi(f *filters.Filters) *BaseAPI { return &BaseAPI{filters: f} } func (api *BaseAPI) chainConfig(tx ethdb.Tx) (*params.ChainConfig, error) { cfg, _, err := api.chainConfigWithGenesis(tx) return cfg, err } //nolint:unused func (api *BaseAPI) genesis(tx ethdb.Tx) (*types.Block, error) { _, genesis, err := api.chainConfigWithGenesis(tx) return genesis, err } func (api *BaseAPI) chainConfigWithGenesis(tx ethdb.Tx) (*params.ChainConfig, *types.Block, error) { if api._chainConfig != nil { return api._chainConfig, api._genesis, nil } genesisBlock, err := rawdb.ReadBlockByNumber(tx, 0) if err != nil { return nil, nil, err } cc, err := rawdb.ReadChainConfig(tx, genesisBlock.Hash()) if err != nil { return nil, nil, err } if cc != nil && genesisBlock != nil { api._genesisSetOnce.Do(func() { api._genesis = genesisBlock api._chainConfig = cc }) } return cc, genesisBlock, nil } func (api *BaseAPI) pendingBlock() *types.Block { return api.filters.LastPendingBlock() } func (api *BaseAPI) getBlockByNumber(number rpc.BlockNumber, tx ethdb.Tx) (*types.Block, error) { if number == rpc.PendingBlockNumber { return api.pendingBlock(), nil } n, err := getBlockNumber(number, tx) if err != nil { return nil, err } block, _, err := rawdb.ReadBlockByNumberWithSenders(tx, n) return block, err } // APIImpl is implementation of the EthAPI interface based on remote Db access type APIImpl struct { *BaseAPI ethBackend core.ApiBackend txPool txpool.TxpoolClient mining txpool.MiningClient db ethdb.RoKV GasCap uint64 } // NewEthAPI returns APIImpl instance func NewEthAPI(base *BaseAPI, db ethdb.RoKV, eth core.ApiBackend, txPool txpool.TxpoolClient, mining txpool.MiningClient, gascap uint64) *APIImpl { if gascap == 0 { gascap = uint64(math.MaxUint64 / 2) } return &APIImpl{ BaseAPI: base, db: db, ethBackend: eth, txPool: txPool, mining: mining, GasCap: gascap, } } // RPCTransaction represents a transaction that will serialize to the RPC representation of a transaction type RPCTransaction struct { BlockHash *common.Hash `json:"blockHash"` BlockNumber *hexutil.Big `json:"blockNumber"` From common.Address `json:"from"` Gas hexutil.Uint64 `json:"gas"` GasPrice *hexutil.Big `json:"gasPrice,omitempty"` Tip *hexutil.Big `json:"tip,omitempty"` FeeCap *hexutil.Big `json:"feeCap,omitempty"` Hash common.Hash `json:"hash"` Input hexutil.Bytes `json:"input"` Nonce hexutil.Uint64 `json:"nonce"` To *common.Address `json:"to"` TransactionIndex *hexutil.Uint64 `json:"transactionIndex"` Value *hexutil.Big `json:"value"` Type hexutil.Uint64 `json:"type"` Accesses *types.AccessList `json:"accessList,omitempty"` ChainID *hexutil.Big `json:"chainId,omitempty"` V *hexutil.Big `json:"v"` R *hexutil.Big `json:"r"` S *hexutil.Big `json:"s"` } // newRPCTransaction returns a transaction that will serialize to the RPC // representation, with the given location metadata set (if available). func newRPCTransaction(tx types.Transaction, blockHash common.Hash, blockNumber uint64, index uint64) *RPCTransaction { // Determine the signer. For replay-protected transactions, use the most permissive // signer, because we assume that signers are backwards-compatible with old // transactions. For non-protected transactions, the homestead signer signer is used // because the return value of ChainId is zero for those transactions. var chainId *big.Int result := &RPCTransaction{ Type: hexutil.Uint64(tx.Type()), Gas: hexutil.Uint64(tx.GetGas()), Hash: tx.Hash(), Input: hexutil.Bytes(tx.GetData()), Nonce: hexutil.Uint64(tx.GetNonce()), To: tx.GetTo(), Value: (*hexutil.Big)(tx.GetValue().ToBig()), } switch t := tx.(type) { case *types.LegacyTx: chainId = types.DeriveChainId(&t.V).ToBig() result.GasPrice = (*hexutil.Big)(t.GasPrice.ToBig()) result.V = (*hexutil.Big)(t.V.ToBig()) result.R = (*hexutil.Big)(t.R.ToBig()) result.S = (*hexutil.Big)(t.S.ToBig()) case *types.AccessListTx: chainId = t.ChainID.ToBig() result.ChainID = (*hexutil.Big)(chainId) result.GasPrice = (*hexutil.Big)(t.GasPrice.ToBig()) result.V = (*hexutil.Big)(t.V.ToBig()) result.R = (*hexutil.Big)(t.R.ToBig()) result.S = (*hexutil.Big)(t.S.ToBig()) result.Accesses = &t.AccessList case *types.DynamicFeeTransaction: chainId = t.ChainID.ToBig() result.ChainID = (*hexutil.Big)(chainId) result.Tip = (*hexutil.Big)(t.Tip.ToBig()) result.FeeCap = (*hexutil.Big)(t.FeeCap.ToBig()) result.V = (*hexutil.Big)(t.V.ToBig()) result.R = (*hexutil.Big)(t.R.ToBig()) result.S = (*hexutil.Big)(t.S.ToBig()) result.Accesses = &t.AccessList } signer := types.LatestSignerForChainID(chainId) result.From, _ = tx.Sender(*signer) if blockHash != (common.Hash{}) { result.BlockHash = &blockHash result.BlockNumber = (*hexutil.Big)(new(big.Int).SetUint64(blockNumber)) result.TransactionIndex = (*hexutil.Uint64)(&index) } return result } // newRPCPendingTransaction returns a pending transaction that will serialize to the RPC representation func newRPCPendingTransaction(tx types.Transaction) *RPCTransaction { return newRPCTransaction(tx, common.Hash{}, 0, 0) } // newRPCRawTransactionFromBlockIndex returns the bytes of a transaction given a block and a transaction index. func newRPCRawTransactionFromBlockIndex(b *types.Block, index uint64) (hexutil.Bytes, error) { txs := b.Transactions() if index >= uint64(len(txs)) { return nil, nil } var buf bytes.Buffer err := txs[index].MarshalBinary(&buf) return buf.Bytes(), err }