2020-10-18 19:44:28 +00:00
package commands
import (
2022-07-15 14:04:23 +00:00
"bytes"
2020-10-18 19:44:28 +00:00
"context"
2022-07-01 14:36:44 +00:00
"errors"
2020-10-18 19:44:28 +00:00
"fmt"
2022-02-20 10:45:29 +00:00
"sort"
2020-10-18 19:44:28 +00:00
2022-07-15 14:04:23 +00:00
"github.com/holiman/uint256"
2022-02-20 10:45:29 +00:00
"github.com/ledgerwatch/erigon-lib/kv"
2021-05-20 18:25:53 +00:00
"github.com/ledgerwatch/erigon/common"
2022-07-15 14:04:23 +00:00
"github.com/ledgerwatch/erigon/common/changeset"
"github.com/ledgerwatch/erigon/common/dbutils"
"github.com/ledgerwatch/erigon/common/hexutil"
2021-05-20 18:25:53 +00:00
"github.com/ledgerwatch/erigon/core/rawdb"
"github.com/ledgerwatch/erigon/core/types"
2022-07-15 14:04:23 +00:00
"github.com/ledgerwatch/erigon/core/types/accounts"
2022-02-20 10:45:29 +00:00
"github.com/ledgerwatch/erigon/internal/ethapi"
2021-05-20 18:25:53 +00:00
"github.com/ledgerwatch/erigon/rpc"
2022-06-14 13:29:49 +00:00
"github.com/ledgerwatch/erigon/turbo/rpchelper"
2020-10-18 19:44:28 +00:00
)
2021-05-26 10:35:39 +00:00
// GetHeaderByNumber implements erigon_getHeaderByNumber. Returns a block's header given a block number ignoring the block's transaction and uncle list (may be faster).
func ( api * ErigonImpl ) GetHeaderByNumber ( ctx context . Context , blockNumber rpc . BlockNumber ) ( * types . Header , error ) {
2021-03-30 07:09:00 +00:00
// Pending block is only known by the miner
if blockNumber == rpc . PendingBlockNumber {
2021-05-17 12:15:19 +00:00
block := api . pendingBlock ( )
if block == nil {
return nil , nil
}
2021-03-30 07:09:00 +00:00
return block . Header ( ) , nil
}
2021-04-03 06:26:00 +00:00
tx , err := api . db . BeginRo ( ctx )
2020-10-18 19:44:28 +00:00
if err != nil {
return nil , err
}
defer tx . Rollback ( )
2022-06-14 13:29:49 +00:00
blockNum , _ , _ , err := rpchelper . GetBlockNumber ( rpc . BlockNumberOrHashWithNumber ( blockNumber ) , tx , api . filters )
2021-08-24 02:13:51 +00:00
if err != nil {
return nil , err
}
2022-07-01 14:36:44 +00:00
header , err := api . _blockReader . HeaderByNumber ( ctx , tx , blockNum )
if err != nil {
return nil , err
}
2020-10-18 19:44:28 +00:00
if header == nil {
2021-08-24 02:13:51 +00:00
return nil , fmt . Errorf ( "block header not found: %d" , blockNum )
2020-10-18 19:44:28 +00:00
}
return header , nil
}
2021-05-26 10:35:39 +00:00
// GetHeaderByHash implements erigon_getHeaderByHash. Returns a block's header given a block's hash.
func ( api * ErigonImpl ) GetHeaderByHash ( ctx context . Context , hash common . Hash ) ( * types . Header , error ) {
2021-04-03 06:26:00 +00:00
tx , err := api . db . BeginRo ( ctx )
2020-10-18 19:44:28 +00:00
if err != nil {
return nil , err
}
defer tx . Rollback ( )
2022-07-01 14:36:44 +00:00
header , err := api . _blockReader . HeaderByHash ( ctx , tx , hash )
2020-10-24 06:57:09 +00:00
if err != nil {
return nil , err
}
2020-10-18 19:44:28 +00:00
if header == nil {
return nil , fmt . Errorf ( "block header not found: %s" , hash . String ( ) )
}
return header , nil
}
2022-02-20 10:45:29 +00:00
2022-02-23 13:52:19 +00:00
func ( api * ErigonImpl ) GetBlockByTimestamp ( ctx context . Context , timeStamp rpc . Timestamp , fullTx bool ) ( map [ string ] interface { } , error ) {
2022-02-20 10:45:29 +00:00
tx , err := api . db . BeginRo ( ctx )
if err != nil {
return nil , err
}
defer tx . Rollback ( )
2022-02-23 13:52:19 +00:00
uintTimestamp := timeStamp . TurnIntoUint64 ( )
2022-02-20 10:45:29 +00:00
currentHeader := rawdb . ReadCurrentHeader ( tx )
currenttHeaderTime := currentHeader . Time
highestNumber := currentHeader . Number . Uint64 ( )
2022-07-01 14:36:44 +00:00
firstHeader , err := api . _blockReader . HeaderByNumber ( ctx , tx , 0 )
if err != nil {
return nil , err
}
if firstHeader == nil {
return nil , errors . New ( "no genesis header found" )
}
2022-02-20 10:45:29 +00:00
firstHeaderTime := firstHeader . Time
2022-02-23 13:52:19 +00:00
if currenttHeaderTime <= uintTimestamp {
2022-02-20 10:45:29 +00:00
blockResponse , err := buildBlockResponse ( tx , highestNumber , fullTx )
if err != nil {
return nil , err
}
return blockResponse , nil
}
2022-02-23 13:52:19 +00:00
if firstHeaderTime >= uintTimestamp {
2022-02-20 10:45:29 +00:00
blockResponse , err := buildBlockResponse ( tx , 0 , fullTx )
if err != nil {
return nil , err
}
return blockResponse , nil
}
blockNum := sort . Search ( int ( currentHeader . Number . Uint64 ( ) ) , func ( blockNum int ) bool {
2022-07-01 14:36:44 +00:00
currentHeader , err := api . _blockReader . HeaderByNumber ( ctx , tx , uint64 ( blockNum ) )
if err != nil {
return false
}
if currentHeader == nil {
return false
}
2022-02-20 10:45:29 +00:00
2022-02-23 13:52:19 +00:00
return currentHeader . Time >= uintTimestamp
2022-02-20 10:45:29 +00:00
} )
2022-07-01 14:36:44 +00:00
resultingHeader , err := api . _blockReader . HeaderByNumber ( ctx , tx , uint64 ( blockNum ) )
if err != nil {
return nil , err
}
if resultingHeader == nil {
return nil , fmt . Errorf ( "no header found with header number: %d" , blockNum )
}
2022-02-20 10:45:29 +00:00
2022-02-23 13:52:19 +00:00
if resultingHeader . Time > uintTimestamp {
2022-02-20 10:45:29 +00:00
response , err := buildBlockResponse ( tx , uint64 ( blockNum ) - 1 , fullTx )
if err != nil {
return nil , err
}
return response , nil
}
response , err := buildBlockResponse ( tx , uint64 ( blockNum ) , fullTx )
if err != nil {
return nil , err
}
return response , nil
}
func buildBlockResponse ( db kv . Tx , blockNum uint64 , fullTx bool ) ( map [ string ] interface { } , error ) {
block , err := rawdb . ReadBlockByNumber ( db , blockNum )
if err != nil {
return nil , err
}
if block == nil {
return nil , nil
}
response , err := ethapi . RPCMarshalBlock ( block , true , fullTx )
if err == nil && rpc . BlockNumber ( block . NumberU64 ( ) ) == rpc . PendingBlockNumber {
// Pending blocks need to nil out a few fields
for _ , field := range [ ] string { "hash" , "nonce" , "miner" } {
response [ field ] = nil
}
}
return response , err
}
2022-07-15 14:04:23 +00:00
func ( api * ErigonImpl ) GetBalanceChangesInBlock ( ctx context . Context , blockNrOrHash rpc . BlockNumberOrHash ) ( map [ common . Address ] * hexutil . Big , error ) {
tx , err := api . db . BeginRo ( ctx )
if err != nil {
return nil , err
}
defer tx . Rollback ( )
blockNumber , _ , _ , err := rpchelper . GetBlockNumber ( blockNrOrHash , tx , api . filters )
if err != nil {
return nil , err
}
c , err := tx . Cursor ( kv . AccountChangeSet )
if err != nil {
return nil , err
}
defer c . Close ( )
startkey := dbutils . EncodeBlockNumber ( blockNumber )
decodeFn := changeset . Mapper [ kv . AccountChangeSet ] . Decode
balancesMapping := make ( map [ common . Address ] * hexutil . Big )
2022-09-05 14:31:00 +00:00
newReader , err := rpchelper . CreateStateReader ( ctx , tx , blockNrOrHash , api . filters , api . stateCache , api . historyV2 ( tx ) , api . _agg , api . _txNums )
2022-07-15 14:04:23 +00:00
if err != nil {
return nil , err
}
2022-09-05 14:31:00 +00:00
for dbKey , dbValue , err := c . Seek ( startkey ) ; bytes . Equal ( dbKey , startkey ) && dbKey != nil ; dbKey , dbValue , err = c . Next ( ) {
if err != nil {
return nil , err
}
2022-07-15 14:04:23 +00:00
_ , addressBytes , v , err := decodeFn ( dbKey , dbValue )
if err != nil {
return nil , err
}
var oldAcc accounts . Account
if err = oldAcc . DecodeForStorage ( v ) ; err != nil {
return nil , err
}
oldBalance := oldAcc . Balance
address := common . BytesToAddress ( addressBytes )
newAcc , err := newReader . ReadAccountData ( address )
if err != nil {
return nil , err
}
newBalance := uint256 . NewInt ( 0 )
if newAcc != nil {
newBalance = & newAcc . Balance
}
if ! oldBalance . Eq ( newBalance ) {
newBalanceDesc := ( * hexutil . Big ) ( newBalance . ToBig ( ) )
balancesMapping [ address ] = newBalanceDesc
}
}
return balancesMapping , nil
}