2021-07-28 09:47:38 +07:00
package privateapi
2020-08-12 16:47:59 +03:00
import (
2023-01-24 13:37:03 +01:00
"bytes"
2020-08-12 16:47:59 +03:00
"context"
2022-02-18 09:45:20 +07:00
"errors"
2021-11-22 11:36:52 +01:00
"fmt"
2021-11-20 17:27:11 +01:00
"math/big"
2023-02-21 10:24:59 +01:00
"reflect"
2021-11-29 15:58:46 +01:00
"sync"
2022-02-21 14:45:10 +01:00
"time"
2020-08-12 16:47:59 +03:00
2022-01-04 18:37:36 +01:00
"github.com/holiman/uint256"
2023-01-30 08:52:29 +01:00
"github.com/ledgerwatch/log/v3"
"google.golang.org/protobuf/types/known/emptypb"
2023-01-13 18:12:18 +00:00
"github.com/ledgerwatch/erigon-lib/chain"
libcommon "github.com/ledgerwatch/erigon-lib/common"
2021-07-01 22:31:14 +01:00
"github.com/ledgerwatch/erigon-lib/gointerfaces"
"github.com/ledgerwatch/erigon-lib/gointerfaces/remote"
types2 "github.com/ledgerwatch/erigon-lib/gointerfaces/types"
2021-11-14 11:08:52 +07:00
"github.com/ledgerwatch/erigon-lib/kv"
2022-09-20 13:15:56 +01:00
2021-05-21 01:25:53 +07:00
"github.com/ledgerwatch/erigon/common"
2021-11-21 21:52:36 +01:00
"github.com/ledgerwatch/erigon/consensus/serenity"
2022-02-23 21:27:38 +01:00
"github.com/ledgerwatch/erigon/core"
"github.com/ledgerwatch/erigon/core/rawdb"
2021-05-21 01:25:53 +07:00
"github.com/ledgerwatch/erigon/core/types"
"github.com/ledgerwatch/erigon/params"
"github.com/ledgerwatch/erigon/rlp"
2022-02-23 21:27:38 +01:00
"github.com/ledgerwatch/erigon/rpc"
2022-06-13 15:43:09 +02:00
"github.com/ledgerwatch/erigon/turbo/builder"
2022-03-22 17:49:12 +01:00
"github.com/ledgerwatch/erigon/turbo/engineapi"
2022-05-26 10:31:06 +07:00
"github.com/ledgerwatch/erigon/turbo/services"
2022-10-05 05:42:38 +01:00
"github.com/ledgerwatch/erigon/turbo/shards"
2022-07-23 18:57:23 +02:00
"github.com/ledgerwatch/erigon/turbo/stages/headerdownload"
2020-08-12 16:47:59 +03:00
)
2021-05-22 17:00:13 +07:00
// EthBackendAPIVersion
// 2.0.0 - move all mining-related methods to 'txpool/mining' server
2021-06-17 23:55:20 +02:00
// 2.1.0 - add NetPeerCount function
2021-12-01 01:42:12 +03:00
// 2.2.0 - add NodesInfo function
2021-12-02 13:40:48 +00:00
// 3.0.0 - adding PoS interfaces
2022-03-17 10:40:18 +03:00
// 3.1.0 - add Subscribe to logs
2023-02-10 13:45:33 +07:00
// 3.2.0 - add EngineGetBlobsBundleV1
var EthBackendAPIVersion = & types2 . VersionReply { Major : 3 , Minor : 2 , Patch : 0 }
2021-05-22 17:00:13 +07:00
2022-06-13 15:43:09 +02:00
const MaxBuilders = 128
2022-02-09 08:33:22 +01:00
2022-05-31 15:06:00 +02:00
var UnknownPayloadErr = rpc . CustomError { Code : - 38001 , Message : "Unknown payload" }
var InvalidForkchoiceStateErr = rpc . CustomError { Code : - 38002 , Message : "Invalid forkchoice state" }
var InvalidPayloadAttributesErr = rpc . CustomError { Code : - 38003 , Message : "Invalid payload attributes" }
2023-02-06 18:38:45 +01:00
var TooLargeRequestErr = rpc . CustomError { Code : - 38004 , Message : "Too large request" }
2022-02-23 21:27:38 +01:00
2020-08-12 16:47:59 +03:00
type EthBackendServer struct {
2020-10-24 13:54:03 +07:00
remote . UnimplementedETHBACKENDServer // must be embedded to have forward compatible implementations.
2020-08-12 16:47:59 +03:00
2021-12-29 17:25:13 +01:00
ctx context . Context
eth EthBackend
2022-10-05 05:42:38 +01:00
events * shards . Events
2021-12-29 17:25:13 +01:00
db kv . RoDB
2022-05-26 10:31:06 +07:00
blockReader services . BlockAndTxnReader
2023-01-13 18:12:18 +00:00
config * chain . Config
2023-02-21 10:24:59 +01:00
2021-12-29 17:25:13 +01:00
// Block proposing for proof-of-stake
2023-02-21 10:24:59 +01:00
payloadId uint64
lastParameters * core . BlockBuilderParameters
builders map [ uint64 ] * builder . BlockBuilder
builderFunc builder . BlockBuilderFunc
proposing bool
lock sync . Mutex // Engine API is asynchronous, we want to avoid CL to call different APIs at the same time
logsFilter * LogsFilterAggregator
hd * headerdownload . HeaderDownload
2020-08-12 16:47:59 +03:00
}
2021-05-22 17:00:13 +07:00
type EthBackend interface {
2023-01-13 18:12:18 +00:00
Etherbase ( ) ( libcommon . Address , error )
2021-05-22 17:00:13 +07:00
NetVersion ( ) ( uint64 , error )
2021-06-17 23:55:20 +02:00
NetPeerCount ( ) ( uint64 , error )
2021-12-01 01:42:12 +03:00
NodesInfo ( limit int ) ( * remote . NodesInfoReply , error )
2022-04-25 15:40:04 +02:00
Peers ( ctx context . Context ) ( * remote . PeersReply , error )
2021-05-22 17:00:13 +07:00
}
2022-10-05 05:42:38 +01:00
func NewEthBackendServer ( ctx context . Context , eth EthBackend , db kv . RwDB , events * shards . Events , blockReader services . BlockAndTxnReader ,
2023-01-13 18:12:18 +00:00
config * chain . Config , builderFunc builder . BlockBuilderFunc , hd * headerdownload . HeaderDownload , proposing bool ,
2021-11-22 16:12:34 +01:00
) * EthBackendServer {
2022-03-17 10:40:18 +03:00
s := & EthBackendServer { ctx : ctx , eth : eth , events : events , db : db , blockReader : blockReader , config : config ,
2022-07-23 18:57:23 +02:00
builders : make ( map [ uint64 ] * builder . BlockBuilder ) ,
builderFunc : builderFunc , proposing : proposing , logsFilter : NewLogsFilterAggregator ( events ) , hd : hd ,
2021-11-29 15:47:08 +01:00
}
2022-03-26 18:21:31 +00:00
ch , clean := s . events . AddLogsSubscription ( )
go func ( ) {
var err error
defer clean ( )
log . Info ( "new subscription to logs established" )
defer func ( ) {
if err != nil {
if ! errors . Is ( err , context . Canceled ) {
log . Warn ( "subscription to logs closed" , "reason" , err )
}
} else {
log . Warn ( "subscription to logs closed" )
}
} ( )
for {
select {
case <- s . ctx . Done ( ) :
err = s . ctx . Err ( )
return
case logs := <- ch :
s . logsFilter . distributeLogs ( logs )
}
}
} ( )
2022-03-17 10:40:18 +03:00
return s
2021-05-17 19:15:19 +07:00
}
func ( s * EthBackendServer ) Version ( context . Context , * emptypb . Empty ) ( * types2 . VersionReply , error ) {
2021-05-22 17:00:13 +07:00
return EthBackendAPIVersion , nil
2020-08-12 16:47:59 +03:00
}
func ( s * EthBackendServer ) Etherbase ( _ context . Context , _ * remote . EtherbaseRequest ) ( * remote . EtherbaseReply , error ) {
2023-01-13 18:12:18 +00:00
out := & remote . EtherbaseReply { Address : gointerfaces . ConvertAddressToH160 ( libcommon . Address { } ) }
2020-08-12 16:47:59 +03:00
base , err := s . eth . Etherbase ( )
if err != nil {
return out , err
}
2021-03-19 21:24:49 +00:00
out . Address = gointerfaces . ConvertAddressToH160 ( base )
2020-08-12 16:47:59 +03:00
return out , nil
}
2020-08-18 20:22:49 +03:00
func ( s * EthBackendServer ) NetVersion ( _ context . Context , _ * remote . NetVersionRequest ) ( * remote . NetVersionReply , error ) {
id , err := s . eth . NetVersion ( )
if err != nil {
return & remote . NetVersionReply { } , err
}
return & remote . NetVersionReply { Id : id } , nil
}
2020-11-17 20:13:41 +01:00
2021-06-17 23:55:20 +02:00
func ( s * EthBackendServer ) NetPeerCount ( _ context . Context , _ * remote . NetPeerCountRequest ) ( * remote . NetPeerCountReply , error ) {
id , err := s . eth . NetPeerCount ( )
if err != nil {
return & remote . NetPeerCountReply { } , err
}
2021-06-18 15:34:15 +02:00
return & remote . NetPeerCountReply { Count : id } , nil
2021-06-17 23:55:20 +02:00
}
2022-02-10 08:25:58 +07:00
func ( s * EthBackendServer ) Subscribe ( r * remote . SubscribeRequest , subscribeServer remote . ETHBACKEND_SubscribeServer ) ( err error ) {
2022-06-17 20:24:36 +02:00
log . Debug ( "Establishing event subscription channel with the RPC daemon ..." )
2022-02-10 08:25:58 +07:00
ch , clean := s . events . AddHeaderSubscription ( )
defer clean ( )
2022-03-18 15:06:23 +07:00
newSnCh , newSnClean := s . events . AddNewSnapshotSubscription ( )
defer newSnClean ( )
2022-02-10 08:25:58 +07:00
log . Info ( "new subscription to newHeaders established" )
defer func ( ) {
if err != nil {
2022-02-18 09:45:20 +07:00
if ! errors . Is ( err , context . Canceled ) {
log . Warn ( "subscription to newHeaders closed" , "reason" , err )
}
2022-02-10 08:25:58 +07:00
} else {
log . Warn ( "subscription to newHeaders closed" )
}
} ( )
2022-07-28 16:57:38 +07:00
_ = subscribeServer . Send ( & remote . SubscribeReply { Type : remote . Event_NEW_SNAPSHOT } )
2022-02-10 08:25:58 +07:00
for {
2020-12-05 04:17:13 +07:00
select {
2021-08-17 15:52:55 +07:00
case <- s . ctx . Done ( ) :
2022-02-10 08:25:58 +07:00
return s . ctx . Err ( )
2020-12-05 04:17:13 +07:00
case <- subscribeServer . Context ( ) . Done ( ) :
2022-02-10 08:25:58 +07:00
return subscribeServer . Context ( ) . Err ( )
case headersRlp := <- ch :
for _ , headerRlp := range headersRlp {
if err = subscribeServer . Send ( & remote . SubscribeReply {
Type : remote . Event_HEADER ,
Data : headerRlp ,
} ) ; err != nil {
return err
}
}
2022-03-18 15:06:23 +07:00
case <- newSnCh :
if err = subscribeServer . Send ( & remote . SubscribeReply { Type : remote . Event_NEW_SNAPSHOT } ) ; err != nil {
return err
}
2020-11-17 20:13:41 +01:00
}
2021-08-17 15:52:55 +07:00
}
2020-11-17 20:13:41 +01:00
}
2021-03-23 16:00:07 +07:00
2021-04-24 16:46:29 +01:00
func ( s * EthBackendServer ) ProtocolVersion ( _ context . Context , _ * remote . ProtocolVersionRequest ) ( * remote . ProtocolVersionReply , error ) {
// Hardcoding to avoid import cycle
2022-06-28 13:42:35 +02:00
return & remote . ProtocolVersionReply { Id : 66 } , nil
2021-04-24 16:46:29 +01:00
}
func ( s * EthBackendServer ) ClientVersion ( _ context . Context , _ * remote . ClientVersionRequest ) ( * remote . ClientVersionReply , error ) {
2021-07-15 16:11:39 +07:00
return & remote . ClientVersionReply { NodeName : common . MakeName ( "erigon" , params . Version ) } , nil
2021-04-24 16:46:29 +01:00
}
2021-11-14 11:08:52 +07:00
2022-01-07 20:52:38 +07:00
func ( s * EthBackendServer ) TxnLookup ( ctx context . Context , req * remote . TxnLookupRequest ) ( * remote . TxnLookupReply , error ) {
tx , err := s . db . BeginRo ( ctx )
if err != nil {
return nil , err
}
defer tx . Rollback ( )
blockNum , ok , err := s . blockReader . TxnLookup ( ctx , tx , gointerfaces . ConvertH256ToHash ( req . TxnHash ) )
if err != nil {
return nil , err
}
if ! ok {
2022-05-22 20:35:02 +01:00
// Not a perfect solution, assumes there are no transactions in block 0
return & remote . TxnLookupReply { BlockNumber : 0 } , nil
2022-01-07 20:52:38 +07:00
}
return & remote . TxnLookupReply { BlockNumber : blockNum } , nil
}
2021-11-14 11:08:52 +07:00
func ( s * EthBackendServer ) Block ( ctx context . Context , req * remote . BlockRequest ) ( * remote . BlockReply , error ) {
tx , err := s . db . BeginRo ( ctx )
if err != nil {
return nil , err
}
defer tx . Rollback ( )
block , senders , err := s . blockReader . BlockWithSenders ( ctx , tx , gointerfaces . ConvertH256ToHash ( req . BlockHash ) , req . BlockHeight )
if err != nil {
return nil , err
}
blockRlp , err := rlp . EncodeToBytes ( block )
if err != nil {
return nil , err
}
sendersBytes := make ( [ ] byte , 20 * len ( senders ) )
2022-02-05 20:11:56 +01:00
for i , sender := range senders {
copy ( sendersBytes [ i * 20 : ] , sender [ : ] )
2021-11-14 11:08:52 +07:00
}
return & remote . BlockReply { BlockRlp : blockRlp , Senders : sendersBytes } , nil
}
2021-11-20 17:27:11 +01:00
2022-08-15 11:19:45 +03:00
func ( s * EthBackendServer ) PendingBlock ( _ context . Context , _ * emptypb . Empty ) ( * remote . PendingBlockReply , error ) {
2022-08-18 18:11:12 +02:00
s . lock . Lock ( )
defer s . lock . Unlock ( )
2022-08-15 11:19:45 +03:00
b := s . builders [ s . payloadId ]
if b == nil {
return nil , nil
}
pendingBlock := b . Block ( )
if pendingBlock == nil {
return nil , nil
}
blockRlp , err := rlp . EncodeToBytes ( pendingBlock )
if err != nil {
return nil , err
}
return & remote . PendingBlockReply { BlockRlp : blockRlp } , nil
}
2022-07-23 18:57:23 +02:00
func convertPayloadStatus ( payloadStatus * engineapi . PayloadStatus ) * remote . EnginePayloadStatus {
2022-02-09 08:33:22 +01:00
reply := remote . EnginePayloadStatus { Status : payloadStatus . Status }
2022-07-24 16:20:08 +02:00
if payloadStatus . Status != remote . EngineStatus_SYNCING {
2022-07-03 13:11:16 +02:00
reply . LatestValidHash = gointerfaces . ConvertHashToH256 ( payloadStatus . LatestValidHash )
}
2022-02-09 08:33:22 +01:00
if payloadStatus . ValidationError != nil {
reply . ValidationError = payloadStatus . ValidationError . Error ( )
}
return & reply
}
2022-02-21 18:05:39 +01:00
func ( s * EthBackendServer ) stageLoopIsBusy ( ) bool {
2023-02-06 11:17:32 +00:00
ctx , cancel := context . WithTimeout ( context . Background ( ) , 1 * time . Second )
defer cancel ( )
wait , ok := s . hd . BeaconRequestList . WaitForWaiting ( ctx )
if ! ok {
2023-01-30 08:52:29 +01:00
select {
2023-02-06 11:17:32 +00:00
case <- wait :
2023-02-14 11:18:38 +00:00
return false
2023-02-06 11:17:32 +00:00
case <- ctx . Done ( ) :
2023-02-14 11:18:38 +00:00
return true
2022-02-21 18:05:39 +01:00
}
2023-02-06 11:17:32 +00:00
}
2023-02-14 11:18:38 +00:00
return false
2022-02-21 18:05:39 +01:00
}
2023-02-06 18:38:45 +01:00
func ( s * EthBackendServer ) checkWithdrawalsPresence ( time uint64 , withdrawals [ ] * types . Withdrawal ) error {
if ! s . config . IsShanghai ( time ) && withdrawals != nil {
return & rpc . InvalidParamsError { Message : "withdrawals before shanghai" }
}
if s . config . IsShanghai ( time ) && withdrawals == nil {
return & rpc . InvalidParamsError { Message : "missing withdrawals list" }
}
return nil
}
2023-02-10 13:53:08 +07:00
func ( s * EthBackendServer ) EngineGetBlobsBundleV1 ( ctx context . Context , in * remote . EngineGetBlobsBundleRequest ) ( * types2 . BlobsBundleV1 , error ) {
2023-02-10 13:45:33 +07:00
return nil , fmt . Errorf ( "EngineGetBlobsBundleV1: not implemented yet" )
}
2023-01-20 18:43:08 +01:00
// EngineNewPayload validates and possibly executes payload
func ( s * EthBackendServer ) EngineNewPayload ( ctx context . Context , req * types2 . ExecutionPayload ) ( * remote . EnginePayloadStatus , error ) {
2021-11-20 17:27:11 +01:00
header := types . Header {
2021-11-20 22:53:03 +01:00
ParentHash : gointerfaces . ConvertH256ToHash ( req . ParentHash ) ,
Coinbase : gointerfaces . ConvertH160toAddress ( req . Coinbase ) ,
Root : gointerfaces . ConvertH256ToHash ( req . StateRoot ) ,
Bloom : gointerfaces . ConvertH2048ToBloom ( req . LogsBloom ) ,
2022-12-01 09:15:01 +01:00
BaseFee : gointerfaces . ConvertH256ToUint256Int ( req . BaseFeePerGas ) . ToBig ( ) ,
2021-12-29 17:25:13 +01:00
Extra : req . ExtraData ,
2021-11-20 22:53:03 +01:00
Number : big . NewInt ( int64 ( req . BlockNumber ) ) ,
GasUsed : req . GasUsed ,
GasLimit : req . GasLimit ,
Time : req . Timestamp ,
2022-02-24 18:16:05 +01:00
MixDigest : gointerfaces . ConvertH256ToHash ( req . PrevRandao ) ,
2021-11-21 22:47:33 +01:00
UncleHash : types . EmptyUncleHash ,
Difficulty : serenity . SerenityDifficulty ,
Nonce : serenity . SerenityNonce ,
2021-11-20 22:53:03 +01:00
ReceiptHash : gointerfaces . ConvertH256ToHash ( req . ReceiptRoot ) ,
2022-07-21 19:40:00 +02:00
TxHash : types . DeriveSha ( types . BinaryTransactions ( req . Transactions ) ) ,
2021-11-20 17:27:11 +01:00
}
2023-01-20 18:43:08 +01:00
var withdrawals [ ] * types . Withdrawal
if req . Version >= 2 {
withdrawals = ConvertWithdrawalsFromRpc ( req . Withdrawals )
}
2022-12-01 09:15:01 +01:00
if withdrawals != nil {
wh := types . DeriveSha ( types . Withdrawals ( withdrawals ) )
header . WithdrawalsHash = & wh
}
2022-02-09 08:33:22 +01:00
2023-02-06 18:38:45 +01:00
if err := s . checkWithdrawalsPresence ( header . Time , withdrawals ) ; err != nil {
return nil , err
2023-01-18 13:31:25 +00:00
}
2022-02-09 08:33:22 +01:00
blockHash := gointerfaces . ConvertH256ToHash ( req . BlockHash )
2022-07-21 19:40:00 +02:00
if header . Hash ( ) != blockHash {
2023-01-13 18:12:18 +00:00
log . Error ( "[NewPayload] invalid block hash" , "stated" , libcommon . Hash ( blockHash ) , "actual" , header . Hash ( ) )
2023-01-23 14:28:46 +01:00
return & remote . EnginePayloadStatus {
Status : remote . EngineStatus_INVALID ,
ValidationError : "invalid block hash" ,
} , nil
2022-07-21 19:40:00 +02:00
}
for _ , txn := range req . Transactions {
if types . TypedTransactionMarshalledAsRlpString ( txn ) {
log . Warn ( "[NewPayload] typed txn marshalled as RLP string" , "txn" , common . Bytes2Hex ( txn ) )
return & remote . EnginePayloadStatus {
Status : remote . EngineStatus_INVALID ,
ValidationError : "typed txn marshalled as RLP string" ,
} , nil
}
}
transactions , err := types . DecodeTransactions ( req . Transactions )
if err != nil {
log . Warn ( "[NewPayload] failed to decode transactions" , "err" , err )
return & remote . EnginePayloadStatus {
Status : remote . EngineStatus_INVALID ,
ValidationError : err . Error ( ) ,
} , nil
}
2022-12-01 09:15:01 +01:00
block := types . NewBlockFromStorage ( blockHash , & header , transactions , nil /* uncles */ , withdrawals )
2022-07-21 19:40:00 +02:00
2022-08-31 11:01:02 +02:00
possibleStatus , err := s . getQuickPayloadStatusIfPossible ( blockHash , req . BlockNumber , header . ParentHash , nil , true )
2022-07-23 18:57:23 +02:00
if err != nil {
return nil , err
}
if possibleStatus != nil {
return convertPayloadStatus ( possibleStatus ) , nil
}
2022-08-08 11:26:34 +02:00
2022-06-30 02:27:34 +02:00
s . lock . Lock ( )
defer s . lock . Unlock ( )
2022-04-05 17:23:17 +02:00
2023-01-13 18:12:18 +00:00
log . Debug ( "[NewPayload] sending block" , "height" , header . Number , "hash" , libcommon . Hash ( blockHash ) )
2022-07-23 18:57:23 +02:00
s . hd . BeaconRequestList . AddPayloadRequest ( block )
2021-11-29 15:47:08 +01:00
2022-07-23 18:57:23 +02:00
payloadStatus := <- s . hd . PayloadStatusCh
2022-06-17 20:24:36 +02:00
log . Debug ( "[NewPayload] got reply" , "payloadStatus" , payloadStatus )
2022-03-16 06:55:21 +01:00
2022-02-09 08:33:22 +01:00
if payloadStatus . CriticalError != nil {
return nil , payloadStatus . CriticalError
2021-12-23 15:06:10 +01:00
}
2022-02-09 08:33:22 +01:00
return convertPayloadStatus ( & payloadStatus ) , nil
2021-11-21 21:52:36 +01:00
}
2021-11-22 16:12:34 +01:00
2022-08-31 11:01:02 +02:00
// Check if we can quickly determine the status of a newPayload or forkchoiceUpdated.
2023-01-13 18:12:18 +00:00
func ( s * EthBackendServer ) getQuickPayloadStatusIfPossible ( blockHash libcommon . Hash , blockNumber uint64 , parentHash libcommon . Hash , forkchoiceMessage * engineapi . ForkChoiceMessage , newPayload bool ) ( * engineapi . PayloadStatus , error ) {
2022-07-24 16:20:08 +02:00
// Determine which prefix to use for logs
2022-07-23 18:57:23 +02:00
var prefix string
if newPayload {
prefix = "NewPayload"
} else {
prefix = "ForkChoiceUpdated"
}
2022-07-24 16:20:08 +02:00
if s . config . TerminalTotalDifficulty == nil {
log . Error ( fmt . Sprintf ( "[%s] not a proof-of-stake chain" , prefix ) )
return nil , fmt . Errorf ( "not a proof-of-stake chain" )
}
if s . hd == nil {
2022-08-08 11:26:34 +02:00
return nil , fmt . Errorf ( "headerdownload is nil" )
2022-07-24 16:20:08 +02:00
}
2022-07-23 18:57:23 +02:00
tx , err := s . db . BeginRo ( s . ctx )
if err != nil {
return nil , err
}
defer tx . Rollback ( )
2022-08-08 11:26:34 +02:00
// Some Consensus layer clients sometimes sends us repeated FCUs and make Erigon print a gazillion logs.
// E.G teku sometimes will end up spamming fcu on the terminal block if it has not synced to that point.
if forkchoiceMessage != nil &&
forkchoiceMessage . FinalizedBlockHash == rawdb . ReadForkchoiceFinalized ( tx ) &&
forkchoiceMessage . HeadBlockHash == rawdb . ReadForkchoiceHead ( tx ) &&
forkchoiceMessage . SafeBlockHash == rawdb . ReadForkchoiceSafe ( tx ) {
return & engineapi . PayloadStatus { Status : remote . EngineStatus_VALID , LatestValidHash : blockHash } , nil
}
2022-07-23 18:57:23 +02:00
header , err := rawdb . ReadHeaderByHash ( tx , blockHash )
if err != nil {
return nil , err
}
2022-07-24 16:20:08 +02:00
// Retrieve parent and total difficulty.
2022-07-23 18:57:23 +02:00
var parent * types . Header
2022-08-31 11:01:02 +02:00
var td * big . Int
2022-07-23 18:57:23 +02:00
if newPayload {
parent , err = rawdb . ReadHeaderByHash ( tx , parentHash )
2022-07-24 16:20:08 +02:00
if err != nil {
return nil , err
}
2022-08-31 11:01:02 +02:00
td , err = rawdb . ReadTdByHash ( tx , parentHash )
} else {
td , err = rawdb . ReadTdByHash ( tx , blockHash )
}
if err != nil {
return nil , err
}
if td != nil && td . Cmp ( s . config . TerminalTotalDifficulty ) < 0 {
log . Warn ( fmt . Sprintf ( "[%s] Beacon Chain request before TTD" , prefix ) , "hash" , blockHash )
2023-01-13 18:12:18 +00:00
return & engineapi . PayloadStatus { Status : remote . EngineStatus_INVALID , LatestValidHash : libcommon . Hash { } } , nil
2022-07-23 18:57:23 +02:00
}
2022-08-20 16:27:51 +02:00
if ! s . hd . POSSync ( ) {
2022-09-29 22:17:16 +01:00
log . Info ( fmt . Sprintf ( "[%s] Still in PoW sync" , prefix ) , "hash" , blockHash )
2022-09-16 10:59:46 +02:00
return & engineapi . PayloadStatus { Status : remote . EngineStatus_SYNCING } , nil
2022-07-24 16:20:08 +02:00
}
2022-07-23 18:57:23 +02:00
2023-01-13 18:12:18 +00:00
var canonicalHash libcommon . Hash
2022-07-23 18:57:23 +02:00
if header != nil {
canonicalHash , err = rawdb . ReadCanonicalHash ( tx , header . Number . Uint64 ( ) )
}
if err != nil {
return nil , err
}
if newPayload && parent != nil && blockNumber != parent . Number . Uint64 ( ) + 1 {
log . Warn ( fmt . Sprintf ( "[%s] Invalid block number" , prefix ) , "headerNumber" , blockNumber , "parentNumber" , parent . Number . Uint64 ( ) )
s . hd . ReportBadHeaderPoS ( blockHash , parent . Hash ( ) )
return & engineapi . PayloadStatus {
Status : remote . EngineStatus_INVALID ,
LatestValidHash : parent . Hash ( ) ,
ValidationError : errors . New ( "invalid block number" ) ,
} , nil
}
// Check if we already determined if the hash is attributed to a previously received invalid header.
bad , lastValidHash := s . hd . IsBadHeaderPoS ( blockHash )
if bad {
log . Warn ( fmt . Sprintf ( "[%s] Previously known bad block" , prefix ) , "hash" , blockHash )
} else if newPayload {
bad , lastValidHash = s . hd . IsBadHeaderPoS ( parentHash )
if bad {
log . Warn ( fmt . Sprintf ( "[%s] Previously known bad block" , prefix ) , "hash" , blockHash , "parentHash" , parentHash )
}
}
if bad {
s . hd . ReportBadHeaderPoS ( blockHash , lastValidHash )
return & engineapi . PayloadStatus { Status : remote . EngineStatus_INVALID , LatestValidHash : lastValidHash } , nil
}
// If header is already validated or has a missing parent, you can either return VALID or SYNCING.
if newPayload {
if header != nil && canonicalHash == blockHash {
return & engineapi . PayloadStatus { Status : remote . EngineStatus_VALID , LatestValidHash : blockHash } , nil
}
2022-09-16 10:59:46 +02:00
if parent == nil && s . hd . PosStatus ( ) != headerdownload . Idle {
log . Debug ( fmt . Sprintf ( "[%s] Downloading some other PoS blocks" , prefix ) , "hash" , blockHash )
2022-07-23 18:57:23 +02:00
return & engineapi . PayloadStatus { Status : remote . EngineStatus_SYNCING } , nil
}
2022-08-08 11:26:34 +02:00
} else {
2022-09-16 10:59:46 +02:00
if header == nil && s . hd . PosStatus ( ) != headerdownload . Idle {
log . Debug ( fmt . Sprintf ( "[%s] Downloading some other PoS stuff" , prefix ) , "hash" , blockHash )
return & engineapi . PayloadStatus { Status : remote . EngineStatus_SYNCING } , nil
2022-08-08 11:26:34 +02:00
}
2022-10-06 18:25:24 +01:00
// Following code ensures we skip the fork choice state update if if forkchoiceState.headBlockHash references an ancestor of the head of canonical chain
headHash := rawdb . ReadHeadBlockHash ( tx )
if err != nil {
return nil , err
}
// We add the extra restriction blockHash != headHash for the FCU case of canonicalHash == blockHash
// because otherwise (when FCU points to the head) we want go to stage headers
// so that it calls writeForkChoiceHashes.
if blockHash != headHash && canonicalHash == blockHash {
return & engineapi . PayloadStatus { Status : remote . EngineStatus_VALID , LatestValidHash : blockHash } , nil
}
2022-07-23 18:57:23 +02:00
}
2022-08-08 11:26:34 +02:00
// If another payload is already commissioned then we just reply with syncing
if s . stageLoopIsBusy ( ) {
2022-10-02 17:10:11 +02:00
log . Debug ( fmt . Sprintf ( "[%s] stage loop is busy" , prefix ) )
2022-08-08 11:26:34 +02:00
return & engineapi . PayloadStatus { Status : remote . EngineStatus_SYNCING } , nil
2022-07-23 18:57:23 +02:00
}
return nil , nil
}
2023-01-20 18:43:08 +01:00
// The expected value to be received by the feeRecipient in wei
2023-02-14 16:05:27 +01:00
func blockValue ( br * types . BlockWithReceipts , baseFee * uint256 . Int ) * uint256 . Int {
2023-01-20 18:43:08 +01:00
blockValue := uint256 . NewInt ( 0 )
2023-02-14 16:05:27 +01:00
txs := br . Block . Transactions ( )
for i := range txs {
gas := new ( uint256 . Int ) . SetUint64 ( br . Receipts [ i ] . GasUsed )
effectiveTip := txs [ i ] . GetEffectiveGasTip ( baseFee )
2023-01-20 18:43:08 +01:00
txValue := new ( uint256 . Int ) . Mul ( gas , effectiveTip )
blockValue . Add ( blockValue , txValue )
2022-12-01 09:15:01 +01:00
}
2023-01-20 18:43:08 +01:00
return blockValue
2022-12-01 09:15:01 +01:00
}
2023-01-20 18:43:08 +01:00
// EngineGetPayload retrieves previously assembled payload (Validators only)
func ( s * EthBackendServer ) EngineGetPayload ( ctx context . Context , req * remote . EngineGetPayloadRequest ) ( * remote . EngineGetPayloadResponse , error ) {
2022-01-04 18:37:36 +01:00
if ! s . proposing {
2023-01-20 18:43:08 +01:00
return nil , fmt . Errorf ( "execution layer not running as a proposer. enable proposer by taking out the --proposer.disable flag on startup" )
2022-01-04 18:37:36 +01:00
}
2021-11-29 15:58:46 +01:00
2021-11-24 23:19:40 +01:00
if s . config . TerminalTotalDifficulty == nil {
2023-01-20 18:43:08 +01:00
return nil , fmt . Errorf ( "not a proof-of-stake chain" )
2021-11-24 23:19:40 +01:00
}
2022-06-17 20:24:36 +02:00
log . Debug ( "[GetPayload] acquiring lock" )
2022-06-13 15:43:09 +02:00
s . lock . Lock ( )
defer s . lock . Unlock ( )
2022-06-17 20:24:36 +02:00
log . Debug ( "[GetPayload] lock acquired" )
2022-06-09 13:16:11 +02:00
2022-06-13 15:43:09 +02:00
builder , ok := s . builders [ req . PayloadId ]
2022-02-23 21:27:38 +01:00
if ! ok {
2022-06-09 13:16:11 +02:00
log . Warn ( "Payload not stored" , "payloadId" , req . PayloadId )
2023-01-20 18:43:08 +01:00
return nil , & UnknownPayloadErr
2022-02-23 21:27:38 +01:00
}
2022-01-04 18:37:36 +01:00
2023-02-14 16:05:27 +01:00
blockWithReceipts , err := builder . Stop ( )
2022-12-01 09:15:01 +01:00
if err != nil {
log . Error ( "Failed to build PoS block" , "err" , err )
2023-01-20 18:43:08 +01:00
return nil , err
2022-12-01 09:15:01 +01:00
}
2023-02-14 16:05:27 +01:00
block := blockWithReceipts . Block
2022-01-04 18:37:36 +01:00
2023-01-20 18:43:08 +01:00
baseFee := new ( uint256 . Int )
baseFee . SetFromBig ( block . Header ( ) . BaseFee )
2022-02-23 21:27:38 +01:00
encodedTransactions , err := types . MarshalTransactionsBinary ( block . Transactions ( ) )
if err != nil {
2023-01-20 18:43:08 +01:00
return nil , err
2022-02-23 21:27:38 +01:00
}
2022-08-18 18:11:37 +02:00
2023-01-20 18:43:08 +01:00
payload := & types2 . ExecutionPayload {
Version : 1 ,
2022-02-23 21:27:38 +01:00
ParentHash : gointerfaces . ConvertHashToH256 ( block . Header ( ) . ParentHash ) ,
Coinbase : gointerfaces . ConvertAddressToH160 ( block . Header ( ) . Coinbase ) ,
Timestamp : block . Header ( ) . Time ,
2022-02-24 18:16:05 +01:00
PrevRandao : gointerfaces . ConvertHashToH256 ( block . Header ( ) . MixDigest ) ,
2022-02-23 21:27:38 +01:00
StateRoot : gointerfaces . ConvertHashToH256 ( block . Root ( ) ) ,
ReceiptRoot : gointerfaces . ConvertHashToH256 ( block . ReceiptHash ( ) ) ,
LogsBloom : gointerfaces . ConvertBytesToH2048 ( block . Bloom ( ) . Bytes ( ) ) ,
GasLimit : block . GasLimit ( ) ,
GasUsed : block . GasUsed ( ) ,
BlockNumber : block . NumberU64 ( ) ,
ExtraData : block . Extra ( ) ,
2023-01-20 18:43:08 +01:00
BaseFeePerGas : gointerfaces . ConvertUint256IntToH256 ( baseFee ) ,
2022-02-23 21:27:38 +01:00
BlockHash : gointerfaces . ConvertHashToH256 ( block . Header ( ) . Hash ( ) ) ,
Transactions : encodedTransactions ,
2023-01-20 18:43:08 +01:00
}
if block . Withdrawals ( ) != nil {
payload . Version = 2
payload . Withdrawals = ConvertWithdrawalsToRpc ( block . Withdrawals ( ) )
}
2022-12-01 09:15:01 +01:00
2023-02-14 16:05:27 +01:00
blockValue := blockValue ( blockWithReceipts , baseFee )
2023-01-20 18:43:08 +01:00
return & remote . EngineGetPayloadResponse {
ExecutionPayload : payload ,
BlockValue : gointerfaces . ConvertUint256IntToH256 ( blockValue ) ,
} , nil
2022-12-01 09:15:01 +01:00
}
// engineForkChoiceUpdated either states new block head or request the assembling of a new block
2023-01-20 18:43:08 +01:00
func ( s * EthBackendServer ) EngineForkChoiceUpdated ( ctx context . Context , req * remote . EngineForkChoiceUpdatedRequest ,
) ( * remote . EngineForkChoiceUpdatedResponse , error ) {
2022-04-05 17:23:17 +02:00
forkChoice := engineapi . ForkChoiceMessage {
2023-01-20 18:43:08 +01:00
HeadBlockHash : gointerfaces . ConvertH256ToHash ( req . ForkchoiceState . HeadBlockHash ) ,
SafeBlockHash : gointerfaces . ConvertH256ToHash ( req . ForkchoiceState . SafeBlockHash ) ,
FinalizedBlockHash : gointerfaces . ConvertH256ToHash ( req . ForkchoiceState . FinalizedBlockHash ) ,
2022-04-05 17:23:17 +02:00
}
2023-01-13 18:12:18 +00:00
status , err := s . getQuickPayloadStatusIfPossible ( forkChoice . HeadBlockHash , 0 , libcommon . Hash { } , & forkChoice , false )
2022-07-23 18:57:23 +02:00
if err != nil {
return nil , err
2022-06-30 02:27:34 +02:00
}
2022-07-27 12:14:35 +02:00
s . lock . Lock ( )
defer s . lock . Unlock ( )
if status == nil {
2022-07-23 18:57:23 +02:00
log . Debug ( "[ForkChoiceUpdated] sending forkChoiceMessage" , "head" , forkChoice . HeadBlockHash )
s . hd . BeaconRequestList . AddForkChoiceRequest ( & forkChoice )
2022-03-16 06:55:21 +01:00
2022-07-27 12:14:35 +02:00
statusDeref := <- s . hd . PayloadStatusCh
status = & statusDeref
2022-07-23 18:57:23 +02:00
log . Debug ( "[ForkChoiceUpdated] got reply" , "payloadStatus" , status )
2022-05-17 15:26:47 +02:00
2022-07-23 18:57:23 +02:00
if status . CriticalError != nil {
return nil , status . CriticalError
}
2022-02-09 08:33:22 +01:00
}
// No need for payload building
2023-01-20 18:43:08 +01:00
payloadAttributes := req . PayloadAttributes
2022-12-01 09:15:01 +01:00
if payloadAttributes == nil || status . Status != remote . EngineStatus_VALID {
2023-01-20 18:43:08 +01:00
return & remote . EngineForkChoiceUpdatedResponse { PayloadStatus : convertPayloadStatus ( status ) } , nil
2021-12-29 17:25:13 +01:00
}
2022-01-04 18:37:36 +01:00
if ! s . proposing {
2022-01-21 22:50:34 +03:00
return nil , fmt . Errorf ( "execution layer not running as a proposer. enable proposer by taking out the --proposer.disable flag on startup" )
2022-01-04 18:37:36 +01:00
}
2022-04-05 17:23:17 +02:00
tx2 , err := s . db . BeginRo ( ctx )
2022-02-23 21:27:38 +01:00
if err != nil {
return nil , err
}
2022-06-19 13:40:28 +01:00
defer tx2 . Rollback ( )
2022-05-17 22:20:57 +02:00
headHash := rawdb . ReadHeadBlockHash ( tx2 )
2022-04-05 17:23:17 +02:00
headNumber := rawdb . ReadHeaderNumber ( tx2 , headHash )
headHeader := rawdb . ReadHeader ( tx2 , headHash , * headNumber )
tx2 . Rollback ( )
2022-02-23 21:27:38 +01:00
2022-04-05 17:23:17 +02:00
if headHeader . Hash ( ) != forkChoice . HeadBlockHash {
2022-12-01 09:15:01 +01:00
// Per Item 2 of https://github.com/ethereum/execution-apis/blob/v1.0.0-alpha.9/src/engine/specification.md#specification-1:
2022-06-17 20:24:36 +02:00
// Client software MAY skip an update of the forkchoice state and
// MUST NOT begin a payload build process if forkchoiceState.headBlockHash doesn't reference a leaf of the block tree.
// That is, the block referenced by forkchoiceState.headBlockHash is neither the head of the canonical chain nor a block at the tip of any other chain.
// In the case of such an event, client software MUST return
// {payloadStatus: {status: VALID, latestValidHash: forkchoiceState.headBlockHash, validationError: null}, payloadId: null}.
log . Warn ( "Skipping payload building because forkchoiceState.headBlockHash is not the head of the canonical chain" ,
"forkChoice.HeadBlockHash" , forkChoice . HeadBlockHash , "headHeader.Hash" , headHeader . Hash ( ) )
2023-01-20 18:43:08 +01:00
return & remote . EngineForkChoiceUpdatedResponse { PayloadStatus : convertPayloadStatus ( status ) } , nil
2021-12-29 17:25:13 +01:00
}
2022-02-23 21:27:38 +01:00
2022-12-01 09:15:01 +01:00
if headHeader . Time >= payloadAttributes . Timestamp {
2022-05-31 15:06:00 +02:00
return nil , & InvalidPayloadAttributesErr
2022-05-18 21:33:35 +02:00
}
2022-05-31 15:06:00 +02:00
2023-01-20 18:43:08 +01:00
param := core . BlockBuilderParameters {
ParentHash : forkChoice . HeadBlockHash ,
Timestamp : payloadAttributes . Timestamp ,
PrevRandao : gointerfaces . ConvertH256ToHash ( payloadAttributes . PrevRandao ) ,
SuggestedFeeRecipient : gointerfaces . ConvertH160toAddress ( payloadAttributes . SuggestedFeeRecipient ) ,
PayloadId : s . payloadId ,
}
if payloadAttributes . Version >= 2 {
param . Withdrawals = ConvertWithdrawalsFromRpc ( payloadAttributes . Withdrawals )
}
2023-02-06 18:38:45 +01:00
if err := s . checkWithdrawalsPresence ( payloadAttributes . Timestamp , param . Withdrawals ) ; err != nil {
return nil , err
2022-12-20 09:09:04 +00:00
}
2023-02-21 10:24:59 +01:00
// First check if we're already building a block with the requested parameters
if reflect . DeepEqual ( s . lastParameters , & param ) {
return & remote . EngineForkChoiceUpdatedResponse {
PayloadStatus : & remote . EnginePayloadStatus {
Status : remote . EngineStatus_VALID ,
LatestValidHash : gointerfaces . ConvertHashToH256 ( headHash ) ,
} ,
PayloadId : s . payloadId ,
} , nil
}
2022-06-17 20:24:36 +02:00
2023-02-21 10:24:59 +01:00
// Initiate payload building
2022-06-17 20:24:36 +02:00
s . evictOldBuilders ( )
s . payloadId ++
2023-02-21 10:24:59 +01:00
param . PayloadId = s . payloadId
s . lastParameters = & param
2022-06-17 20:24:36 +02:00
2022-12-01 09:15:01 +01:00
s . builders [ s . payloadId ] = builder . NewBlockBuilder ( s . builderFunc , & param )
2022-09-20 13:15:56 +01:00
log . Debug ( "BlockBuilder added" , "payload" , s . payloadId )
2022-02-23 21:27:38 +01:00
2023-01-20 18:43:08 +01:00
return & remote . EngineForkChoiceUpdatedResponse {
2022-05-18 09:40:41 +02:00
PayloadStatus : & remote . EnginePayloadStatus {
Status : remote . EngineStatus_VALID ,
LatestValidHash : gointerfaces . ConvertHashToH256 ( headHash ) ,
} ,
PayloadId : s . payloadId ,
2021-12-29 17:25:13 +01:00
} , nil
}
2023-01-24 13:37:03 +01:00
func ( s * EthBackendServer ) EngineGetPayloadBodiesByHashV1 ( ctx context . Context , request * remote . EngineGetPayloadBodiesByHashV1Request ) ( * remote . EngineGetPayloadBodiesV1Response , error ) {
tx , err := s . db . BeginRo ( ctx )
if err != nil {
return nil , err
}
bodies := make ( [ ] * types2 . ExecutionPayloadBodyV1 , len ( request . Hashes ) )
for hashIdx , hash := range request . Hashes {
h := gointerfaces . ConvertH256ToHash ( hash )
block , err := rawdb . ReadBlockByHash ( tx , h )
if err != nil {
return nil , err
}
body , err := extractPayloadBodyFromBlock ( block )
if err != nil {
return nil , err
}
bodies [ hashIdx ] = body
}
return & remote . EngineGetPayloadBodiesV1Response { Bodies : bodies } , nil
}
func ( s * EthBackendServer ) EngineGetPayloadBodiesByRangeV1 ( ctx context . Context , request * remote . EngineGetPayloadBodiesByRangeV1Request ) ( * remote . EngineGetPayloadBodiesV1Response , error ) {
tx , err := s . db . BeginRo ( ctx )
if err != nil {
return nil , err
}
2023-02-06 18:38:45 +01:00
bodies := make ( [ ] * types2 . ExecutionPayloadBodyV1 , 0 , request . Count )
2023-01-24 13:37:03 +01:00
2023-02-06 18:38:45 +01:00
for i := uint64 ( 0 ) ; i < request . Count ; i ++ {
hash , err := rawdb . ReadCanonicalHash ( tx , request . Start + i )
2023-01-24 13:37:03 +01:00
if err != nil {
return nil , err
}
2023-02-06 18:38:45 +01:00
if hash == ( libcommon . Hash { } ) {
// break early if beyond the last known canonical header
break
}
block := rawdb . ReadBlock ( tx , hash , request . Start + i )
2023-01-24 13:37:03 +01:00
body , err := extractPayloadBodyFromBlock ( block )
if err != nil {
return nil , err
}
2023-02-06 18:38:45 +01:00
bodies = append ( bodies , body )
2023-01-24 13:37:03 +01:00
}
return & remote . EngineGetPayloadBodiesV1Response { Bodies : bodies } , nil
}
func extractPayloadBodyFromBlock ( block * types . Block ) ( * types2 . ExecutionPayloadBodyV1 , error ) {
if block == nil {
return nil , nil
}
txs := block . Transactions ( )
bdTxs := make ( [ ] [ ] byte , len ( txs ) )
for idx , tx := range txs {
var buf bytes . Buffer
if err := tx . MarshalBinary ( & buf ) ; err != nil {
return nil , err
} else {
bdTxs [ idx ] = buf . Bytes ( )
}
}
wds := block . Withdrawals ( )
bdWds := make ( [ ] * types2 . Withdrawal , len ( wds ) )
if wds == nil {
// pre shanghai blocks could have nil withdrawals so nil the slice as per spec
bdWds = nil
} else {
for idx , wd := range wds {
bdWds [ idx ] = & types2 . Withdrawal {
Index : wd . Index ,
ValidatorIndex : wd . Validator ,
Address : gointerfaces . ConvertAddressToH160 ( wd . Address ) ,
Amount : wd . Amount ,
}
}
}
return & types2 . ExecutionPayloadBodyV1 { Transactions : bdTxs , Withdrawals : bdWds } , nil
}
2022-06-13 15:43:09 +02:00
func ( s * EthBackendServer ) evictOldBuilders ( ) {
2022-11-10 13:47:57 +01:00
ids := common . SortedKeys ( s . builders )
2022-02-09 08:33:22 +01:00
2022-06-13 15:43:09 +02:00
// remove old builders so that at most MaxBuilders - 1 remain
for i := 0 ; i <= len ( s . builders ) - MaxBuilders ; i ++ {
delete ( s . builders , ids [ i ] )
2022-02-09 08:33:22 +01:00
}
}
2021-12-01 01:42:12 +03:00
func ( s * EthBackendServer ) NodeInfo ( _ context . Context , r * remote . NodesInfoRequest ) ( * remote . NodesInfoReply , error ) {
nodesInfo , err := s . eth . NodesInfo ( int ( r . Limit ) )
if err != nil {
return nil , err
}
return nodesInfo , nil
}
2022-03-17 10:40:18 +03:00
2022-04-25 15:40:04 +02:00
func ( s * EthBackendServer ) Peers ( ctx context . Context , _ * emptypb . Empty ) ( * remote . PeersReply , error ) {
return s . eth . Peers ( ctx )
}
2022-03-26 18:21:31 +00:00
func ( s * EthBackendServer ) SubscribeLogs ( server remote . ETHBACKEND_SubscribeLogsServer ) ( err error ) {
2022-03-17 10:40:18 +03:00
if s . logsFilter != nil {
return s . logsFilter . subscribeLogs ( server )
}
return fmt . Errorf ( "no logs filter available" )
}
2022-12-01 09:15:01 +01:00
func ConvertWithdrawalsFromRpc ( in [ ] * types2 . Withdrawal ) [ ] * types . Withdrawal {
2023-01-17 17:26:44 +01:00
if in == nil {
return nil
}
2022-12-01 09:15:01 +01:00
out := make ( [ ] * types . Withdrawal , 0 , len ( in ) )
for _ , w := range in {
out = append ( out , & types . Withdrawal {
Index : w . Index ,
Validator : w . ValidatorIndex ,
Address : gointerfaces . ConvertH160toAddress ( w . Address ) ,
2023-01-17 11:22:08 +01:00
Amount : w . Amount ,
2022-12-01 09:15:01 +01:00
} )
}
return out
}
func ConvertWithdrawalsToRpc ( in [ ] * types . Withdrawal ) [ ] * types2 . Withdrawal {
2023-01-17 17:26:44 +01:00
if in == nil {
return nil
}
2022-12-01 09:15:01 +01:00
out := make ( [ ] * types2 . Withdrawal , 0 , len ( in ) )
for _ , w := range in {
out = append ( out , & types2 . Withdrawal {
Index : w . Index ,
ValidatorIndex : w . Validator ,
Address : gointerfaces . ConvertAddressToH160 ( w . Address ) ,
2023-01-17 11:22:08 +01:00
Amount : w . Amount ,
2022-12-01 09:15:01 +01:00
} )
}
return out
}