diff --git a/cmd/rpcdaemon/commands/engine_api.go b/cmd/rpcdaemon/commands/engine_api.go index 728ee6555e8..c3f889475c1 100644 --- a/cmd/rpcdaemon/commands/engine_api.go +++ b/cmd/rpcdaemon/commands/engine_api.go @@ -202,7 +202,9 @@ func (e *EngineImpl) GetPayloadV1(ctx context.Context, payloadID hexutil.Bytes) }, nil } -// Gets a transistionConfiguration and pings the execution layer and checks if the execution layer has the correct configurations +// 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 func (e *EngineImpl) ExchangeTransitionConfigurationV1(ctx context.Context, beaconConfig TransitionConfiguration) (TransitionConfiguration, error) { tx, err := e.db.BeginRo(ctx) diff --git a/core/block_proposer.go b/core/block_proposer.go new file mode 100644 index 00000000000..b8f81b8e537 --- /dev/null +++ b/core/block_proposer.go @@ -0,0 +1,11 @@ +package core + +import "github.com/ledgerwatch/erigon/common" + +// See https://github.com/ethereum/execution-apis/blob/main/src/engine/specification.md#payloadattributesv1 +type BlockProposerParametersPOS struct { + ParentHash common.Hash + Timestamp uint64 + PrevRandao common.Hash + SuggestedFeeRecipient common.Address +} diff --git a/core/chain_makers.go b/core/chain_makers.go index 35cf450a119..6d20fc6b151 100644 --- a/core/chain_makers.go +++ b/core/chain_makers.go @@ -396,6 +396,33 @@ func GenerateChain(config *params.ChainConfig, parent *types.Block, engine conse return &ChainPack{Length: n, Headers: headers, Blocks: blocks, Receipts: receipts, TopBlock: blocks[n-1]}, nil } +func MakeEmptyHeader(parent *types.Header, chainConfig *params.ChainConfig, timestamp uint64, targetGasLimit *uint64) *types.Header { + header := &types.Header{ + Root: parent.Root, + ParentHash: parent.Hash(), + Number: new(big.Int).Add(parent.Number, common.Big1), + Difficulty: common.Big0, + Time: timestamp, + } + + parentGasLimit := parent.GasLimit + // Set baseFee and GasLimit if we are on an EIP-1559 chain + if chainConfig.IsLondon(header.Number.Uint64()) { + header.Eip1559 = true + header.BaseFee = misc.CalcBaseFee(chainConfig, parent) + if !chainConfig.IsLondon(parent.Number.Uint64()) { + parentGasLimit = parent.GasLimit * params.ElasticityMultiplier + } + } + if targetGasLimit != nil { + header.GasLimit = CalcGasLimit(parentGasLimit, *targetGasLimit) + } else { + header.GasLimit = parentGasLimit + } + + return header +} + func makeHeader(chain consensus.ChainReader, parent *types.Block, state *state.IntraBlockState, engine consensus.Engine) *types.Header { var time uint64 if parent.Time() == 0 { @@ -404,32 +431,17 @@ func makeHeader(chain consensus.ChainReader, parent *types.Block, state *state.I time = parent.Time() + 10 // block time is fixed at 10 seconds } - header := &types.Header{ - Root: common.Hash{}, - ParentHash: parent.Hash(), - Coinbase: parent.Coinbase(), - Difficulty: engine.CalcDifficulty(chain, time, - time-10, - parent.Difficulty(), - parent.NumberU64(), - parent.Hash(), - parent.UncleHash(), - parent.Seal(), - ), - GasLimit: CalcGasLimit(parent.GasLimit(), parent.GasLimit()), - Number: new(big.Int).Add(parent.Number(), common.Big1), - Time: time, - } + header := MakeEmptyHeader(parent.Header(), chain.Config(), time, nil) + header.Coinbase = parent.Coinbase() + header.Difficulty = engine.CalcDifficulty(chain, time, + time-10, + parent.Difficulty(), + parent.NumberU64(), + parent.Hash(), + parent.UncleHash(), + parent.Seal(), + ) header.Seal = engine.GenerateSeal(chain, header, parent.Header(), nil) - - if chain.Config().IsLondon(header.Number.Uint64()) { - header.BaseFee = misc.CalcBaseFee(chain.Config(), parent.Header()) - header.Eip1559 = true - if !chain.Config().IsLondon(parent.NumberU64()) { - parentGasLimit := parent.GasLimit() * params.ElasticityMultiplier - header.GasLimit = CalcGasLimit(parentGasLimit, parentGasLimit) - } - } header.WithSeal = chain.Config().IsHeaderWithSeal() return header diff --git a/core/types/block.go b/core/types/block.go index 1f768c57c27..c89910e0a92 100644 --- a/core/types/block.go +++ b/core/types/block.go @@ -958,6 +958,7 @@ func NewBlock(header *Header, txs []Transaction, uncles []*Header, receipts []*R if len(receipts) == 0 { b.header.ReceiptHash = EmptyRootHash + b.header.Bloom = Bloom{} } else { b.header.ReceiptHash = DeriveSha(Receipts(receipts)) b.header.Bloom = CreateBloom(receipts) diff --git a/eth/backend.go b/eth/backend.go index 09ca00e7bb1..dd4dda590ab 100644 --- a/eth/backend.go +++ b/eth/backend.go @@ -395,16 +395,12 @@ func New(stack *node.Node, config *ethconfig.Config, txpoolCfg txpool2.Config, l ethashApi = casted.APIs(nil)[1].Service.(*ethash.API) } // proof-of-stake mining - assembleBlockPOS := func(random common.Hash, suggestedFeeRecipient common.Address, timestamp uint64) (*types.Block, error) { + assembleBlockPOS := func(param *core.BlockProposerParametersPOS) (*types.Block, error) { miningStatePos := stagedsync.NewMiningState(&config.Miner) - miningStatePos.MiningConfig.Etherbase = suggestedFeeRecipient + miningStatePos.MiningConfig.Etherbase = param.SuggestedFeeRecipient proposingSync := stagedsync.New( stagedsync.MiningStages(backend.sentryCtx, - stagedsync.StageMiningCreateBlockCfg(backend.chainDB, miningStatePos, *backend.chainConfig, backend.engine, backend.txPool2, backend.txPool2DB, &stagedsync.BlockProposerParametersPOS{ - Random: random, - SuggestedFeeRecipient: suggestedFeeRecipient, - Timestamp: timestamp, - }, tmpdir), + stagedsync.StageMiningCreateBlockCfg(backend.chainDB, miningStatePos, *backend.chainConfig, backend.engine, backend.txPool2, backend.txPool2DB, param, tmpdir), stagedsync.StageMiningExecCfg(backend.chainDB, miningStatePos, backend.notifications.Events, *backend.chainConfig, backend.engine, &vm.Config{}, tmpdir), stagedsync.StageHashStateCfg(backend.chainDB, tmpdir), stagedsync.StageTrieCfg(backend.chainDB, false, true, tmpdir, blockReader), diff --git a/eth/stagedsync/stage_mining_create_block.go b/eth/stagedsync/stage_mining_create_block.go index 359d5026d40..4911bdf36a2 100644 --- a/eth/stagedsync/stage_mining_create_block.go +++ b/eth/stagedsync/stage_mining_create_block.go @@ -9,8 +9,6 @@ import ( "math/big" "time" - "github.com/ledgerwatch/erigon/core/state" - mapset "github.com/deckarep/golang-set" libcommon "github.com/ledgerwatch/erigon-lib/common" "github.com/ledgerwatch/erigon-lib/kv" @@ -18,9 +16,9 @@ import ( "github.com/ledgerwatch/erigon/common" "github.com/ledgerwatch/erigon/common/debug" "github.com/ledgerwatch/erigon/consensus" - "github.com/ledgerwatch/erigon/consensus/misc" "github.com/ledgerwatch/erigon/core" "github.com/ledgerwatch/erigon/core/rawdb" + "github.com/ledgerwatch/erigon/core/state" "github.com/ledgerwatch/erigon/core/types" "github.com/ledgerwatch/erigon/eth/ethutils" "github.com/ledgerwatch/erigon/params" @@ -55,12 +53,6 @@ func NewMiningState(cfg *params.MiningConfig) MiningState { } } -type BlockProposerParametersPOS struct { - Random common.Hash - SuggestedFeeRecipient common.Address // For now, we apply a suggested recipient only if etherbase is unset - Timestamp uint64 -} - type MiningCreateBlockCfg struct { db kv.RwDB miner MiningState @@ -69,10 +61,10 @@ type MiningCreateBlockCfg struct { txPool2 *txpool.TxPool txPool2DB kv.RoDB tmpdir string - blockProposerParameters *BlockProposerParametersPOS + blockProposerParameters *core.BlockProposerParametersPOS } -func StageMiningCreateBlockCfg(db kv.RwDB, miner MiningState, chainConfig params.ChainConfig, engine consensus.Engine, txPool2 *txpool.TxPool, txPool2DB kv.RoDB, blockProposerParameters *BlockProposerParametersPOS, tmpdir string) MiningCreateBlockCfg { +func StageMiningCreateBlockCfg(db kv.RwDB, miner MiningState, chainConfig params.ChainConfig, engine consensus.Engine, txPool2 *txpool.TxPool, txPool2DB kv.RoDB, blockProposerParameters *core.BlockProposerParametersPOS, tmpdir string) MiningCreateBlockCfg { return MiningCreateBlockCfg{ db: db, miner: miner, @@ -105,7 +97,11 @@ func SpawnMiningCreateBlockStage(s *StageState, tx kv.RwTx, cfg MiningCreateBloc } parent := rawdb.ReadHeaderByNumber(tx, executionAt) if parent == nil { // todo: how to return error and don't stop Erigon? - return fmt.Errorf(fmt.Sprintf("[%s] Empty block", logPrefix), "blocknum", executionAt) + return fmt.Errorf("empty block %d", executionAt) + } + + if cfg.blockProposerParameters != nil && cfg.blockProposerParameters.ParentHash != parent.Hash() { + return fmt.Errorf("wrong head block: %x (current) vs %x (requested)", parent.Hash(), cfg.blockProposerParameters.ParentHash) } isTrans, err := rawdb.Transitioned(tx, executionAt, cfg.chainConfig.TerminalTotalDifficulty) @@ -187,40 +183,23 @@ func SpawnMiningCreateBlockStage(s *StageState, tx kv.RwTx, cfg MiningCreateBloc } // re-written miner/worker.go:commitNewWork - var timestamp int64 - // If we are on proof-of-stake timestamp should be already set for us + var timestamp uint64 if !isTrans { - timestamp = time.Now().Unix() - if parent.Time >= uint64(timestamp) { - timestamp = int64(parent.Time + 1) + timestamp = uint64(time.Now().Unix()) + if parent.Time >= timestamp { + timestamp = parent.Time + 1 } + } else { + // If we are on proof-of-stake timestamp should be already set for us + timestamp = cfg.blockProposerParameters.Timestamp } - num := parent.Number - header := &types.Header{ - ParentHash: parent.Hash(), - Number: num.Add(num, common.Big1), - GasLimit: core.CalcGasLimit(parent.GasLimit, cfg.miner.MiningConfig.GasLimit), - Extra: cfg.miner.MiningConfig.ExtraData, - Time: uint64(timestamp), - } + header := core.MakeEmptyHeader(parent, &cfg.chainConfig, timestamp, &cfg.miner.MiningConfig.GasLimit) + header.Coinbase = coinbase + header.Extra = cfg.miner.MiningConfig.ExtraData - // Set baseFee and GasLimit if we are on an EIP-1559 chain - if cfg.chainConfig.IsLondon(header.Number.Uint64()) { - header.Eip1559 = true - header.BaseFee = misc.CalcBaseFee(&cfg.chainConfig, parent) - if !cfg.chainConfig.IsLondon(parent.Number.Uint64()) { - parentGasLimit := parent.GasLimit * params.ElasticityMultiplier - header.GasLimit = core.CalcGasLimit(parentGasLimit, cfg.miner.MiningConfig.GasLimit) - } - } log.Info(fmt.Sprintf("[%s] Start mine", logPrefix), "block", executionAt+1, "baseFee", header.BaseFee, "gasLimit", header.GasLimit) - // Only set the coinbase if our consensus engine is running (avoid spurious block rewards) - //if w.isRunning() { - header.Coinbase = coinbase - //} - stateReader := state.NewPlainStateReader(tx) ibs := state.New(stateReader) @@ -237,9 +216,7 @@ func SpawnMiningCreateBlockStage(s *StageState, tx kv.RwTx, cfg MiningCreateBloc } if isTrans { - // We apply pre-made fields - header.MixDigest = cfg.blockProposerParameters.Random - header.Time = cfg.blockProposerParameters.Timestamp + header.MixDigest = cfg.blockProposerParameters.PrevRandao current.Header = header current.Uncles = nil diff --git a/ethdb/privateapi/ethbackend.go b/ethdb/privateapi/ethbackend.go index ce1bbfcde55..b7de9f02a4e 100644 --- a/ethdb/privateapi/ethbackend.go +++ b/ethdb/privateapi/ethbackend.go @@ -1,7 +1,6 @@ package privateapi import ( - "bytes" "context" "errors" "fmt" @@ -19,14 +18,17 @@ import ( "github.com/ledgerwatch/erigon/cmd/rpcdaemon/interfaces" "github.com/ledgerwatch/erigon/common" "github.com/ledgerwatch/erigon/consensus/serenity" + "github.com/ledgerwatch/erigon/core" + "github.com/ledgerwatch/erigon/core/rawdb" "github.com/ledgerwatch/erigon/core/types" "github.com/ledgerwatch/erigon/params" "github.com/ledgerwatch/erigon/rlp" + "github.com/ledgerwatch/erigon/rpc" "github.com/ledgerwatch/log/v3" "google.golang.org/protobuf/types/known/emptypb" ) -type assemblePayloadPOSFunc func(random common.Hash, suggestedFeeRecipient common.Address, timestamp uint64) (*types.Block, error) +type assemblePayloadPOSFunc func(param *core.BlockProposerParametersPOS) (*types.Block, error) // EthBackendAPIVersion // 2.0.0 - move all mining-related methods to 'txpool/mining' server @@ -37,6 +39,8 @@ var EthBackendAPIVersion = &types2.VersionReply{Major: 3, Minor: 0, Patch: 0} const MaxPendingPayloads = 128 +var UnknownPayload = rpc.CustomError{Code: -32001, Message: "Unknown payload"} + type EthBackendServer struct { remote.UnimplementedETHBACKENDServer // must be embedded to have forward compatible implementations. @@ -48,7 +52,7 @@ type EthBackendServer struct { config *params.ChainConfig // Block proposing for proof-of-stake payloadId uint64 - pendingPayloads map[uint64]types2.ExecutionPayload + pendingPayloads map[uint64]*pendingPayload // Send new Beacon Chain payloads to staged sync newPayloadCh chan<- PayloadMessage // Send Beacon Chain fork choice updates to staged sync @@ -94,13 +98,18 @@ type ForkChoiceMessage struct { FinalizedBlockHash common.Hash } +type pendingPayload struct { + block *types.Block + built bool +} + func NewEthBackendServer(ctx context.Context, eth EthBackend, db kv.RwDB, events *Events, blockReader interfaces.BlockAndTxnReader, config *params.ChainConfig, newPayloadCh chan<- PayloadMessage, forkChoiceCh chan<- ForkChoiceMessage, statusCh <-chan PayloadStatus, waitingForBeaconChain *uint32, skipCycleHack chan struct{}, assemblePayloadPOS assemblePayloadPOSFunc, proposing bool, ) *EthBackendServer { return &EthBackendServer{ctx: ctx, eth: eth, events: events, db: db, blockReader: blockReader, config: config, newPayloadCh: newPayloadCh, forkChoiceCh: forkChoiceCh, statusCh: statusCh, waitingForBeaconChain: waitingForBeaconChain, - pendingPayloads: make(map[uint64]types2.ExecutionPayload), skipCycleHack: skipCycleHack, + pendingPayloads: make(map[uint64]*pendingPayload), skipCycleHack: skipCycleHack, assemblePayloadPOS: assemblePayloadPOS, proposing: proposing, syncCond: sync.NewCond(&sync.Mutex{}), } } @@ -240,7 +249,7 @@ func (s *EthBackendServer) stageLoopIsBusy() bool { // and thus waitingForBeaconChain is not set yet. // TODO(yperbasis): find a more elegant solution - time.Sleep(time.Millisecond) + time.Sleep(2 * time.Millisecond) } } return atomic.LoadUint32(s.waitingForBeaconChain) == 0 @@ -330,19 +339,45 @@ func (s *EthBackendServer) EngineGetPayloadV1(ctx context.Context, req *remote.E return nil, fmt.Errorf("not a proof-of-stake chain") } - for { - payload, ok := s.pendingPayloads[req.PayloadId] - if !ok { - return nil, fmt.Errorf("unknown payload") - } + payload, ok := s.pendingPayloads[req.PayloadId] + if !ok { + return nil, &UnknownPayload + } - if payload.BlockNumber != 0 { - return &payload, nil - } + // getPayload should stop the build process + // https://github.com/ethereum/execution-apis/blob/v1.0.0-alpha.7/src/engine/specification.md#payload-building + payload.built = true + + block := payload.block - // Wait for payloads assembling thread to finish - s.syncCond.Wait() + var baseFeeReply *types2.H256 + if block.Header().BaseFee != nil { + var baseFee uint256.Int + baseFee.SetFromBig(block.Header().BaseFee) + baseFeeReply = gointerfaces.ConvertUint256IntToH256(&baseFee) } + + encodedTransactions, err := types.MarshalTransactionsBinary(block.Transactions()) + if err != nil { + return nil, err + } + + return &types2.ExecutionPayload{ + ParentHash: gointerfaces.ConvertHashToH256(block.Header().ParentHash), + Coinbase: gointerfaces.ConvertAddressToH160(block.Header().Coinbase), + Timestamp: block.Header().Time, + Random: gointerfaces.ConvertHashToH256(block.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: baseFeeReply, + BlockHash: gointerfaces.ConvertHashToH256(block.Header().Hash()), + Transactions: encodedTransactions, + }, nil } // EngineForkChoiceUpdatedV1, either states new block head or request the assembling of a new block @@ -365,11 +400,12 @@ func (s *EthBackendServer) EngineForkChoiceUpdatedV1(ctx context.Context, req *r }, nil } - s.forkChoiceCh <- ForkChoiceMessage{ + forkChoiceMessage := ForkChoiceMessage{ HeadBlockHash: gointerfaces.ConvertH256ToHash(req.ForkchoiceState.HeadBlockHash), SafeBlockHash: gointerfaces.ConvertH256ToHash(req.ForkchoiceState.SafeBlockHash), FinalizedBlockHash: gointerfaces.ConvertH256ToHash(req.ForkchoiceState.FinalizedBlockHash), } + s.forkChoiceCh <- forkChoiceMessage payloadStatus := <-s.statusCh if payloadStatus.CriticalError != nil { @@ -390,13 +426,28 @@ func (s *EthBackendServer) EngineForkChoiceUpdatedV1(ctx context.Context, req *r // payload IDs start from 1 (0 signifies null) s.payloadId++ - s.pendingPayloads[s.payloadId] = types2.ExecutionPayload{ - Random: req.PayloadAttributes.Random, - Timestamp: req.PayloadAttributes.Timestamp, - Coinbase: req.PayloadAttributes.SuggestedFeeRecipient, + tx, err := s.db.BeginRo(ctx) + if err != nil { + return nil, err + } + headHash := rawdb.ReadHeadBlockHash(tx) + headNumber := rawdb.ReadHeaderNumber(tx, headHash) + headHeader := rawdb.ReadHeader(tx, headHash, *headNumber) + tx.Rollback() + + if headHeader.Hash() != forkChoiceMessage.HeadBlockHash { + return nil, fmt.Errorf("unexpected head hash: %x vs %x", headHeader.Hash(), forkChoiceMessage.HeadBlockHash) } + + emptyHeader := core.MakeEmptyHeader(headHeader, s.config, req.PayloadAttributes.Timestamp, nil) + emptyHeader.Coinbase = gointerfaces.ConvertH160toAddress(req.PayloadAttributes.SuggestedFeeRecipient) + emptyHeader.MixDigest = gointerfaces.ConvertH256ToHash(req.PayloadAttributes.Random) + + s.pendingPayloads[s.payloadId] = &pendingPayload{block: types.NewBlock(emptyHeader, nil, nil, nil)} + // Unpause assemble process s.syncCond.Broadcast() + // successfully assembled the payload and assigned the correct id return &remote.EngineForkChoiceUpdatedReply{ PayloadStatus: &remote.EnginePayloadStatus{Status: remote.EngineStatus_VALID}, @@ -425,72 +476,58 @@ func (s *EthBackendServer) StartProposer() { defer s.syncCond.L.Unlock() for { - // Wait until we have to process new payloads - s.syncCond.Wait() - - if s.shutdown { - return - } + var blockToBuild *types.Block + var payloadId uint64 - // Go over each payload and re-update them - for id := range s.pendingPayloads { - // If we already assembled this block, let's just skip it - if s.pendingPayloads[id].BlockNumber != 0 { - continue + FindPayloadToBuild: + for { + if s.shutdown { + return } - // we do not want to make a copy of the payload in the loop because it contains a lock - random := gointerfaces.ConvertH256ToHash(s.pendingPayloads[id].Random) - coinbase := gointerfaces.ConvertH160toAddress(s.pendingPayloads[id].Coinbase) - timestamp := s.pendingPayloads[id].Timestamp - // Tell the stage headers to leave space for the write transaction for mining stages - s.skipCycleHack <- struct{}{} - - block, err := s.assemblePayloadPOS(random, coinbase, timestamp) + + tx, err := s.db.BeginRo(s.ctx) if err != nil { - log.Warn("Error during block assembling", "err", err.Error()) + log.Error("Error while opening txn in block proposer", "err", err.Error()) return } - var baseFeeReply *types2.H256 - if block.Header().BaseFee != nil { - var baseFee uint256.Int - baseFee.SetFromBig(block.Header().BaseFee) - baseFeeReply = gointerfaces.ConvertUint256IntToH256(&baseFee) - } - var encodedTransactions [][]byte - buf := bytes.NewBuffer(nil) - - for _, tx := range block.Transactions() { - buf.Reset() - // EIP-2718 txn shouldn't be additionally wrapped as RLP strings, - // so MarshalBinary instead of rlp.Encode - err := tx.MarshalBinary(buf) - if err != nil { - log.Warn("Failed to marshal transaction", "err", err.Error()) - return + headHash := rawdb.ReadHeadBlockHash(tx) + tx.Rollback() + + for id, payload := range s.pendingPayloads { + if !payload.built && payload.block.ParentHash() == headHash { + blockToBuild = payload.block + payloadId = id + break FindPayloadToBuild } - encodedTransactions = append(encodedTransactions, common.CopyBytes(buf.Bytes())) - } - // Set parameters accordingly to what the beacon chain told us and from what the mining stage told us - s.pendingPayloads[id] = types2.ExecutionPayload{ - ParentHash: gointerfaces.ConvertHashToH256(block.Header().ParentHash), - Coinbase: gointerfaces.ConvertAddressToH160(block.Header().Coinbase), - Timestamp: s.pendingPayloads[id].Timestamp, - Random: s.pendingPayloads[id].Random, - 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: baseFeeReply, - BlockHash: gointerfaces.ConvertHashToH256(block.Header().Hash()), - Transactions: encodedTransactions, } + + // Wait until we have to process new payloads + s.syncCond.Wait() } - // Broadcast the signal that an entire loop over pending payloads has been executed - s.syncCond.Broadcast() + // Tell the stage headers to leave space for the write transaction for mining stages + s.skipCycleHack <- struct{}{} + + param := core.BlockProposerParametersPOS{ + ParentHash: blockToBuild.ParentHash(), + Timestamp: blockToBuild.Header().Time, + PrevRandao: blockToBuild.MixDigest(), + SuggestedFeeRecipient: blockToBuild.Header().Coinbase, + } + + s.syncCond.L.Unlock() + block, err := s.assemblePayloadPOS(¶m) + s.syncCond.L.Lock() + + if err != nil { + log.Warn("Error during block assembling", "err", err.Error()) + } else { + payload, ok := s.pendingPayloads[payloadId] + if ok && !payload.built { // don't update after engine_getPayload was called + payload.block = block + payload.built = true + } + } } }() } diff --git a/rpc/errors.go b/rpc/errors.go index dbfde8b1965..5375a68621c 100644 --- a/rpc/errors.go +++ b/rpc/errors.go @@ -25,6 +25,7 @@ var ( _ Error = new(invalidRequestError) _ Error = new(invalidMessageError) _ Error = new(invalidParamsError) + _ Error = new(CustomError) ) const defaultErrorCode = -32000 @@ -72,3 +73,12 @@ type invalidParamsError struct{ message string } func (e *invalidParamsError) ErrorCode() int { return -32602 } func (e *invalidParamsError) Error() string { return e.message } + +type CustomError struct { + Code int + Message string +} + +func (e *CustomError) ErrorCode() int { return e.Code } + +func (e *CustomError) Error() string { return e.Message }