2020-01-17 23:16:01 +00:00
// VulcanizeDB
// Copyright © 2019 Vulcanize
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
2020-01-16 23:21:49 +00:00
package eth
import (
"context"
2021-04-21 11:16:47 +00:00
"database/sql"
2021-02-24 16:50:26 +00:00
"encoding/json"
2020-10-20 20:33:18 +00:00
"errors"
2020-09-25 13:58:18 +00:00
"fmt"
2021-04-21 19:42:11 +00:00
"io"
2020-01-21 19:12:35 +00:00
"math/big"
2021-09-01 07:02:28 +00:00
"strconv"
2020-09-25 13:58:18 +00:00
"time"
2021-09-29 05:27:11 +00:00
"github.com/ethereum/go-ethereum/accounts/abi"
2020-01-21 19:12:35 +00:00
"github.com/ethereum/go-ethereum/common"
2020-01-16 23:21:49 +00:00
"github.com/ethereum/go-ethereum/common/hexutil"
2021-07-27 12:07:50 +00:00
"github.com/ethereum/go-ethereum/common/math"
2020-10-20 20:33:18 +00:00
"github.com/ethereum/go-ethereum/core"
2021-09-29 05:27:11 +00:00
"github.com/ethereum/go-ethereum/core/state"
2020-01-21 19:12:35 +00:00
"github.com/ethereum/go-ethereum/core/types"
2020-10-28 03:04:19 +00:00
"github.com/ethereum/go-ethereum/crypto"
2021-04-12 04:05:03 +00:00
"github.com/ethereum/go-ethereum/eth/filters"
2020-10-26 13:58:37 +00:00
"github.com/ethereum/go-ethereum/ethclient"
2020-10-28 03:04:19 +00:00
"github.com/ethereum/go-ethereum/rlp"
2020-01-16 23:21:49 +00:00
"github.com/ethereum/go-ethereum/rpc"
2021-02-24 22:20:06 +00:00
"github.com/ethereum/go-ethereum/statediff"
2020-10-20 20:33:18 +00:00
"github.com/sirupsen/logrus"
2022-05-20 13:16:36 +00:00
"github.com/vulcanize/ipld-eth-server/v4/pkg/shared"
2020-01-16 23:21:49 +00:00
)
2020-06-30 00:16:52 +00:00
// APIName is the namespace for the watcher's eth api
2020-01-16 23:21:49 +00:00
const APIName = "eth"
2020-06-30 00:16:52 +00:00
// APIVersion is the version of the watcher's eth api
2020-01-16 23:21:49 +00:00
const APIVersion = "0.0.1"
2021-04-09 14:50:46 +00:00
// PublicEthAPI is the eth namespace API
2020-01-16 23:21:49 +00:00
type PublicEthAPI struct {
2020-10-26 13:58:37 +00:00
// Local db backend
2020-05-01 21:25:58 +00:00
B * Backend
2020-10-26 13:58:37 +00:00
2021-02-24 22:20:06 +00:00
// Proxy node for forwarding cache misses
2021-12-27 18:25:54 +00:00
supportsStateDiff bool // Whether the remote node supports the statediff_writeStateDiffAt endpoint, if it does we can fill the local cache when we hit a miss
2021-02-24 16:50:26 +00:00
rpc * rpc . Client
ethClient * ethclient . Client
2021-12-27 18:25:54 +00:00
forwardEthCalls bool // if true, forward eth_call calls directly to the configured proxy node
2021-12-30 02:29:59 +00:00
proxyOnError bool // turn on regular proxy fall-through on errors; needed to test difference between direct and indirect fall-through
2020-01-16 23:21:49 +00:00
}
// NewPublicEthAPI creates a new PublicEthAPI with the provided underlying Backend
2021-12-30 02:29:59 +00:00
func NewPublicEthAPI ( b * Backend , client * rpc . Client , supportsStateDiff , forwardEthCalls , proxyOnError bool ) ( * PublicEthAPI , error ) {
2022-08-08 18:52:36 +00:00
if b == nil {
return nil , errors . New ( "ipld-eth-server must be configured with an ethereum backend" )
}
2021-12-27 18:25:54 +00:00
if forwardEthCalls && client == nil {
return nil , errors . New ( "ipld-eth-server is configured to forward eth_calls to proxy node but no proxy node is configured" )
}
2021-12-30 02:29:59 +00:00
if proxyOnError && client == nil {
return nil , errors . New ( "ipld-eth-server is configured to forward all calls to proxy node on errors but no proxy node is configured" )
}
2020-10-27 17:42:38 +00:00
var ethClient * ethclient . Client
if client != nil {
ethClient = ethclient . NewClient ( client )
}
2020-01-16 23:21:49 +00:00
return & PublicEthAPI {
2021-02-24 16:50:26 +00:00
B : b ,
supportsStateDiff : supportsStateDiff ,
rpc : client ,
ethClient : ethClient ,
2021-12-27 18:25:54 +00:00
forwardEthCalls : forwardEthCalls ,
2021-12-30 02:29:59 +00:00
proxyOnError : proxyOnError ,
2021-12-27 18:25:54 +00:00
} , nil
2020-01-16 23:21:49 +00:00
}
2020-10-28 03:04:19 +00:00
/ *
Headers and blocks
* /
// GetHeaderByNumber returns the requested canonical block header.
// * When blockNr is -1 the chain head is returned.
// * We cannot support pending block calls since we do not have an active miner
func ( pea * PublicEthAPI ) GetHeaderByNumber ( ctx context . Context , number rpc . BlockNumber ) ( map [ string ] interface { } , error ) {
header , err := pea . B . HeaderByNumber ( ctx , number )
if header != nil && err == nil {
return pea . rpcMarshalHeader ( header )
}
2021-12-30 02:29:59 +00:00
if pea . proxyOnError {
2020-10-28 03:04:19 +00:00
if header , err := pea . ethClient . HeaderByNumber ( ctx , big . NewInt ( number . Int64 ( ) ) ) ; header != nil && err == nil {
2021-02-24 16:50:26 +00:00
go pea . writeStateDiffAt ( number . Int64 ( ) )
2020-10-28 03:04:19 +00:00
return pea . rpcMarshalHeader ( header )
}
}
2021-08-20 07:37:11 +00:00
2020-10-28 03:04:19 +00:00
return nil , err
}
// GetHeaderByHash returns the requested header by hash.
func ( pea * PublicEthAPI ) GetHeaderByHash ( ctx context . Context , hash common . Hash ) map [ string ] interface { } {
header , err := pea . B . HeaderByHash ( ctx , hash )
if header != nil && err == nil {
2020-10-29 19:59:09 +00:00
if res , err := pea . rpcMarshalHeader ( header ) ; err == nil {
2020-10-28 03:04:19 +00:00
return res
}
}
2021-08-20 07:37:11 +00:00
2021-12-30 02:29:59 +00:00
if pea . proxyOnError {
2022-07-20 04:04:44 +00:00
var result map [ string ] interface { }
if err := pea . rpc . CallContext ( ctx , & result , "eth_getHeaderByHash" , hash ) ; result != nil && err == nil {
2021-02-24 16:50:26 +00:00
go pea . writeStateDiffFor ( hash )
2022-07-20 04:04:44 +00:00
return result
2020-10-28 03:04:19 +00:00
}
}
2021-08-20 07:37:11 +00:00
2020-10-28 03:04:19 +00:00
return nil
}
// rpcMarshalHeader uses the generalized output filler, then adds the total difficulty field
func ( pea * PublicEthAPI ) rpcMarshalHeader ( header * types . Header ) ( map [ string ] interface { } , error ) {
2021-10-07 09:35:11 +00:00
fields := RPCMarshalHeader ( header )
2020-10-28 03:04:19 +00:00
td , err := pea . B . GetTd ( header . Hash ( ) )
if err != nil {
return nil , err
}
2021-08-20 07:37:11 +00:00
2020-10-28 03:04:19 +00:00
fields [ "totalDifficulty" ] = ( * hexutil . Big ) ( td )
2021-08-20 07:37:11 +00:00
2020-10-28 03:04:19 +00:00
return fields , nil
}
2020-01-16 23:21:49 +00:00
// BlockNumber returns the block number of the chain head.
func ( pea * PublicEthAPI ) BlockNumber ( ) hexutil . Uint64 {
2020-05-01 21:25:58 +00:00
number , _ := pea . B . Retriever . RetrieveLastBlockNumber ( )
2020-01-16 23:21:49 +00:00
return hexutil . Uint64 ( number )
}
2020-10-28 03:04:19 +00:00
// GetBlockByNumber returns the requested canonical block.
// * When blockNr is -1 the chain head is returned.
// * We cannot support pending block calls since we do not have an active miner
// * When fullTx is true all transactions in the block are returned, otherwise
// only the transaction hash is returned.
func ( pea * PublicEthAPI ) GetBlockByNumber ( ctx context . Context , number rpc . BlockNumber , fullTx bool ) ( map [ string ] interface { } , error ) {
2022-07-15 10:49:31 +00:00
logrus . Debug ( "Received getBlockByNumber request for number " , number . Int64 ( ) )
2020-10-28 03:04:19 +00:00
block , err := pea . B . BlockByNumber ( ctx , number )
if block != nil && err == nil {
return pea . rpcMarshalBlock ( block , true , fullTx )
}
2021-08-20 07:37:11 +00:00
2021-12-30 02:29:59 +00:00
if pea . proxyOnError {
2020-10-28 03:04:19 +00:00
if block , err := pea . ethClient . BlockByNumber ( ctx , big . NewInt ( number . Int64 ( ) ) ) ; block != nil && err == nil {
2021-02-24 16:50:26 +00:00
go pea . writeStateDiffAt ( number . Int64 ( ) )
2020-10-28 03:04:19 +00:00
return pea . rpcMarshalBlock ( block , true , fullTx )
}
}
2021-08-20 07:37:11 +00:00
2020-10-28 03:04:19 +00:00
return nil , err
}
// GetBlockByHash returns the requested block. When fullTx is true all transactions in the block are returned in full
// detail, otherwise only the transaction hash is returned.
func ( pea * PublicEthAPI ) GetBlockByHash ( ctx context . Context , hash common . Hash , fullTx bool ) ( map [ string ] interface { } , error ) {
2022-08-25 10:24:32 +00:00
logrus . Debug ( "Received getBlockByHash request for hash " , hash . Hex ( ) )
2020-10-28 03:04:19 +00:00
block , err := pea . B . BlockByHash ( ctx , hash )
if block != nil && err == nil {
return pea . rpcMarshalBlock ( block , true , fullTx )
}
2021-08-20 07:37:11 +00:00
2021-12-30 02:29:59 +00:00
if pea . proxyOnError {
2020-10-28 03:04:19 +00:00
if block , err := pea . ethClient . BlockByHash ( ctx , hash ) ; block != nil && err == nil {
2021-02-24 16:50:26 +00:00
go pea . writeStateDiffFor ( hash )
2020-10-28 03:04:19 +00:00
return pea . rpcMarshalBlock ( block , true , fullTx )
}
}
2021-08-20 07:37:11 +00:00
2020-10-28 03:04:19 +00:00
return nil , err
}
2021-04-19 21:52:58 +00:00
// ChainId is the EIP-155 replay-protection chain id for the current ethereum chain config.
2022-08-01 17:27:29 +00:00
func ( pea * PublicEthAPI ) ChainId ( ) * hexutil . Big {
2022-08-08 18:52:36 +00:00
if pea . B . Config . ChainConfig . ChainID == nil || pea . B . Config . ChainConfig . ChainID . Cmp ( big . NewInt ( 0 ) ) <= 0 {
2021-12-30 02:29:59 +00:00
if pea . proxyOnError {
if id , err := pea . ethClient . ChainID ( context . Background ( ) ) ; err == nil {
2022-08-01 17:27:29 +00:00
return ( * hexutil . Big ) ( id )
2021-12-30 02:29:59 +00:00
}
}
2022-08-01 17:27:29 +00:00
return nil
2021-04-19 21:52:58 +00:00
}
2021-08-20 07:37:11 +00:00
2022-08-08 18:52:36 +00:00
return ( * hexutil . Big ) ( pea . B . Config . ChainConfig . ChainID )
2021-04-19 21:52:58 +00:00
}
2020-10-29 19:59:09 +00:00
/ *
Uncles
* /
2020-10-28 03:04:19 +00:00
// GetUncleByBlockNumberAndIndex returns the uncle block for the given block hash and index. When fullTx is true
// all transactions in the block are returned in full detail, otherwise only the transaction hash is returned.
func ( pea * PublicEthAPI ) GetUncleByBlockNumberAndIndex ( ctx context . Context , blockNr rpc . BlockNumber , index hexutil . Uint ) ( map [ string ] interface { } , error ) {
block , err := pea . B . BlockByNumber ( ctx , blockNr )
2020-10-28 13:22:57 +00:00
if block != nil && err == nil {
2020-10-28 03:04:19 +00:00
uncles := block . Uncles ( )
if index >= hexutil . Uint ( len ( uncles ) ) {
logrus . Debugf ( "uncle with index %s request at block number %d was not found" , index . String ( ) , blockNr . Int64 ( ) )
return nil , nil
}
block = types . NewBlockWithHeader ( uncles [ index ] )
return pea . rpcMarshalBlock ( block , false , false )
}
2021-08-20 07:37:11 +00:00
2021-12-30 02:29:59 +00:00
if pea . proxyOnError {
2020-10-28 13:22:57 +00:00
if uncle , uncleHashes , err := getBlockAndUncleHashes ( pea . rpc , ctx , "eth_getUncleByBlockNumberAndIndex" , blockNr , index ) ; uncle != nil && err == nil {
2021-02-24 16:50:26 +00:00
go pea . writeStateDiffAt ( blockNr . Int64 ( ) )
2020-10-28 13:22:57 +00:00
return pea . rpcMarshalBlockWithUncleHashes ( uncle , uncleHashes , false , false )
}
}
2021-08-20 07:37:11 +00:00
2020-10-28 03:04:19 +00:00
return nil , err
}
// GetUncleByBlockHashAndIndex returns the uncle block for the given block hash and index. When fullTx is true
// all transactions in the block are returned in full detail, otherwise only the transaction hash is returned.
func ( pea * PublicEthAPI ) GetUncleByBlockHashAndIndex ( ctx context . Context , blockHash common . Hash , index hexutil . Uint ) ( map [ string ] interface { } , error ) {
block , err := pea . B . BlockByHash ( ctx , blockHash )
if block != nil {
uncles := block . Uncles ( )
if index >= hexutil . Uint ( len ( uncles ) ) {
logrus . Debugf ( "uncle with index %s request at block hash %s was not found" , index . String ( ) , blockHash . Hex ( ) )
return nil , nil
}
block = types . NewBlockWithHeader ( uncles [ index ] )
return pea . rpcMarshalBlock ( block , false , false )
}
2021-08-20 07:37:11 +00:00
2021-12-30 02:29:59 +00:00
if pea . proxyOnError {
2020-10-28 13:22:57 +00:00
if uncle , uncleHashes , err := getBlockAndUncleHashes ( pea . rpc , ctx , "eth_getUncleByBlockHashAndIndex" , blockHash , index ) ; uncle != nil && err == nil {
2021-02-24 16:50:26 +00:00
go pea . writeStateDiffFor ( blockHash )
2020-10-28 13:22:57 +00:00
return pea . rpcMarshalBlockWithUncleHashes ( uncle , uncleHashes , false , false )
}
}
2021-08-20 07:37:11 +00:00
2020-10-28 03:04:19 +00:00
return nil , err
}
// GetUncleCountByBlockNumber returns number of uncles in the block for the given block number
func ( pea * PublicEthAPI ) GetUncleCountByBlockNumber ( ctx context . Context , blockNr rpc . BlockNumber ) * hexutil . Uint {
2020-10-28 13:22:57 +00:00
if block , err := pea . B . BlockByNumber ( ctx , blockNr ) ; block != nil && err == nil {
2020-10-28 03:04:19 +00:00
n := hexutil . Uint ( len ( block . Uncles ( ) ) )
return & n
}
2021-08-20 07:37:11 +00:00
2021-12-30 02:29:59 +00:00
if pea . proxyOnError {
2020-10-28 13:22:57 +00:00
var num * hexutil . Uint
if err := pea . rpc . CallContext ( ctx , & num , "eth_getUncleCountByBlockNumber" , blockNr ) ; num != nil && err == nil {
2021-02-24 16:50:26 +00:00
go pea . writeStateDiffAt ( blockNr . Int64 ( ) )
2020-10-28 13:22:57 +00:00
return num
}
}
2021-08-20 07:37:11 +00:00
2020-10-28 03:04:19 +00:00
return nil
}
// GetUncleCountByBlockHash returns number of uncles in the block for the given block hash
func ( pea * PublicEthAPI ) GetUncleCountByBlockHash ( ctx context . Context , blockHash common . Hash ) * hexutil . Uint {
2020-10-28 13:22:57 +00:00
if block , err := pea . B . BlockByHash ( ctx , blockHash ) ; block != nil && err == nil {
2020-10-28 03:04:19 +00:00
n := hexutil . Uint ( len ( block . Uncles ( ) ) )
return & n
}
2021-08-20 07:37:11 +00:00
2021-12-30 02:29:59 +00:00
if pea . proxyOnError {
2020-10-28 13:22:57 +00:00
var num * hexutil . Uint
if err := pea . rpc . CallContext ( ctx , & num , "eth_getUncleCountByBlockHash" , blockHash ) ; num != nil && err == nil {
2021-02-24 16:50:26 +00:00
go pea . writeStateDiffFor ( blockHash )
2020-10-28 13:22:57 +00:00
return num
}
}
2021-08-20 07:37:11 +00:00
2020-10-28 03:04:19 +00:00
return nil
}
/ *
Transactions
* /
// GetTransactionCount returns the number of transactions the given address has sent for the given block number
func ( pea * PublicEthAPI ) GetTransactionCount ( ctx context . Context , address common . Address , blockNrOrHash rpc . BlockNumberOrHash ) ( * hexutil . Uint64 , error ) {
2020-10-30 03:07:39 +00:00
count , err := pea . localGetTransactionCount ( ctx , address , blockNrOrHash )
if count != nil && err == nil {
return count , nil
2020-10-28 03:04:19 +00:00
}
2021-08-20 07:37:11 +00:00
2021-12-30 02:29:59 +00:00
if pea . proxyOnError {
2020-10-28 13:22:57 +00:00
var num * hexutil . Uint64
if err := pea . rpc . CallContext ( ctx , & num , "eth_getTransactionCount" , address , blockNrOrHash ) ; num != nil && err == nil {
2021-02-24 16:50:26 +00:00
go pea . writeStateDiffAtOrFor ( blockNrOrHash )
2020-10-28 13:22:57 +00:00
return num , nil
}
}
2021-08-20 07:37:11 +00:00
2020-10-28 13:22:57 +00:00
return nil , err
2020-10-28 03:04:19 +00:00
}
2020-10-30 03:07:39 +00:00
func ( pea * PublicEthAPI ) localGetTransactionCount ( ctx context . Context , address common . Address , blockNrOrHash rpc . BlockNumberOrHash ) ( * hexutil . Uint64 , error ) {
account , err := pea . B . GetAccountByNumberOrHash ( ctx , address , blockNrOrHash )
if err != nil {
return nil , err
}
2021-08-20 07:37:11 +00:00
2020-10-30 03:07:39 +00:00
nonce := hexutil . Uint64 ( account . Nonce )
return & nonce , nil
}
2020-10-28 03:04:19 +00:00
// GetBlockTransactionCountByNumber returns the number of transactions in the block with the given block number.
func ( pea * PublicEthAPI ) GetBlockTransactionCountByNumber ( ctx context . Context , blockNr rpc . BlockNumber ) * hexutil . Uint {
if block , _ := pea . B . BlockByNumber ( ctx , blockNr ) ; block != nil {
n := hexutil . Uint ( len ( block . Transactions ( ) ) )
return & n
}
2021-08-20 07:37:11 +00:00
2021-12-30 02:29:59 +00:00
if pea . proxyOnError {
2020-10-28 13:22:57 +00:00
var num * hexutil . Uint
if err := pea . rpc . CallContext ( ctx , & num , "eth_getBlockTransactionCountByNumber" , blockNr ) ; num != nil && err == nil {
2021-02-24 16:50:26 +00:00
go pea . writeStateDiffAt ( blockNr . Int64 ( ) )
2020-10-28 13:22:57 +00:00
return num
}
}
2021-08-20 07:37:11 +00:00
2020-10-28 03:04:19 +00:00
return nil
}
// GetBlockTransactionCountByHash returns the number of transactions in the block with the given hash.
func ( pea * PublicEthAPI ) GetBlockTransactionCountByHash ( ctx context . Context , blockHash common . Hash ) * hexutil . Uint {
if block , _ := pea . B . BlockByHash ( ctx , blockHash ) ; block != nil {
n := hexutil . Uint ( len ( block . Transactions ( ) ) )
return & n
}
2021-08-20 07:37:11 +00:00
2021-12-30 02:29:59 +00:00
if pea . proxyOnError {
2020-10-28 13:22:57 +00:00
var num * hexutil . Uint
if err := pea . rpc . CallContext ( ctx , & num , "eth_getBlockTransactionCountByHash" , blockHash ) ; num != nil && err == nil {
2021-02-24 16:50:26 +00:00
go pea . writeStateDiffFor ( blockHash )
2020-10-28 13:22:57 +00:00
return num
}
}
2021-08-20 07:37:11 +00:00
2020-10-28 03:04:19 +00:00
return nil
}
// GetTransactionByBlockNumberAndIndex returns the transaction for the given block number and index.
func ( pea * PublicEthAPI ) GetTransactionByBlockNumberAndIndex ( ctx context . Context , blockNr rpc . BlockNumber , index hexutil . Uint ) * RPCTransaction {
if block , _ := pea . B . BlockByNumber ( ctx , blockNr ) ; block != nil {
return newRPCTransactionFromBlockIndex ( block , uint64 ( index ) )
}
2021-08-20 07:37:11 +00:00
2021-12-30 02:29:59 +00:00
if pea . proxyOnError {
2020-10-28 13:22:57 +00:00
var tx * RPCTransaction
if err := pea . rpc . CallContext ( ctx , & tx , "eth_getTransactionByBlockNumberAndIndex" , blockNr , index ) ; tx != nil && err == nil {
2021-02-24 16:50:26 +00:00
go pea . writeStateDiffAt ( blockNr . Int64 ( ) )
2020-10-28 13:22:57 +00:00
return tx
}
}
2021-08-20 07:37:11 +00:00
2020-10-28 03:04:19 +00:00
return nil
}
// GetTransactionByBlockHashAndIndex returns the transaction for the given block hash and index.
func ( pea * PublicEthAPI ) GetTransactionByBlockHashAndIndex ( ctx context . Context , blockHash common . Hash , index hexutil . Uint ) * RPCTransaction {
if block , _ := pea . B . BlockByHash ( ctx , blockHash ) ; block != nil {
return newRPCTransactionFromBlockIndex ( block , uint64 ( index ) )
}
2021-08-20 07:37:11 +00:00
2021-12-30 02:29:59 +00:00
if pea . proxyOnError {
2020-10-28 13:22:57 +00:00
var tx * RPCTransaction
if err := pea . rpc . CallContext ( ctx , & tx , "eth_getTransactionByBlockHashAndIndex" , blockHash , index ) ; tx != nil && err == nil {
2021-02-24 16:50:26 +00:00
go pea . writeStateDiffFor ( blockHash )
2020-10-28 13:22:57 +00:00
return tx
}
}
2021-08-20 07:37:11 +00:00
2020-10-28 03:04:19 +00:00
return nil
}
// GetRawTransactionByBlockNumberAndIndex returns the bytes of the transaction for the given block number and index.
func ( pea * PublicEthAPI ) GetRawTransactionByBlockNumberAndIndex ( ctx context . Context , blockNr rpc . BlockNumber , index hexutil . Uint ) hexutil . Bytes {
if block , _ := pea . B . BlockByNumber ( ctx , blockNr ) ; block != nil {
return newRPCRawTransactionFromBlockIndex ( block , uint64 ( index ) )
}
2021-12-30 02:29:59 +00:00
if pea . proxyOnError {
2020-10-28 13:22:57 +00:00
var tx hexutil . Bytes
if err := pea . rpc . CallContext ( ctx , & tx , "eth_getRawTransactionByBlockNumberAndIndex" , blockNr , index ) ; tx != nil && err == nil {
2021-02-24 16:50:26 +00:00
go pea . writeStateDiffAt ( blockNr . Int64 ( ) )
2020-10-28 13:22:57 +00:00
return tx
}
}
2020-10-28 03:04:19 +00:00
return nil
}
// GetRawTransactionByBlockHashAndIndex returns the bytes of the transaction for the given block hash and index.
func ( pea * PublicEthAPI ) GetRawTransactionByBlockHashAndIndex ( ctx context . Context , blockHash common . Hash , index hexutil . Uint ) hexutil . Bytes {
if block , _ := pea . B . BlockByHash ( ctx , blockHash ) ; block != nil {
return newRPCRawTransactionFromBlockIndex ( block , uint64 ( index ) )
}
2021-12-30 02:29:59 +00:00
if pea . proxyOnError {
2020-10-28 13:22:57 +00:00
var tx hexutil . Bytes
if err := pea . rpc . CallContext ( ctx , & tx , "eth_getRawTransactionByBlockHashAndIndex" , blockHash , index ) ; tx != nil && err == nil {
2021-02-24 16:50:26 +00:00
go pea . writeStateDiffFor ( blockHash )
2020-10-28 13:22:57 +00:00
return tx
}
}
2020-10-28 03:04:19 +00:00
return nil
}
// GetTransactionByHash returns the transaction for the given hash
// eth ipld-eth-server cannot currently handle pending/tx_pool txs
func ( pea * PublicEthAPI ) GetTransactionByHash ( ctx context . Context , hash common . Hash ) ( * RPCTransaction , error ) {
tx , blockHash , blockNumber , index , err := pea . B . GetTransaction ( ctx , hash )
if tx != nil && err == nil {
2021-07-27 12:07:50 +00:00
header , err := pea . B . HeaderByHash ( ctx , blockHash )
if err != nil {
return nil , err
}
return NewRPCTransaction ( tx , blockHash , blockNumber , index , header . BaseFee ) , nil
2020-10-28 03:04:19 +00:00
}
2021-12-30 02:29:59 +00:00
if pea . proxyOnError {
2020-10-28 13:22:57 +00:00
var tx * RPCTransaction
if err := pea . rpc . CallContext ( ctx , & tx , "eth_getTransactionByHash" , hash ) ; tx != nil && err == nil {
2021-02-24 16:50:26 +00:00
go pea . writeStateDiffFor ( hash )
2020-10-28 03:04:19 +00:00
return tx , nil
}
}
return nil , err
}
// GetRawTransactionByHash returns the bytes of the transaction for the given hash.
func ( pea * PublicEthAPI ) GetRawTransactionByHash ( ctx context . Context , hash common . Hash ) ( hexutil . Bytes , error ) {
// Retrieve a finalized transaction, or a pooled otherwise
tx , _ , _ , _ , err := pea . B . GetTransaction ( ctx , hash )
if tx != nil && err == nil {
return rlp . EncodeToBytes ( tx )
}
2021-12-30 02:29:59 +00:00
if pea . proxyOnError {
2020-10-28 13:22:57 +00:00
var tx hexutil . Bytes
if err := pea . rpc . CallContext ( ctx , & tx , "eth_getRawTransactionByHash" , hash ) ; tx != nil && err == nil {
2021-02-24 16:50:26 +00:00
go pea . writeStateDiffFor ( hash )
2020-10-28 03:04:19 +00:00
return tx , nil
}
}
return nil , err
}
/ *
Receipts and Logs
* /
// GetTransactionReceipt returns the transaction receipt for the given transaction hash.
func ( pea * PublicEthAPI ) GetTransactionReceipt ( ctx context . Context , hash common . Hash ) ( map [ string ] interface { } , error ) {
2020-10-29 19:59:09 +00:00
receipt , err := pea . localGetTransactionReceipt ( ctx , hash )
if receipt != nil && err == nil {
return receipt , nil
}
2021-12-30 02:29:59 +00:00
if pea . proxyOnError {
2020-10-29 19:59:09 +00:00
if receipt := pea . remoteGetTransactionReceipt ( ctx , hash ) ; receipt != nil {
2021-02-24 16:50:26 +00:00
go pea . writeStateDiffFor ( hash )
2020-10-29 19:59:09 +00:00
return receipt , nil
}
}
return nil , err
}
func ( pea * PublicEthAPI ) localGetTransactionReceipt ( ctx context . Context , hash common . Hash ) ( map [ string ] interface { } , error ) {
2020-10-30 03:07:39 +00:00
// TODO: this can be optimized for Postgres
2020-10-28 13:22:57 +00:00
tx , blockHash , blockNumber , index , err := pea . B . GetTransaction ( ctx , hash )
if err != nil {
return nil , err
}
2020-10-28 03:04:19 +00:00
if tx == nil {
return nil , nil
}
receipts , err := pea . B . GetReceipts ( ctx , blockHash )
if err != nil {
return nil , err
}
2021-04-29 13:33:27 +00:00
block , err := pea . B . BlockByHash ( ctx , blockHash )
if err != nil {
return nil , err
}
err = receipts . DeriveFields ( pea . B . Config . ChainConfig , blockHash , blockNumber , block . Transactions ( ) )
if err != nil {
return nil , err
}
2020-10-28 03:04:19 +00:00
if len ( receipts ) <= int ( index ) {
return nil , nil
}
receipt := receipts [ index ]
var signer types . Signer = types . FrontierSigner { }
if tx . Protected ( ) {
signer = types . NewEIP155Signer ( tx . ChainId ( ) )
}
from , _ := types . Sender ( signer , tx )
fields := map [ string ] interface { } {
"blockHash" : blockHash ,
"blockNumber" : hexutil . Uint64 ( blockNumber ) ,
"transactionHash" : hash ,
"transactionIndex" : hexutil . Uint64 ( index ) ,
"from" : from ,
"to" : tx . To ( ) ,
"gasUsed" : hexutil . Uint64 ( receipt . GasUsed ) ,
"cumulativeGasUsed" : hexutil . Uint64 ( receipt . CumulativeGasUsed ) ,
"contractAddress" : nil ,
"logs" : receipt . Logs ,
"logsBloom" : receipt . Bloom ,
}
2021-09-15 11:43:12 +00:00
// Assign receipt status or post state.
if len ( receipt . PostState ) > 0 {
2020-10-28 03:04:19 +00:00
fields [ "root" ] = hexutil . Bytes ( receipt . PostState )
} else {
fields [ "status" ] = hexutil . Uint ( receipt . Status )
}
if receipt . Logs == nil {
2020-10-30 03:07:39 +00:00
fields [ "logs" ] = [ ] * types . Log { }
2020-10-28 03:04:19 +00:00
}
// If the ContractAddress is 20 0x0 bytes, assume it is not a contract creation
if receipt . ContractAddress != ( common . Address { } ) {
fields [ "contractAddress" ] = receipt . ContractAddress
}
return fields , nil
}
2020-10-28 13:22:57 +00:00
func ( pea * PublicEthAPI ) remoteGetTransactionReceipt ( ctx context . Context , hash common . Hash ) map [ string ] interface { } {
2020-10-29 19:59:09 +00:00
var rct * RPCReceipt
if err := pea . rpc . CallContext ( ctx , & rct , "eth_getTransactionReceipt" , hash ) ; rct != nil && err == nil {
return map [ string ] interface { } {
"blockHash" : rct . BlockHash ,
"blockNumber" : rct . BlockNumber ,
"transactionHash" : rct . TransactionHash ,
"transactionIndex" : rct . TransactionIndex ,
"from" : rct . From ,
"to" : rct . To ,
"gasUsed" : rct . GasUsed ,
"cumulativeGasUsed" : rct . CumulativeGsUsed ,
"contractAddress" : rct . ContractAddress ,
"logs" : rct . Logs ,
"logsBloom" : rct . Bloom ,
"root" : rct . Root ,
"status" : rct . Status ,
2020-10-28 13:22:57 +00:00
}
}
return nil
}
2020-01-21 19:12:35 +00:00
// GetLogs returns logs matching the given argument that are stored within the state.
//
// https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_getlogs
2021-04-12 04:05:03 +00:00
func ( pea * PublicEthAPI ) GetLogs ( ctx context . Context , crit filters . FilterCriteria ) ( [ ] * types . Log , error ) {
logs , err := pea . localGetLogs ( crit )
2021-12-30 02:29:59 +00:00
if err != nil && pea . proxyOnError {
2021-04-12 04:05:03 +00:00
var res [ ] * types . Log
if err := pea . rpc . CallContext ( ctx , & res , "eth_getLogs" , crit ) ; err == nil {
go pea . writeStateDiffWithCriteria ( crit )
return res , nil
2020-10-26 13:58:37 +00:00
}
}
return logs , err
}
2021-04-12 04:05:03 +00:00
func ( pea * PublicEthAPI ) localGetLogs ( crit filters . FilterCriteria ) ( [ ] * types . Log , error ) {
2020-10-30 03:07:39 +00:00
// TODO: this can be optimized away from using the old cid retriever and ipld fetcher interfaces
2020-01-21 19:12:35 +00:00
// Convert FilterQuery into ReceiptFilter
addrStrs := make ( [ ] string , len ( crit . Addresses ) )
for i , addr := range crit . Addresses {
addrStrs [ i ] = addr . String ( )
}
2021-06-09 03:42:46 +00:00
topicStrSets := make ( [ ] [ ] string , len ( crit . Topics ) )
2020-01-21 19:12:35 +00:00
for i , topicSet := range crit . Topics {
if i > 3 {
2021-06-09 03:42:46 +00:00
topicStrSets = topicStrSets [ : 4 ]
2020-02-20 22:13:19 +00:00
// don't allow more than 4 topics
2020-01-21 19:12:35 +00:00
break
}
for _ , topic := range topicSet {
topicStrSets [ i ] = append ( topicStrSets [ i ] , topic . String ( ) )
}
}
2020-02-03 18:22:29 +00:00
filter := ReceiptFilter {
2020-04-02 03:34:06 +00:00
LogAddresses : addrStrs ,
Topics : topicStrSets ,
2020-01-21 19:12:35 +00:00
}
2020-05-01 21:25:58 +00:00
// Begin tx
tx , err := pea . B . DB . Beginx ( )
2020-01-21 19:12:35 +00:00
if err != nil {
return nil , err
}
2020-05-01 21:25:58 +00:00
defer func ( ) {
if p := recover ( ) ; p != nil {
shared . Rollback ( tx )
panic ( p )
} else if err != nil {
shared . Rollback ( tx )
} else {
err = tx . Commit ( )
}
} ( )
2021-08-20 07:37:11 +00:00
// If we have a blockHash to filter on, fire off single retrieval query
2020-01-21 19:12:35 +00:00
if crit . BlockHash != nil {
2021-08-20 07:37:11 +00:00
filteredLogs , err := pea . B . Retriever . RetrieveFilteredLog ( tx , filter , 0 , crit . BlockHash )
2021-08-14 13:50:22 +00:00
if err != nil {
return nil , err
}
2021-09-01 07:02:28 +00:00
return decomposeLogs ( filteredLogs )
2020-01-21 19:12:35 +00:00
}
2021-06-09 03:42:46 +00:00
2020-01-21 19:12:35 +00:00
// Otherwise, create block range from criteria
// nil values are filled in; to request a single block have both ToBlock and FromBlock equal that number
startingBlock := crit . FromBlock
endingBlock := crit . ToBlock
if startingBlock == nil {
2020-10-26 13:58:37 +00:00
startingBlock = common . Big0
2020-01-21 19:12:35 +00:00
}
2021-06-09 03:42:46 +00:00
2020-01-21 19:12:35 +00:00
if endingBlock == nil {
2020-05-01 21:25:58 +00:00
endingBlockInt , err := pea . B . Retriever . RetrieveLastBlockNumber ( )
2020-01-21 19:12:35 +00:00
if err != nil {
return nil , err
}
endingBlock = big . NewInt ( endingBlockInt )
}
2021-06-09 03:42:46 +00:00
2020-01-21 19:12:35 +00:00
start := startingBlock . Int64 ( )
end := endingBlock . Int64 ( )
2021-06-09 03:42:46 +00:00
var logs [ ] * types . Log
2021-08-31 11:32:01 +00:00
for i := start ; i <= end ; i ++ {
2021-09-01 07:02:28 +00:00
filteredLogs , err := pea . B . Retriever . RetrieveFilteredLog ( tx , filter , i , nil )
2021-08-14 13:50:22 +00:00
if err != nil {
return nil , err
}
2021-09-01 07:02:28 +00:00
logCIDs , err := decomposeLogs ( filteredLogs )
2021-08-14 13:50:22 +00:00
if err != nil {
return nil , err
}
2021-08-20 07:37:11 +00:00
logs = append ( logs , logCIDs ... )
2020-01-21 19:12:35 +00:00
}
2021-06-09 03:42:46 +00:00
2020-05-01 16:07:47 +00:00
if err := tx . Commit ( ) ; err != nil {
2020-01-21 19:12:35 +00:00
return nil , err
}
2021-06-09 03:42:46 +00:00
2020-05-01 21:25:58 +00:00
return logs , err // need to return err variable so that we return the err = tx.Commit() assignment in the defer
2020-01-21 19:12:35 +00:00
}
2020-10-28 03:04:19 +00:00
/ *
State and Storage
* /
// GetBalance returns the amount of wei for the given address in the state of the
// given block number. The rpc.LatestBlockNumber and rpc.PendingBlockNumber meta
// block numbers are also allowed.
func ( pea * PublicEthAPI ) GetBalance ( ctx context . Context , address common . Address , blockNrOrHash rpc . BlockNumberOrHash ) ( * hexutil . Big , error ) {
2020-10-30 03:07:39 +00:00
bal , err := pea . localGetBalance ( ctx , address , blockNrOrHash )
if bal != nil && err == nil {
return bal , nil
2020-10-28 13:22:57 +00:00
}
2021-12-30 02:29:59 +00:00
if pea . proxyOnError {
2020-10-28 13:22:57 +00:00
var res * hexutil . Big
if err := pea . rpc . CallContext ( ctx , & res , "eth_getBalance" , address , blockNrOrHash ) ; res != nil && err == nil {
2021-02-24 16:50:26 +00:00
go pea . writeStateDiffAtOrFor ( blockNrOrHash )
2020-10-28 13:22:57 +00:00
return res , nil
}
2020-10-26 13:58:37 +00:00
}
2021-04-23 09:46:35 +00:00
if err == sql . ErrNoRows {
return ( * hexutil . Big ) ( big . NewInt ( 0 ) ) , nil
}
2020-10-28 13:22:57 +00:00
return nil , err
2020-01-26 19:55:26 +00:00
}
2020-10-30 03:07:39 +00:00
func ( pea * PublicEthAPI ) localGetBalance ( ctx context . Context , address common . Address , blockNrOrHash rpc . BlockNumberOrHash ) ( * hexutil . Big , error ) {
account , err := pea . B . GetAccountByNumberOrHash ( ctx , address , blockNrOrHash )
if err != nil {
return nil , err
}
return ( * hexutil . Big ) ( account . Balance ) , nil
}
2020-10-28 03:04:19 +00:00
// GetStorageAt returns the storage from the state at the given address, key and
// block number. The rpc.LatestBlockNumber and rpc.PendingBlockNumber meta block
// numbers are also allowed.
func ( pea * PublicEthAPI ) GetStorageAt ( ctx context . Context , address common . Address , key string , blockNrOrHash rpc . BlockNumberOrHash ) ( hexutil . Bytes , error ) {
2020-10-30 16:54:22 +00:00
storageVal , err := pea . B . GetStorageByNumberOrHash ( ctx , address , common . HexToHash ( key ) , blockNrOrHash )
if storageVal != nil && err == nil {
2021-04-21 19:09:57 +00:00
var value common . Hash
_ , content , _ , err := rlp . Split ( storageVal )
2021-04-21 19:42:11 +00:00
if err == io . ErrUnexpectedEOF {
return hexutil . Bytes { } , nil
}
2021-04-21 19:09:57 +00:00
if err != nil {
return nil , err
}
value . SetBytes ( content )
return value [ : ] , nil
2020-10-28 13:22:57 +00:00
}
2021-12-30 02:29:59 +00:00
if pea . proxyOnError {
2020-10-28 13:22:57 +00:00
var res hexutil . Bytes
if err := pea . rpc . CallContext ( ctx , & res , "eth_getStorageAt" , address , key , blockNrOrHash ) ; res != nil && err == nil {
2021-02-24 16:50:26 +00:00
go pea . writeStateDiffAtOrFor ( blockNrOrHash )
2020-10-28 13:22:57 +00:00
return res , nil
}
2020-10-26 13:58:37 +00:00
}
2021-04-23 09:46:35 +00:00
if err == sql . ErrNoRows {
2021-06-09 18:50:12 +00:00
return make ( [ ] byte , 32 ) , nil
2021-04-23 09:46:35 +00:00
}
2020-10-28 13:22:57 +00:00
return nil , err
2020-01-26 19:55:26 +00:00
}
2020-10-28 03:04:19 +00:00
// GetCode returns the code stored at the given address in the state for the given block number.
func ( pea * PublicEthAPI ) GetCode ( ctx context . Context , address common . Address , blockNrOrHash rpc . BlockNumberOrHash ) ( hexutil . Bytes , error ) {
2020-10-30 03:07:39 +00:00
code , err := pea . B . GetCodeByNumberOrHash ( ctx , address , blockNrOrHash )
if code != nil && err == nil {
return code , nil
2020-10-26 13:58:37 +00:00
}
2021-12-30 02:29:59 +00:00
if pea . proxyOnError {
2020-10-28 13:22:57 +00:00
var res hexutil . Bytes
if err := pea . rpc . CallContext ( ctx , & res , "eth_getCode" , address , blockNrOrHash ) ; res != nil && err == nil {
2021-02-24 16:50:26 +00:00
go pea . writeStateDiffAtOrFor ( blockNrOrHash )
2020-10-28 13:22:57 +00:00
return res , nil
}
}
2021-04-21 11:16:47 +00:00
if err == sql . ErrNoRows {
return code , nil
}
2020-10-28 13:22:57 +00:00
return nil , err
2020-10-28 03:04:19 +00:00
}
// GetProof returns the Merkle-proof for a given account and optionally some storage keys.
func ( pea * PublicEthAPI ) GetProof ( ctx context . Context , address common . Address , storageKeys [ ] string , blockNrOrHash rpc . BlockNumberOrHash ) ( * AccountResult , error ) {
2020-10-28 13:22:57 +00:00
proof , err := pea . localGetProof ( ctx , address , storageKeys , blockNrOrHash )
if proof != nil && err == nil {
return proof , nil
}
2021-12-30 02:29:59 +00:00
if pea . proxyOnError {
2020-10-28 13:22:57 +00:00
var res * AccountResult
if err := pea . rpc . CallContext ( ctx , & res , "eth_getProof" , address , storageKeys , blockNrOrHash ) ; res != nil && err == nil {
2021-02-24 16:50:26 +00:00
go pea . writeStateDiffAtOrFor ( blockNrOrHash )
2020-10-28 13:22:57 +00:00
return res , nil
}
}
return nil , err
}
func ( pea * PublicEthAPI ) localGetProof ( ctx context . Context , address common . Address , storageKeys [ ] string , blockNrOrHash rpc . BlockNumberOrHash ) ( * AccountResult , error ) {
2020-10-28 03:04:19 +00:00
state , _ , err := pea . B . StateAndHeaderByNumberOrHash ( ctx , blockNrOrHash )
if state == nil || err != nil {
return nil , err
2020-01-26 19:55:26 +00:00
}
2020-10-28 03:04:19 +00:00
storageTrie := state . StorageTrie ( address )
storageHash := types . EmptyRootHash
codeHash := state . GetCodeHash ( address )
storageProof := make ( [ ] StorageResult , len ( storageKeys ) )
// if we have a storageTrie, (which means the account exists), we can update the storagehash
if storageTrie != nil {
storageHash = storageTrie . Hash ( )
} else {
// no storageTrie means the account does not exist, so the codeHash is the hash of an empty bytearray.
codeHash = crypto . Keccak256Hash ( nil )
}
// create the proof for the storageKeys
for i , key := range storageKeys {
if storageTrie != nil {
proof , storageError := state . GetStorageProof ( address , common . HexToHash ( key ) )
if storageError != nil {
return nil , storageError
}
2021-02-19 20:23:45 +00:00
storageProof [ i ] = StorageResult { key , ( * hexutil . Big ) ( state . GetState ( address , common . HexToHash ( key ) ) . Big ( ) ) , toHexSlice ( proof ) }
2020-10-28 03:04:19 +00:00
} else {
storageProof [ i ] = StorageResult { key , & hexutil . Big { } , [ ] string { } }
2020-10-26 13:58:37 +00:00
}
}
2020-10-28 03:04:19 +00:00
// create the accountProof
accountProof , proofErr := state . GetProof ( address )
if proofErr != nil {
return nil , proofErr
2020-10-26 13:58:37 +00:00
}
2020-10-28 03:04:19 +00:00
return & AccountResult {
Address : address ,
2021-02-19 20:23:45 +00:00
AccountProof : toHexSlice ( accountProof ) ,
2020-10-28 03:04:19 +00:00
Balance : ( * hexutil . Big ) ( state . GetBalance ( address ) ) ,
CodeHash : codeHash ,
Nonce : hexutil . Uint64 ( state . GetNonce ( address ) ) ,
StorageHash : storageHash ,
StorageProof : storageProof ,
} , state . Error ( )
2020-01-26 19:55:26 +00:00
}
2020-09-25 13:58:18 +00:00
2021-09-29 05:27:11 +00:00
// revertError is an API error that encompassas an EVM revertal with JSON error
// code and a binary data blob.
type revertError struct {
error
reason string // revert reason hex encoded
}
// ErrorCode returns the JSON error code for a revertal.
// See: https://github.com/ethereum/wiki/wiki/JSON-RPC-Error-Codes-Improvement-Proposal
func ( e * revertError ) ErrorCode ( ) int {
return 3
}
// ErrorData returns the hex encoded revert reason.
func ( e * revertError ) ErrorData ( ) interface { } {
return e . reason
}
func newRevertError ( result * core . ExecutionResult ) * revertError {
reason , errUnpack := abi . UnpackRevert ( result . Revert ( ) )
err := errors . New ( "execution reverted" )
if errUnpack == nil {
err = fmt . Errorf ( "execution reverted: %v" , reason )
2020-10-26 13:58:37 +00:00
}
2021-09-29 05:27:11 +00:00
return & revertError {
error : err ,
reason : hexutil . Encode ( result . Revert ( ) ) ,
2020-10-20 20:33:18 +00:00
}
2020-09-25 13:58:18 +00:00
}
2021-09-29 05:27:11 +00:00
// OverrideAccount indicates the overriding fields of account during the execution
// of a message call.
// Note, state and stateDiff can't be specified at the same time. If state is
// set, message execution will only use the data in the given state. Otherwise
// if statDiff is set, all diff will be applied first and then execute the call
// message.
type OverrideAccount struct {
Nonce * hexutil . Uint64 ` json:"nonce" `
Code * hexutil . Bytes ` json:"code" `
Balance * * hexutil . Big ` json:"balance" `
State * map [ common . Hash ] common . Hash ` json:"state" `
StateDiff * map [ common . Hash ] common . Hash ` json:"stateDiff" `
}
// StateOverride is the collection of overridden accounts.
type StateOverride map [ common . Address ] OverrideAccount
// Apply overrides the fields of specified accounts into the given state.
func ( diff * StateOverride ) Apply ( state * state . StateDB ) error {
if diff == nil {
return nil
2020-09-25 13:58:18 +00:00
}
2021-09-29 05:27:11 +00:00
for addr , account := range * diff {
2020-09-25 13:58:18 +00:00
// Override account nonce.
if account . Nonce != nil {
state . SetNonce ( addr , uint64 ( * account . Nonce ) )
}
// Override account(contract) code.
if account . Code != nil {
state . SetCode ( addr , * account . Code )
}
// Override account balance.
if account . Balance != nil {
state . SetBalance ( addr , ( * big . Int ) ( * account . Balance ) )
}
if account . State != nil && account . StateDiff != nil {
2021-09-29 05:27:11 +00:00
return fmt . Errorf ( "account %s has both 'state' and 'stateDiff'" , addr . Hex ( ) )
2020-09-25 13:58:18 +00:00
}
// Replace entire state if caller requires.
if account . State != nil {
state . SetStorage ( addr , * account . State )
}
// Apply state diff into specified accounts.
if account . StateDiff != nil {
for key , value := range * account . StateDiff {
state . SetState ( addr , key , value )
}
}
}
2021-09-29 05:27:11 +00:00
return nil
}
2021-07-27 12:07:50 +00:00
2021-09-29 05:27:11 +00:00
// Call executes the given transaction on the state for the given block number.
//
// Additionally, the caller can specify a batch of contract for fields overriding.
//
// Note, this function doesn't make and changes in the state/blockchain and is
// useful to execute and retrieve values.
func ( pea * PublicEthAPI ) Call ( ctx context . Context , args CallArgs , blockNrOrHash rpc . BlockNumberOrHash , overrides * StateOverride ) ( hexutil . Bytes , error ) {
2021-12-27 18:25:54 +00:00
if pea . forwardEthCalls {
var hex hexutil . Bytes
err := pea . rpc . CallContext ( ctx , & hex , "eth_call" , args , blockNrOrHash , overrides )
return hex , err
}
2021-09-29 05:27:11 +00:00
result , err := DoCall ( ctx , pea . B , args , blockNrOrHash , overrides , 5 * time . Second , pea . B . Config . RPCGasCap . Uint64 ( ) )
2021-07-27 12:07:50 +00:00
2021-09-29 05:27:11 +00:00
// If the result contains a revert reason, try to unpack and return it.
2021-09-30 11:10:07 +00:00
if err == nil {
if len ( result . Revert ( ) ) > 0 {
err = newRevertError ( result )
} else if result . Err != nil {
err = result . Err
}
2021-09-29 05:27:11 +00:00
}
2021-07-27 12:07:50 +00:00
2021-12-30 02:29:59 +00:00
if err != nil && pea . proxyOnError {
2021-09-29 05:27:11 +00:00
var hex hexutil . Bytes
2021-09-30 13:43:50 +00:00
if err := pea . rpc . CallContext ( ctx , & hex , "eth_call" , args , blockNrOrHash , overrides ) ; hex != nil && err == nil {
2021-09-29 05:27:11 +00:00
go pea . writeStateDiffAtOrFor ( blockNrOrHash )
return hex , nil
2021-07-27 12:07:50 +00:00
}
2020-09-25 13:58:18 +00:00
}
2021-09-30 11:10:07 +00:00
return result . Return ( ) , err
2021-09-29 05:27:11 +00:00
}
2020-09-25 13:58:18 +00:00
2021-09-29 05:27:11 +00:00
func DoCall ( ctx context . Context , b * Backend , args CallArgs , blockNrOrHash rpc . BlockNumberOrHash , overrides * StateOverride , timeout time . Duration , globalGasCap uint64 ) ( * core . ExecutionResult , error ) {
defer func ( start time . Time ) {
logrus . Debugf ( "Executing EVM call finished %s runtime %s" , time . Now ( ) . String ( ) , time . Since ( start ) . String ( ) )
} ( time . Now ( ) )
2020-09-25 13:58:18 +00:00
2021-09-29 05:27:11 +00:00
state , header , err := b . StateAndHeaderByNumberOrHash ( ctx , blockNrOrHash )
if state == nil || err != nil {
return nil , err
2021-07-27 12:07:50 +00:00
}
2021-09-29 05:27:11 +00:00
if err := overrides . Apply ( state ) ; err != nil {
return nil , err
2020-09-25 13:58:18 +00:00
}
// Setup context so it may be cancelled the call has completed
// or, in case of unmetered gas, setup a context with a timeout.
var cancel context . CancelFunc
if timeout > 0 {
ctx , cancel = context . WithTimeout ( ctx , timeout )
} else {
ctx , cancel = context . WithCancel ( ctx )
}
// Make sure the context is cancelled when the call has completed
// this makes sure resources are cleaned up.
defer cancel ( )
// Get a new instance of the EVM.
2021-09-29 05:27:11 +00:00
msg , err := args . ToMessage ( globalGasCap , header . BaseFee )
if err != nil {
return nil , err
}
evm , vmError , err := b . GetEVM ( ctx , msg , state , header )
2020-09-25 13:58:18 +00:00
if err != nil {
2021-09-29 05:27:11 +00:00
return nil , err
2020-09-25 13:58:18 +00:00
}
2021-09-29 05:27:11 +00:00
2020-09-25 13:58:18 +00:00
// Wait for the context to be done and cancel the evm. Even if the
// EVM has finished, cancelling may be done (repeatedly)
go func ( ) {
<- ctx . Done ( )
evm . Cancel ( )
} ( )
2021-09-29 05:27:11 +00:00
// Execute the message.
2020-09-25 13:58:18 +00:00
gp := new ( core . GasPool ) . AddGas ( math . MaxUint64 )
2021-02-19 20:23:45 +00:00
result , err := core . ApplyMessage ( evm , msg , gp )
2021-09-29 05:27:11 +00:00
if err := vmError ( ) ; err != nil {
return nil , err
2021-02-19 20:23:45 +00:00
}
2021-09-29 05:27:11 +00:00
2020-09-25 13:58:18 +00:00
// If the timer caused an abort, return an appropriate error message
if evm . Cancelled ( ) {
2021-09-29 05:27:11 +00:00
return nil , fmt . Errorf ( "execution aborted (timeout = %v)" , timeout )
}
if err != nil {
return result , fmt . Errorf ( "err: %w (supplied gas %d)" , err , msg . Gas ( ) )
2020-09-25 13:58:18 +00:00
}
2021-09-29 05:27:11 +00:00
return result , nil
2020-09-25 13:58:18 +00:00
}
2020-10-26 13:58:37 +00:00
2021-02-24 22:20:06 +00:00
// writeStateDiffAtOrFor calls out to the proxy statediffing geth client to fill in a gap in the index
2021-02-24 16:50:26 +00:00
func ( pea * PublicEthAPI ) writeStateDiffAtOrFor ( blockNrOrHash rpc . BlockNumberOrHash ) {
2021-02-24 22:20:06 +00:00
// short circuit right away if the proxy doesn't support diffing
if ! pea . supportsStateDiff {
return
}
2021-02-24 16:50:26 +00:00
if blockNr , ok := blockNrOrHash . Number ( ) ; ok {
pea . writeStateDiffAt ( blockNr . Int64 ( ) )
return
}
if hash , ok := blockNrOrHash . Hash ( ) ; ok {
pea . writeStateDiffFor ( hash )
}
}
2021-02-24 22:20:06 +00:00
// writeStateDiffWithCriteria calls out to the proxy statediffing geth client to fill in a gap in the index
2021-04-12 04:05:03 +00:00
func ( pea * PublicEthAPI ) writeStateDiffWithCriteria ( crit filters . FilterCriteria ) {
2021-02-24 22:20:06 +00:00
// short circuit right away if the proxy doesn't support diffing
if ! pea . supportsStateDiff {
return
}
2021-02-24 16:50:26 +00:00
if crit . BlockHash != nil {
pea . writeStateDiffFor ( * crit . BlockHash )
return
}
var start , end int64
if crit . FromBlock != nil {
start = crit . FromBlock . Int64 ( )
}
if crit . ToBlock != nil {
end = crit . ToBlock . Int64 ( )
} else {
end = start
}
for i := start ; i <= end ; i ++ {
pea . writeStateDiffAt ( i )
}
}
2021-02-24 22:20:06 +00:00
// writeStateDiffAt calls out to the proxy statediffing geth client to fill in a gap in the index
2021-02-24 16:50:26 +00:00
func ( pea * PublicEthAPI ) writeStateDiffAt ( height int64 ) {
if ! pea . supportsStateDiff {
return
}
// we use a separate context than the one provided by the client
2021-02-27 16:31:40 +00:00
ctx , cancel := context . WithTimeout ( context . Background ( ) , 240 * time . Second )
2021-02-24 16:50:26 +00:00
defer cancel ( )
var data json . RawMessage
params := statediff . Params {
IntermediateStateNodes : true ,
IntermediateStorageNodes : true ,
IncludeBlock : true ,
IncludeReceipts : true ,
IncludeTD : true ,
IncludeCode : true ,
}
if err := pea . rpc . CallContext ( ctx , & data , "statediff_writeStateDiffAt" , uint64 ( height ) , params ) ; err != nil {
logrus . Errorf ( "writeStateDiffAt %d faild with err %s" , height , err . Error ( ) )
}
}
2021-02-24 22:20:06 +00:00
// writeStateDiffFor calls out to the proxy statediffing geth client to fill in a gap in the index
2021-02-24 16:50:26 +00:00
func ( pea * PublicEthAPI ) writeStateDiffFor ( blockHash common . Hash ) {
if ! pea . supportsStateDiff {
return
}
// we use a separate context than the one provided by the client
2021-02-27 16:31:40 +00:00
ctx , cancel := context . WithTimeout ( context . Background ( ) , 240 * time . Second )
2021-02-24 16:50:26 +00:00
defer cancel ( )
var data json . RawMessage
params := statediff . Params {
IntermediateStateNodes : true ,
IntermediateStorageNodes : true ,
IncludeBlock : true ,
IncludeReceipts : true ,
IncludeTD : true ,
IncludeCode : true ,
}
if err := pea . rpc . CallContext ( ctx , & data , "statediff_writeStateDiffFor" , blockHash , params ) ; err != nil {
logrus . Errorf ( "writeStateDiffFor %s faild with err %s" , blockHash . Hex ( ) , err . Error ( ) )
}
}
2020-10-28 13:22:57 +00:00
// rpcMarshalBlock uses the generalized output filler, then adds the total difficulty field
func ( pea * PublicEthAPI ) rpcMarshalBlock ( b * types . Block , inclTx bool , fullTx bool ) ( map [ string ] interface { } , error ) {
2021-10-07 09:35:11 +00:00
fields , err := RPCMarshalBlock ( b , inclTx , fullTx )
2020-10-28 13:22:57 +00:00
if err != nil {
2022-08-25 10:24:32 +00:00
logrus . Errorf ( "error RPC marshalling block with hash %s: %s" , b . Hash ( ) . String ( ) , err )
2020-10-28 13:22:57 +00:00
return nil , err
2020-10-26 13:58:37 +00:00
}
2020-10-29 19:59:09 +00:00
if inclTx {
td , err := pea . B . GetTd ( b . Hash ( ) )
if err != nil {
2022-08-25 10:24:32 +00:00
logrus . Errorf ( "error getting td for block with hash and number %s, %s: %s" , b . Hash ( ) . String ( ) , b . Number ( ) . String ( ) , err )
2020-10-29 19:59:09 +00:00
return nil , err
}
fields [ "totalDifficulty" ] = ( * hexutil . Big ) ( td )
2020-10-26 13:58:37 +00:00
}
2020-10-28 13:22:57 +00:00
return fields , err
2020-10-26 13:58:37 +00:00
}
2020-10-28 13:22:57 +00:00
// rpcMarshalBlockWithUncleHashes uses the generalized output filler, then adds the total difficulty field
func ( pea * PublicEthAPI ) rpcMarshalBlockWithUncleHashes ( b * types . Block , uncleHashes [ ] common . Hash , inclTx bool , fullTx bool ) ( map [ string ] interface { } , error ) {
2021-10-07 09:35:11 +00:00
fields , err := RPCMarshalBlockWithUncleHashes ( b , uncleHashes , inclTx , fullTx )
2020-10-28 13:22:57 +00:00
if err != nil {
return nil , err
2020-10-26 13:58:37 +00:00
}
2020-10-28 13:22:57 +00:00
td , err := pea . B . GetTd ( b . Hash ( ) )
if err != nil {
return nil , err
}
fields [ "totalDifficulty" ] = ( * hexutil . Big ) ( td )
return fields , err
2020-10-26 13:58:37 +00:00
}
2021-02-19 20:23:45 +00:00
// toHexSlice creates a slice of hex-strings based on []byte.
func toHexSlice ( b [ ] [ ] byte ) [ ] string {
r := make ( [ ] string , len ( b ) )
for i := range b {
r [ i ] = hexutil . Encode ( b [ i ] )
}
return r
}
2021-09-01 07:02:28 +00:00
// decomposeLogs return logs from LogResult.
func decomposeLogs ( logCIDs [ ] LogResult ) ( [ ] * types . Log , error ) {
logs := make ( [ ] * types . Log , len ( logCIDs ) )
for i , l := range logCIDs {
topics := make ( [ ] common . Hash , 0 )
if l . Topic0 != "" {
topics = append ( topics , common . HexToHash ( l . Topic0 ) )
}
if l . Topic1 != "" {
topics = append ( topics , common . HexToHash ( l . Topic1 ) )
}
if l . Topic2 != "" {
topics = append ( topics , common . HexToHash ( l . Topic2 ) )
}
if l . Topic3 != "" {
topics = append ( topics , common . HexToHash ( l . Topic3 ) )
}
// TODO: should we convert string to uint ?
blockNum , err := strconv . ParseUint ( l . BlockNumber , 10 , 64 )
if err != nil {
return nil , err
}
logs [ i ] = & types . Log {
Address : common . HexToAddress ( l . Address ) ,
Topics : topics ,
Data : l . Data ,
BlockNumber : blockNum ,
TxHash : common . HexToHash ( l . TxHash ) ,
TxIndex : uint ( l . TxnIndex ) ,
BlockHash : common . HexToHash ( l . BlockHash ) ,
Index : uint ( l . Index ) ,
}
}
return logs , nil
}