2021-11-07 01:38:36 +00:00
package commands
import (
"context"
2021-12-29 16:25:13 +00:00
"encoding/binary"
2021-11-30 13:14:04 +00:00
"fmt"
2021-11-22 10:36:52 +00:00
"math/big"
2021-11-07 01:38:36 +00:00
2021-11-22 10:36:52 +00:00
"github.com/holiman/uint256"
"github.com/ledgerwatch/erigon-lib/gointerfaces"
2021-12-29 16:25:13 +00:00
"github.com/ledgerwatch/erigon-lib/gointerfaces/remote"
2021-11-22 15:12:34 +00:00
types2 "github.com/ledgerwatch/erigon-lib/gointerfaces/types"
2021-11-07 01:38:36 +00:00
"github.com/ledgerwatch/erigon-lib/kv"
2021-11-22 10:36:52 +00:00
"github.com/ledgerwatch/erigon/cmd/rpcdaemon/services"
2021-11-07 01:38:36 +00:00
"github.com/ledgerwatch/erigon/common"
"github.com/ledgerwatch/erigon/common/hexutil"
2021-11-22 15:12:34 +00:00
"github.com/ledgerwatch/erigon/core/types"
2021-11-22 10:36:52 +00:00
"github.com/ledgerwatch/log/v3"
2021-11-07 01:38:36 +00:00
)
// ExecutionPayload represents an execution payload (aka slot/block)
type ExecutionPayload struct {
2021-11-29 17:30:33 +00:00
ParentHash common . Hash ` json:"parentHash" gencodec:"required" `
2021-12-01 18:15:57 +00:00
FeeRecipient common . Address ` json:"feeRecipient" gencodec:"required" `
2021-11-29 17:30:33 +00:00
StateRoot common . Hash ` json:"stateRoot" gencodec:"required" `
2021-12-01 18:15:57 +00:00
ReceiptsRoot common . Hash ` json:"receiptsRoot" gencodec:"required" `
2021-11-29 17:30:33 +00:00
LogsBloom hexutil . Bytes ` json:"logsBloom" gencodec:"required" `
Random common . Hash ` json:"random" gencodec:"required" `
BlockNumber hexutil . Uint64 ` json:"blockNumber" gencodec:"required" `
GasLimit hexutil . Uint64 ` json:"gasLimit" gencodec:"required" `
GasUsed hexutil . Uint64 ` json:"gasUsed" gencodec:"required" `
2021-12-01 18:15:57 +00:00
Timestamp hexutil . Uint64 ` json:"timestamp" gencodec:"required" `
ExtraData hexutil . Bytes ` json:"extraData" gencodec:"required" `
2021-11-29 17:30:33 +00:00
BaseFeePerGas * hexutil . Big ` json:"baseFeePerGas" gencodec:"required" `
BlockHash common . Hash ` json:"blockHash" gencodec:"required" `
Transactions [ ] hexutil . Bytes ` json:"transactions" gencodec:"required" `
2021-11-07 01:38:36 +00:00
}
2021-12-29 16:25:13 +00:00
// PayloadAttributes represent the attributes required to start assembling a payload
type ForkChoiceState struct {
HeadHash common . Hash ` json:"headBlockHash" gencodec:"required" `
SafeBlockHash common . Hash ` json:"safeBlockHash" gencodec:"required" `
FinalizedBlockHash common . Hash ` json:"finalizedBlockHash" gencodec:"required" `
}
2021-12-01 18:15:57 +00:00
// PayloadAttributes represent the attributes required to start assembling a payload
type PayloadAttributes struct {
Timestamp hexutil . Uint64 ` json:"timestamp" gencodec:"required" `
Random common . Hash ` json:"random" gencodec:"required" `
SuggestedFeeRecipient common . Address ` json:"suggestedFeeRecipient" gencodec:"required" `
2021-11-07 01:38:36 +00:00
}
2022-02-15 09:30:54 +00:00
// TransitionConfiguration represents the correct configurations of the CL and the EL
type TransitionConfiguration struct {
TerminalTotalDifficulty * hexutil . Big ` json:"terminalTotalDifficulty" gencodec:"required" `
2022-02-21 17:05:39 +00:00
TerminalBlockHash common . Hash ` json:"terminalBlockHash" gencodec:"required" `
TerminalBlockNumber hexutil . Uint64 ` json:"terminalBlockNumber" gencodec:"required" `
2022-02-15 09:30:54 +00:00
}
2021-11-07 01:38:36 +00:00
// EngineAPI Beacon chain communication endpoint
type EngineAPI interface {
2021-12-29 16:25:13 +00:00
ForkchoiceUpdatedV1 ( ctx context . Context , forkChoiceState * ForkChoiceState , payloadAttributes * PayloadAttributes ) ( map [ string ] interface { } , error )
2022-02-09 07:33:22 +00:00
NewPayloadV1 ( context . Context , * ExecutionPayload ) ( map [ string ] interface { } , error )
2021-12-29 16:25:13 +00:00
GetPayloadV1 ( ctx context . Context , payloadID hexutil . Bytes ) ( * ExecutionPayload , error )
2022-02-15 09:30:54 +00:00
ExchangeTransitionConfigurationV1 ( ctx context . Context , transitionConfiguration TransitionConfiguration ) ( TransitionConfiguration , error )
2021-11-07 01:38:36 +00:00
}
// EngineImpl is implementation of the EngineAPI interface
type EngineImpl struct {
* BaseAPI
2021-11-22 10:36:52 +00:00
db kv . RoDB
api services . ApiBackend
2021-11-07 01:38:36 +00:00
}
2022-02-09 07:33:22 +00:00
func convertPayloadStatus ( x * remote . EnginePayloadStatus ) map [ string ] interface { } {
json := map [ string ] interface { } {
"status" : x . Status . String ( ) ,
}
if x . LatestValidHash != nil {
2022-02-17 17:30:57 +00:00
json [ "latestValidHash" ] = common . Hash ( gointerfaces . ConvertH256ToHash ( x . LatestValidHash ) )
2022-02-09 07:33:22 +00:00
}
if x . ValidationError != "" {
json [ "validationError" ] = x . ValidationError
}
return json
}
2021-12-29 16:25:13 +00:00
func ( e * EngineImpl ) ForkchoiceUpdatedV1 ( ctx context . Context , forkChoiceState * ForkChoiceState , payloadAttributes * PayloadAttributes ) ( map [ string ] interface { } , error ) {
2022-02-17 17:30:57 +00:00
log . Info ( "Received ForkchoiceUpdated" , "head" , forkChoiceState . HeadHash , "safe" , forkChoiceState . HeadHash , "finalized" , forkChoiceState . FinalizedBlockHash ,
"build" , payloadAttributes != nil )
2022-02-09 07:33:22 +00:00
var prepareParameters * remote . EnginePayloadAttributes
if payloadAttributes != nil {
prepareParameters = & remote . EnginePayloadAttributes {
Timestamp : uint64 ( payloadAttributes . Timestamp ) ,
Random : gointerfaces . ConvertHashToH256 ( payloadAttributes . Random ) ,
SuggestedFeeRecipient : gointerfaces . ConvertAddressToH160 ( payloadAttributes . SuggestedFeeRecipient ) ,
}
2021-11-07 01:38:36 +00:00
}
2022-02-09 07:33:22 +00:00
reply , err := e . api . EngineForkchoiceUpdatedV1 ( ctx , & remote . EngineForkChoiceUpdatedRequest {
ForkchoiceState : & remote . EngineForkChoiceState {
2021-12-29 16:25:13 +00:00
HeadBlockHash : gointerfaces . ConvertHashToH256 ( forkChoiceState . HeadHash ) ,
SafeBlockHash : gointerfaces . ConvertHashToH256 ( forkChoiceState . SafeBlockHash ) ,
2022-02-17 17:30:57 +00:00
FinalizedBlockHash : gointerfaces . ConvertHashToH256 ( forkChoiceState . FinalizedBlockHash ) ,
2021-12-29 16:25:13 +00:00
} ,
2022-02-09 07:33:22 +00:00
PayloadAttributes : prepareParameters ,
2021-12-29 16:25:13 +00:00
} )
if err != nil {
return nil , err
}
2022-02-09 07:33:22 +00:00
json := map [ string ] interface { } {
"payloadStatus" : convertPayloadStatus ( reply . PayloadStatus ) ,
}
if reply . PayloadId != 0 {
encodedPayloadId := make ( [ ] byte , 8 )
binary . BigEndian . PutUint64 ( encodedPayloadId , reply . PayloadId )
json [ "payloadId" ] = hexutil . Bytes ( encodedPayloadId )
2021-12-29 16:25:13 +00:00
}
2022-02-09 07:33:22 +00:00
return json , nil
2021-11-07 01:38:36 +00:00
}
2022-02-09 07:33:22 +00:00
// NewPayloadV1 processes new payloads (blocks) from the beacon chain.
// See https://github.com/ethereum/execution-apis/blob/main/src/engine/specification.md#engine_newpayloadv1
func ( e * EngineImpl ) NewPayloadV1 ( ctx context . Context , payload * ExecutionPayload ) ( map [ string ] interface { } , error ) {
2022-02-17 17:30:57 +00:00
log . Info ( "Received NewPayload" , "height" , payload . BlockNumber , "hash" , payload . BlockHash )
2021-11-22 10:36:52 +00:00
var baseFee * uint256 . Int
if payload . BaseFeePerGas != nil {
2021-11-30 13:14:04 +00:00
var overflow bool
baseFee , overflow = uint256 . FromBig ( ( * big . Int ) ( payload . BaseFeePerGas ) )
if overflow {
return nil , fmt . Errorf ( "invalid request" )
}
2021-11-22 10:36:52 +00:00
}
2021-11-22 15:12:34 +00:00
// Convert slice of hexutil.Bytes to a slice of slice of bytes
transactions := make ( [ ] [ ] byte , len ( payload . Transactions ) )
for i , transaction := range payload . Transactions {
transactions [ i ] = ( [ ] byte ) ( transaction )
}
2022-02-09 07:33:22 +00:00
res , err := e . api . EngineNewPayloadV1 ( ctx , & types2 . ExecutionPayload {
2021-11-25 18:44:02 +00:00
ParentHash : gointerfaces . ConvertHashToH256 ( payload . ParentHash ) ,
2021-12-01 18:15:57 +00:00
Coinbase : gointerfaces . ConvertAddressToH160 ( payload . FeeRecipient ) ,
2021-11-25 18:44:02 +00:00
StateRoot : gointerfaces . ConvertHashToH256 ( payload . StateRoot ) ,
2021-12-01 18:15:57 +00:00
ReceiptRoot : gointerfaces . ConvertHashToH256 ( payload . ReceiptsRoot ) ,
2021-11-25 18:44:02 +00:00
LogsBloom : gointerfaces . ConvertBytesToH2048 ( ( [ ] byte ) ( payload . LogsBloom ) ) ,
2021-11-22 10:36:52 +00:00
Random : gointerfaces . ConvertHashToH256 ( payload . Random ) ,
2022-01-20 07:00:31 +00:00
BlockNumber : uint64 ( payload . BlockNumber ) ,
GasLimit : uint64 ( payload . GasLimit ) ,
GasUsed : uint64 ( payload . GasUsed ) ,
Timestamp : uint64 ( payload . Timestamp ) ,
2021-12-29 16:25:13 +00:00
ExtraData : payload . ExtraData ,
2021-11-22 10:36:52 +00:00
BaseFeePerGas : gointerfaces . ConvertUint256IntToH256 ( baseFee ) ,
2021-12-01 18:15:57 +00:00
BlockHash : gointerfaces . ConvertHashToH256 ( payload . BlockHash ) ,
2021-11-22 15:12:34 +00:00
Transactions : transactions ,
2021-11-22 10:36:52 +00:00
} )
2021-11-21 22:11:50 +00:00
if err != nil {
return nil , err
}
2021-11-22 15:12:34 +00:00
2022-02-09 07:33:22 +00:00
return convertPayloadStatus ( res ) , nil
2021-11-07 01:38:36 +00:00
}
2021-12-29 16:25:13 +00:00
func ( e * EngineImpl ) GetPayloadV1 ( ctx context . Context , payloadID hexutil . Bytes ) ( * ExecutionPayload , error ) {
decodedPayloadId := binary . BigEndian . Uint64 ( payloadID )
2022-02-17 17:30:57 +00:00
log . Info ( "Received GetPayload" , "payloadId" , decodedPayloadId )
2021-12-29 16:25:13 +00:00
payload , err := e . api . EngineGetPayloadV1 ( ctx , decodedPayloadId )
2021-11-22 15:12:34 +00:00
if err != nil {
return nil , err
}
var bloom types . Bloom = gointerfaces . ConvertH2048ToBloom ( payload . LogsBloom )
var baseFee * big . Int
if payload . BaseFeePerGas != nil {
baseFee = gointerfaces . ConvertH256ToUint256Int ( payload . BaseFeePerGas ) . ToBig ( )
}
// Convert slice of hexutil.Bytes to a slice of slice of bytes
transactions := make ( [ ] hexutil . Bytes , len ( payload . Transactions ) )
for i , transaction := range payload . Transactions {
transactions [ i ] = transaction
}
return & ExecutionPayload {
ParentHash : gointerfaces . ConvertH256ToHash ( payload . ParentHash ) ,
2021-12-01 18:15:57 +00:00
FeeRecipient : gointerfaces . ConvertH160toAddress ( payload . Coinbase ) ,
2021-11-22 15:12:34 +00:00
StateRoot : gointerfaces . ConvertH256ToHash ( payload . StateRoot ) ,
2021-12-01 18:15:57 +00:00
ReceiptsRoot : gointerfaces . ConvertH256ToHash ( payload . ReceiptRoot ) ,
2021-11-22 15:12:34 +00:00
LogsBloom : bloom [ : ] ,
2021-12-01 18:15:57 +00:00
Random : gointerfaces . ConvertH256ToHash ( payload . Random ) ,
2021-11-22 15:12:34 +00:00
BlockNumber : hexutil . Uint64 ( payload . BlockNumber ) ,
GasLimit : hexutil . Uint64 ( payload . GasLimit ) ,
GasUsed : hexutil . Uint64 ( payload . GasUsed ) ,
Timestamp : hexutil . Uint64 ( payload . Timestamp ) ,
2021-12-29 16:25:13 +00:00
ExtraData : payload . ExtraData ,
2021-12-01 18:15:57 +00:00
BaseFeePerGas : ( * hexutil . Big ) ( baseFee ) ,
BlockHash : gointerfaces . ConvertH256ToHash ( payload . BlockHash ) ,
2021-11-22 15:12:34 +00:00
Transactions : transactions ,
} , nil
2021-11-07 01:38:36 +00:00
}
2022-02-23 20:27:38 +00:00
// Receives consensus layer's transition configuration and checks if the execution layer has the correct configuration.
// Can also be used to ping the execution layer (heartbeats).
// See https://github.com/ethereum/execution-apis/blob/v1.0.0-alpha.7/src/engine/specification.md#engine_exchangetransitionconfigurationv1
2022-02-21 17:05:39 +00:00
func ( e * EngineImpl ) ExchangeTransitionConfigurationV1 ( ctx context . Context , beaconConfig TransitionConfiguration ) ( TransitionConfiguration , error ) {
2022-02-15 09:30:54 +00:00
tx , err := e . db . BeginRo ( ctx )
if err != nil {
return TransitionConfiguration { } , err
}
defer tx . Rollback ( )
// terminal block number must always be zero
2022-02-21 17:05:39 +00:00
if beaconConfig . TerminalBlockNumber != 0 {
return TransitionConfiguration { } , fmt . Errorf ( "received the wrong terminal block number. expected zero, but instead got: %d" , beaconConfig . TerminalBlockNumber )
2022-02-15 09:30:54 +00:00
}
chainConfig , err := e . BaseAPI . chainConfig ( tx )
if err != nil {
return TransitionConfiguration { } , err
}
terminalTotalDifficulty := chainConfig . TerminalTotalDifficulty
if terminalTotalDifficulty == nil {
2022-02-21 17:05:39 +00:00
return TransitionConfiguration { } , fmt . Errorf ( "the execution layer doesn't have the terminal total difficulty. expected: %v" , beaconConfig . TerminalTotalDifficulty )
2022-02-15 09:30:54 +00:00
}
2022-02-21 17:05:39 +00:00
if terminalTotalDifficulty . Cmp ( ( * big . Int ) ( beaconConfig . TerminalTotalDifficulty ) ) != 0 {
return TransitionConfiguration { } , fmt . Errorf ( "the execution layer has the wrong total terminal difficulty. expected %v, but instead got: %d" , beaconConfig . TerminalTotalDifficulty , terminalTotalDifficulty )
2022-02-15 09:30:54 +00:00
}
2022-02-21 17:05:39 +00:00
if chainConfig . TerminalBlockHash != ( common . Hash { } ) && beaconConfig . TerminalBlockHash != ( common . Hash { } ) &&
chainConfig . TerminalBlockHash != beaconConfig . TerminalBlockHash {
return TransitionConfiguration { } , fmt . Errorf ( "the execution layer has the wrong block hash. expected %s, but instead got: %s" , beaconConfig . TerminalBlockHash , chainConfig . TerminalBlockHash )
2022-02-15 09:30:54 +00:00
}
return TransitionConfiguration {
TerminalTotalDifficulty : ( * hexutil . Big ) ( terminalTotalDifficulty ) ,
2022-02-21 17:05:39 +00:00
TerminalBlockHash : chainConfig . TerminalBlockHash ,
TerminalBlockNumber : hexutil . Uint64 ( chainConfig . TerminalBlockNumber ) ,
2022-02-15 09:30:54 +00:00
} , nil
}
2021-11-07 01:38:36 +00:00
// NewEngineAPI returns EngineImpl instance
2021-11-22 10:36:52 +00:00
func NewEngineAPI ( base * BaseAPI , db kv . RoDB , api services . ApiBackend ) * EngineImpl {
2021-11-07 01:38:36 +00:00
return & EngineImpl {
BaseAPI : base ,
db : db ,
2021-11-22 10:36:52 +00:00
api : api ,
2021-11-07 01:38:36 +00:00
}
}