2020-10-12 14:17:34 +00:00
package commands
2020-10-14 15:59:42 +00:00
import (
"context"
2021-06-04 12:27:21 +00:00
"fmt"
"math/big"
"time"
2020-10-12 14:17:34 +00:00
2021-05-20 18:25:53 +00:00
"github.com/ledgerwatch/erigon/common"
"github.com/ledgerwatch/erigon/common/hexutil"
2021-06-04 12:27:21 +00:00
"github.com/ledgerwatch/erigon/common/math"
"github.com/ledgerwatch/erigon/core"
2021-05-20 18:25:53 +00:00
"github.com/ledgerwatch/erigon/core/rawdb"
2021-06-04 12:27:21 +00:00
"github.com/ledgerwatch/erigon/core/state"
"github.com/ledgerwatch/erigon/core/types"
"github.com/ledgerwatch/erigon/core/vm"
2021-08-10 02:48:56 +00:00
"github.com/ledgerwatch/erigon/ethdb"
2021-05-20 18:25:53 +00:00
"github.com/ledgerwatch/erigon/rpc"
"github.com/ledgerwatch/erigon/turbo/adapter/ethapi"
2021-06-04 12:27:21 +00:00
"github.com/ledgerwatch/erigon/turbo/rpchelper"
"github.com/ledgerwatch/erigon/turbo/transactions"
2021-07-29 10:23:23 +00:00
"github.com/ledgerwatch/log/v3"
2021-06-04 12:27:21 +00:00
"golang.org/x/crypto/sha3"
2020-10-14 15:59:42 +00:00
)
2021-06-04 12:27:21 +00:00
func ( api * APIImpl ) CallBundle ( ctx context . Context , txHashes [ ] common . Hash , stateBlockNumberOrHash rpc . BlockNumberOrHash , timeoutMilliSecondsPtr * int64 ) ( map [ string ] interface { } , error ) {
2021-06-25 16:25:12 +00:00
tx , err := api . db . BeginRo ( ctx )
2021-06-04 12:27:21 +00:00
if err != nil {
return nil , err
}
2021-06-25 16:25:12 +00:00
defer tx . Rollback ( )
2021-06-04 12:27:21 +00:00
2021-06-25 16:25:12 +00:00
chainConfig , err := api . chainConfig ( tx )
2021-06-04 12:27:21 +00:00
if err != nil {
return nil , err
}
if len ( txHashes ) == 0 {
return nil , nil
}
var txs types . Transactions
for _ , txHash := range txHashes {
2022-01-07 13:52:38 +00:00
blockNum , ok , err := api . txnLookup ( ctx , tx , txHash )
2022-01-06 11:22:59 +00:00
if err != nil {
return nil , err
}
2022-01-07 13:52:38 +00:00
if ! ok {
2022-01-06 11:22:59 +00:00
return nil , nil
}
2022-01-07 13:52:38 +00:00
block , err := api . blockByNumberWithSenders ( tx , blockNum )
2021-07-11 05:25:21 +00:00
if err != nil {
return nil , err
}
2022-01-07 13:52:38 +00:00
if block == nil {
return nil , nil
}
var txn types . Transaction
for _ , transaction := range block . Transactions ( ) {
if transaction . Hash ( ) == txHash {
txn = transaction
break
}
}
2021-06-04 12:27:21 +00:00
if txn == nil {
return nil , nil // not error, see https://github.com/ledgerwatch/turbo-geth/issues/1645
}
txs = append ( txs , txn )
}
2021-10-05 01:14:04 +00:00
defer func ( start time . Time ) { log . Trace ( "Executing EVM call finished" , "runtime" , time . Since ( start ) ) } ( time . Now ( ) )
2021-06-04 12:27:21 +00:00
2022-02-23 23:42:14 +00:00
stateBlockNumber , hash , latest , err := rpchelper . GetBlockNumber ( stateBlockNumberOrHash , tx , api . filters )
2021-06-04 12:27:21 +00:00
if err != nil {
return nil , err
}
var stateReader state . StateReader
2022-02-23 23:42:14 +00:00
if latest {
2021-09-29 01:36:25 +00:00
cacheView , err := api . stateCache . View ( ctx , tx )
if err != nil {
return nil , err
}
stateReader = state . NewCachedReader2 ( cacheView , tx )
2021-06-04 12:27:21 +00:00
} else {
2021-07-24 07:14:11 +00:00
stateReader = state . NewPlainState ( tx , stateBlockNumber )
2021-06-04 12:27:21 +00:00
}
st := state . New ( stateReader )
2021-06-25 16:25:12 +00:00
parent := rawdb . ReadHeader ( tx , hash , stateBlockNumber )
2021-06-04 12:27:21 +00:00
if parent == nil {
return nil , fmt . Errorf ( "block %d(%x) not found" , stateBlockNumber , hash )
}
blockNumber := stateBlockNumber + 1
timestamp := parent . Time // Dont care about the timestamp
coinbase := parent . Coinbase
header := & types . Header {
ParentHash : parent . Hash ( ) ,
Number : big . NewInt ( int64 ( blockNumber ) ) ,
GasLimit : parent . GasLimit ,
Time : timestamp ,
Difficulty : parent . Difficulty ,
Coinbase : coinbase ,
}
// Get a new instance of the EVM
signer := types . MakeSigner ( chainConfig , blockNumber )
2022-05-26 16:20:34 +00:00
rules := chainConfig . Rules ( blockNumber )
firstMsg , err := txs [ 0 ] . AsMessage ( * signer , nil , rules )
2021-06-04 12:27:21 +00:00
if err != nil {
return nil , err
}
2021-10-06 09:09:47 +00:00
contractHasTEVM := func ( contractHash common . Hash ) ( bool , error ) { return false , nil }
if api . TevmEnabled {
contractHasTEVM = ethdb . GetHasTEVM ( tx )
}
2022-06-12 11:44:01 +00:00
blockCtx , txCtx := transactions . GetEvmContext ( firstMsg , header , stateBlockNumberOrHash . RequireCanonical , tx , contractHasTEVM , api . _blockReader )
2021-06-04 12:27:21 +00:00
evm := vm . NewEVM ( blockCtx , txCtx , st , chainConfig , vm . Config { Debug : false } )
timeoutMilliSeconds := int64 ( 5000 )
if timeoutMilliSecondsPtr != nil {
timeoutMilliSeconds = * timeoutMilliSecondsPtr
}
timeout := time . Millisecond * time . Duration ( timeoutMilliSeconds )
// 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 ( )
// 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 ( )
} ( )
// Setup the gas pool (also for unmetered requests)
// and apply the message.
gp := new ( core . GasPool ) . AddGas ( math . MaxUint64 )
results := [ ] map [ string ] interface { } { }
bundleHash := sha3 . NewLegacyKeccak256 ( )
2021-06-25 16:25:12 +00:00
for _ , txn := range txs {
2022-05-26 16:20:34 +00:00
msg , err := txn . AsMessage ( * signer , nil , rules )
2021-06-04 12:27:21 +00:00
if err != nil {
return nil , err
}
// Execute the transaction message
result , err := core . ApplyMessage ( evm , msg , gp , true /* refunds */ , false /* gasBailout */ )
if err != nil {
return nil , err
}
// If the timer caused an abort, return an appropriate error message
if evm . Cancelled ( ) {
return nil , fmt . Errorf ( "execution aborted (timeout = %v)" , timeout )
}
2021-06-25 16:25:12 +00:00
txHash := txn . Hash ( ) . String ( )
2021-06-04 12:27:21 +00:00
jsonResult := map [ string ] interface { } {
"txHash" : txHash ,
"gasUsed" : result . UsedGas ,
}
2021-06-25 16:25:12 +00:00
bundleHash . Write ( txn . Hash ( ) . Bytes ( ) )
2021-06-04 12:27:21 +00:00
if result . Err != nil {
jsonResult [ "error" ] = result . Err . Error ( )
} else {
jsonResult [ "value" ] = common . BytesToHash ( result . Return ( ) )
}
results = append ( results , jsonResult )
}
ret := map [ string ] interface { } { }
ret [ "results" ] = results
2021-06-25 16:25:12 +00:00
ret [ "bundleHash" ] = hexutil . Encode ( bundleHash . Sum ( nil ) )
2021-06-04 12:27:21 +00:00
return ret , nil
}
2020-10-24 17:03:52 +00:00
// GetBlockByNumber implements eth_getBlockByNumber. Returns information about a block given the block's number.
2020-10-14 15:59:42 +00:00
func ( api * APIImpl ) GetBlockByNumber ( ctx context . Context , number rpc . BlockNumber , fullTx bool ) ( map [ string ] interface { } , error ) {
2021-04-03 06:26:00 +00:00
tx , err := api . db . BeginRo ( ctx )
2020-10-14 15:59:42 +00:00
if err != nil {
return nil , err
}
defer tx . Rollback ( )
2021-09-29 06:51:51 +00:00
b , err := api . blockByRPCNumber ( number , tx )
2020-10-24 06:57:09 +00:00
if err != nil {
return nil , err
}
2021-05-17 12:15:19 +00:00
if b == nil {
return nil , nil
2020-10-14 15:59:42 +00:00
}
2021-05-17 12:15:19 +00:00
additionalFields := make ( map [ string ] interface { } )
td , err := rawdb . ReadTd ( tx , b . Hash ( ) , b . NumberU64 ( ) )
2020-10-24 06:57:09 +00:00
if err != nil {
return nil , err
}
2022-07-01 11:12:01 +00:00
if td != nil {
additionalFields [ "totalDifficulty" ] = ( * hexutil . Big ) ( td )
}
2022-07-07 08:40:50 +00:00
chainConfig , err := api . chainConfig ( tx )
if err != nil {
return nil , err
}
var borTx types . Transaction
2022-07-09 03:15:22 +00:00
var borTxHash common . Hash
2022-07-07 08:40:50 +00:00
if chainConfig . Bor != nil {
2022-07-09 03:15:22 +00:00
borTx , _ , _ , _ = rawdb . ReadBorTransactionForBlock ( tx , b )
2022-07-07 08:40:50 +00:00
if borTx != nil {
2022-07-09 03:15:22 +00:00
borTxHash = types . ComputeBorTxHash ( b . NumberU64 ( ) , b . Hash ( ) )
2022-07-07 08:40:50 +00:00
}
}
2022-07-09 03:15:22 +00:00
response , err := ethapi . RPCMarshalBlockEx ( b , true , fullTx , borTx , borTxHash , additionalFields )
2020-10-14 15:59:42 +00:00
if err == nil && number == rpc . PendingBlockNumber {
// Pending blocks need to nil out a few fields
for _ , field := range [ ] string { "hash" , "nonce" , "miner" } {
response [ field ] = nil
}
}
return response , err
}
2020-10-24 17:03:52 +00:00
// GetBlockByHash implements eth_getBlockByHash. Returns information about a block given the block's hash.
2020-11-06 07:59:50 +00:00
func ( api * APIImpl ) GetBlockByHash ( ctx context . Context , numberOrHash rpc . BlockNumberOrHash , fullTx bool ) ( map [ string ] interface { } , error ) {
if numberOrHash . BlockHash == nil {
// some web3.js based apps (like ethstats client) for some reason call
// eth_getBlockByHash with a block number as a parameter
// so no matter how weird that is, we would love to support that.
2021-04-01 05:15:22 +00:00
if numberOrHash . BlockNumber == nil {
2021-05-26 10:35:39 +00:00
return nil , nil // not error, see https://github.com/ledgerwatch/erigon/issues/1645
2020-11-06 07:59:50 +00:00
}
2021-04-01 05:15:22 +00:00
return api . GetBlockByNumber ( ctx , * numberOrHash . BlockNumber , fullTx )
2020-11-06 07:59:50 +00:00
}
hash := * numberOrHash . BlockHash
2021-04-03 06:26:00 +00:00
tx , err := api . db . BeginRo ( ctx )
2020-10-14 15:59:42 +00:00
if err != nil {
return nil , err
}
defer tx . Rollback ( )
additionalFields := make ( map [ string ] interface { } )
2021-09-29 06:51:51 +00:00
block , err := api . blockByHashWithSenders ( tx , hash )
2020-10-24 06:57:09 +00:00
if err != nil {
return nil , err
}
2020-10-14 15:59:42 +00:00
if block == nil {
2021-05-26 10:35:39 +00:00
return nil , nil // not error, see https://github.com/ledgerwatch/erigon/issues/1645
2020-10-14 15:59:42 +00:00
}
number := block . NumberU64 ( )
2021-04-08 11:03:45 +00:00
td , err := rawdb . ReadTd ( tx , hash , number )
2020-10-24 06:57:09 +00:00
if err != nil {
return nil , err
}
additionalFields [ "totalDifficulty" ] = ( * hexutil . Big ) ( td )
2022-07-09 03:15:22 +00:00
chainConfig , err := api . chainConfig ( tx )
if err != nil {
return nil , err
}
var borTx types . Transaction
var borTxHash common . Hash
if chainConfig . Bor != nil {
borTx , _ , _ , _ = rawdb . ReadBorTransactionForBlock ( tx , block )
if borTx != nil {
borTxHash = types . ComputeBorTxHash ( block . NumberU64 ( ) , block . Hash ( ) )
}
}
response , err := ethapi . RPCMarshalBlockEx ( block , true , fullTx , borTx , borTxHash , additionalFields )
2020-10-14 15:59:42 +00:00
if err == nil && int64 ( number ) == rpc . PendingBlockNumber . Int64 ( ) {
// Pending blocks need to nil out a few fields
for _ , field := range [ ] string { "hash" , "nonce" , "miner" } {
response [ field ] = nil
}
}
return response , err
}
2020-10-24 17:03:52 +00:00
// GetBlockTransactionCountByNumber implements eth_getBlockTransactionCountByNumber. Returns the number of transactions in a block given the block's block number.
2020-10-14 15:59:42 +00:00
func ( api * APIImpl ) GetBlockTransactionCountByNumber ( ctx context . Context , blockNr rpc . BlockNumber ) ( * hexutil . Uint , error ) {
2021-04-03 06:26:00 +00:00
tx , err := api . db . BeginRo ( ctx )
2020-10-14 15:59:42 +00:00
if err != nil {
return nil , err
}
defer tx . Rollback ( )
2021-05-17 12:15:19 +00:00
if blockNr == rpc . PendingBlockNumber {
2021-09-29 06:51:51 +00:00
b , err := api . blockByRPCNumber ( blockNr , tx )
2021-05-17 12:15:19 +00:00
if err != nil {
return nil , err
}
if b == nil {
return nil , nil
}
n := hexutil . Uint ( len ( b . Transactions ( ) ) )
return & n , nil
}
2022-06-14 13:29:49 +00:00
blockNum , _ , _ , err := rpchelper . GetBlockNumber ( rpc . BlockNumberOrHashWithNumber ( blockNr ) , tx , api . filters )
2020-10-14 15:59:42 +00:00
if err != nil {
return nil , err
}
2021-05-17 12:15:19 +00:00
body , _ , txAmount , err := rawdb . ReadBodyByNumber ( tx , blockNum )
2020-10-24 06:57:09 +00:00
if err != nil {
return nil , err
}
2021-05-17 12:15:19 +00:00
if body == nil {
return nil , nil
2020-10-14 15:59:42 +00:00
}
2021-05-17 12:15:19 +00:00
n := hexutil . Uint ( txAmount )
2020-10-14 15:59:42 +00:00
return & n , nil
}
2020-10-24 17:03:52 +00:00
// GetBlockTransactionCountByHash implements eth_getBlockTransactionCountByHash. Returns the number of transactions in a block given the block's block hash.
2020-10-14 15:59:42 +00:00
func ( api * APIImpl ) GetBlockTransactionCountByHash ( ctx context . Context , blockHash common . Hash ) ( * hexutil . Uint , error ) {
2021-04-03 06:26:00 +00:00
tx , err := api . db . BeginRo ( ctx )
2020-10-14 15:59:42 +00:00
if err != nil {
return nil , err
}
defer tx . Rollback ( )
2021-05-17 12:15:19 +00:00
num := rawdb . ReadHeaderNumber ( tx , blockHash )
if num == nil {
return nil , nil
2020-10-24 06:57:09 +00:00
}
2021-08-15 10:08:28 +00:00
body , _ , txAmount := rawdb . ReadBody ( tx , blockHash , * num )
2021-05-17 12:15:19 +00:00
if body == nil {
return nil , nil
2020-10-14 15:59:42 +00:00
}
2021-05-17 12:15:19 +00:00
n := hexutil . Uint ( txAmount )
2020-10-14 15:59:42 +00:00
return & n , nil
}