package eth1 import ( "context" "fmt" "reflect" "github.com/holiman/uint256" libcommon "github.com/ledgerwatch/erigon-lib/common" "github.com/ledgerwatch/erigon-lib/gointerfaces" "github.com/ledgerwatch/erigon-lib/gointerfaces/execution" types2 "github.com/ledgerwatch/erigon-lib/gointerfaces/types" "github.com/ledgerwatch/erigon/core" "github.com/ledgerwatch/erigon/core/types" "github.com/ledgerwatch/erigon/rpc" "github.com/ledgerwatch/erigon/turbo/builder" "github.com/ledgerwatch/erigon/turbo/engineapi/engine_helpers" "github.com/ledgerwatch/erigon/turbo/execution/eth1/eth1_utils" ) func (e *EthereumExecutionModule) checkWithdrawalsPresence(time uint64, withdrawals []*types.Withdrawal) error { if !e.config.IsShanghai(time) && withdrawals != nil { return &rpc.InvalidParamsError{Message: "withdrawals before shanghai"} } if e.config.IsShanghai(time) && withdrawals == nil { return &rpc.InvalidParamsError{Message: "missing withdrawals list"} } return nil } func (e *EthereumExecutionModule) evictOldBuilders() { ids := libcommon.SortedKeys(e.builders) // remove old builders so that at most MaxBuilders - 1 remain for i := 0; i <= len(e.builders)-engine_helpers.MaxBuilders; i++ { delete(e.builders, ids[i]) } } // Missing: NewPayload, AssembleBlock func (e *EthereumExecutionModule) AssembleBlock(ctx context.Context, req *execution.AssembleBlockRequest) (*execution.AssembleBlockResponse, error) { if !e.semaphore.TryAcquire(1) { return &execution.AssembleBlockResponse{ Id: 0, Busy: true, }, nil } defer e.semaphore.Release(1) param := core.BlockBuilderParameters{ ParentHash: gointerfaces.ConvertH256ToHash(req.ParentHash), Timestamp: req.Timestamp, PrevRandao: gointerfaces.ConvertH256ToHash(req.PrevRandao), SuggestedFeeRecipient: gointerfaces.ConvertH160toAddress(req.SuggestedFeeRecipient), Withdrawals: eth1_utils.ConvertWithdrawalsFromRpc(req.Withdrawals), } if err := e.checkWithdrawalsPresence(param.Timestamp, param.Withdrawals); err != nil { return nil, err } if req.ParentBeaconBlockRoot != nil { pbbr := libcommon.Hash(gointerfaces.ConvertH256ToHash(req.ParentBeaconBlockRoot)) param.ParentBeaconBlockRoot = &pbbr } // First check if we're already building a block with the requested parameters if reflect.DeepEqual(e.lastParameters, ¶m) { e.logger.Info("[ForkChoiceUpdated] duplicate build request") return &execution.AssembleBlockResponse{ Id: e.nextPayloadId, Busy: false, }, nil } // Initiate payload building e.evictOldBuilders() e.nextPayloadId++ param.PayloadId = e.nextPayloadId e.lastParameters = ¶m e.builders[e.nextPayloadId] = builder.NewBlockBuilder(e.builderFunc, ¶m) e.logger.Info("[ForkChoiceUpdated] BlockBuilder added", "payload", e.nextPayloadId) return &execution.AssembleBlockResponse{ Id: e.nextPayloadId, Busy: false, }, nil } // The expected value to be received by the feeRecipient in wei func blockValue(br *types.BlockWithReceipts, baseFee *uint256.Int) *uint256.Int { blockValue := uint256.NewInt(0) txs := br.Block.Transactions() for i := range txs { gas := new(uint256.Int).SetUint64(br.Receipts[i].GasUsed) effectiveTip := txs[i].GetEffectiveGasTip(baseFee) txValue := new(uint256.Int).Mul(gas, effectiveTip) blockValue.Add(blockValue, txValue) } return blockValue } func (e *EthereumExecutionModule) GetAssembledBlock(ctx context.Context, req *execution.GetAssembledBlockRequest) (*execution.GetAssembledBlockResponse, error) { if !e.semaphore.TryAcquire(1) { return &execution.GetAssembledBlockResponse{ Busy: true, }, nil } defer e.semaphore.Release(1) payloadId := req.Id builder, ok := e.builders[payloadId] if !ok { return &execution.GetAssembledBlockResponse{ Busy: false, }, nil } blockWithReceipts, err := builder.Stop() if err != nil { e.logger.Error("Failed to build PoS block", "err", err) return nil, err } block := blockWithReceipts.Block header := block.Header() baseFee := new(uint256.Int) baseFee.SetFromBig(header.BaseFee) encodedTransactions, err := types.MarshalTransactionsBinary(block.Transactions()) if err != nil { return nil, err } payload := &types2.ExecutionPayload{ Version: 1, ParentHash: gointerfaces.ConvertHashToH256(header.ParentHash), Coinbase: gointerfaces.ConvertAddressToH160(header.Coinbase), Timestamp: header.Time, PrevRandao: gointerfaces.ConvertHashToH256(header.MixDigest), 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(), BaseFeePerGas: gointerfaces.ConvertUint256IntToH256(baseFee), BlockHash: gointerfaces.ConvertHashToH256(block.Hash()), Transactions: encodedTransactions, } if block.Withdrawals() != nil { payload.Version = 2 payload.Withdrawals = eth1_utils.ConvertWithdrawalsToRpc(block.Withdrawals()) } if header.BlobGasUsed != nil && header.ExcessBlobGas != nil { payload.Version = 3 payload.BlobGasUsed = header.BlobGasUsed payload.ExcessBlobGas = header.ExcessBlobGas } blockValue := blockValue(blockWithReceipts, baseFee) blobsBundle := &types2.BlobsBundleV1{} for i, tx := range block.Transactions() { if tx.Type() != types.BlobTxType { continue } blobTx, ok := tx.(*types.BlobTxWrapper) if !ok { return nil, fmt.Errorf("expected blob transaction to be type BlobTxWrapper, got: %T", blobTx) } versionedHashes, commitments, proofs, blobs := blobTx.GetBlobHashes(), blobTx.Commitments, blobTx.Proofs, blobTx.Blobs lenCheck := len(versionedHashes) if lenCheck != len(commitments) || lenCheck != len(proofs) || lenCheck != len(blobs) { return nil, fmt.Errorf("tx %d in block %s has inconsistent commitments (%d) / proofs (%d) / blobs (%d) / "+ "versioned hashes (%d)", i, block.Hash(), len(commitments), len(proofs), len(blobs), lenCheck) } for _, commitment := range commitments { c := types.KZGCommitment{} copy(c[:], commitment[:]) blobsBundle.Commitments = append(blobsBundle.Commitments, c[:]) } for _, proof := range proofs { p := types.KZGProof{} copy(p[:], proof[:]) blobsBundle.Proofs = append(blobsBundle.Proofs, p[:]) } for _, blob := range blobs { b := types.Blob{} copy(b[:], blob[:]) blobsBundle.Blobs = append(blobsBundle.Blobs, b[:]) } } return &execution.GetAssembledBlockResponse{ Data: &execution.AssembledBlockData{ ExecutionPayload: payload, BlockValue: gointerfaces.ConvertUint256IntToH256(blockValue), BlobsBundle: blobsBundle, }, Busy: false, }, nil }