From e609f10a5c3c64e807f7549b68747c4d3b28afca Mon Sep 17 00:00:00 2001 From: gop Date: Wed, 27 Sep 2023 09:50:25 -0500 Subject: [PATCH] bugfix: SetCurrentHeader bug with not updating the current header CurrentHeader is set based on POEM decision --- core/core.go | 8 ++--- core/headerchain.go | 17 ++++++---- core/slice.go | 65 +++++++++++++++--------------------- eth/api_backend.go | 2 +- internal/quaiapi/backend.go | 2 +- internal/quaiapi/quai_api.go | 3 +- quaiclient/quaiclient.go | 9 ++--- 7 files changed, 49 insertions(+), 57 deletions(-) diff --git a/core/core.go b/core/core.go index c7e4d4675e..96873627d9 100644 --- a/core/core.go +++ b/core/core.go @@ -112,7 +112,7 @@ func (c *Core) InsertChain(blocks types.Blocks) (int, error) { log.Info("Already processing block:", "Number:", block.Header().NumberArray(), "Hash:", block.Hash()) return idx, errors.New("Already in process of appending this block") } - newPendingEtxs, _, err := c.sl.Append(block.Header(), types.EmptyHeader(), common.Hash{}, false, nil) + newPendingEtxs, _, _, err := c.sl.Append(block.Header(), types.EmptyHeader(), common.Hash{}, false, nil) c.processingCache.Remove(block.Hash()) if err == nil { // If we have a dom, send the dom any pending ETXs which will become @@ -462,8 +462,8 @@ func (c *Core) WriteBlock(block *types.Block) { } } -func (c *Core) Append(header *types.Header, manifest types.BlockManifest, domPendingHeader *types.Header, domTerminus common.Hash, domOrigin bool, newInboundEtxs types.Transactions) (types.Transactions, bool, error) { - newPendingEtxs, subReorg, err := c.sl.Append(header, domPendingHeader, domTerminus, domOrigin, newInboundEtxs) +func (c *Core) Append(header *types.Header, manifest types.BlockManifest, domPendingHeader *types.Header, domTerminus common.Hash, domOrigin bool, newInboundEtxs types.Transactions) (types.Transactions, bool, bool, error) { + newPendingEtxs, subReorg, setHead, err := c.sl.Append(header, domPendingHeader, domTerminus, domOrigin, newInboundEtxs) if err != nil { if err.Error() == ErrBodyNotFound.Error() || err.Error() == consensus.ErrUnknownAncestor.Error() || err.Error() == ErrSubNotSyncedToDom.Error() { // Fetch the blocks for each hash in the manifest @@ -489,7 +489,7 @@ func (c *Core) Append(header *types.Header, manifest types.BlockManifest, domPen } } } - return newPendingEtxs, subReorg, err + return newPendingEtxs, subReorg, setHead, err } func (c *Core) DownloadBlocksInManifest(manifest types.BlockManifest, entropy *big.Int) { diff --git a/core/headerchain.go b/core/headerchain.go index d9271ce678..a620541a3c 100644 --- a/core/headerchain.go +++ b/core/headerchain.go @@ -304,12 +304,10 @@ func (hc *HeaderChain) SetCurrentHeader(head *types.Header) error { if prevHeader.Hash() == head.Hash() { return nil } - //Find a common header - commonHeader := hc.findCommonAncestor(head) - newHeader := head // write the head block hash to the db rawdb.WriteHeadBlockHash(hc.headerDb, head.Hash()) + log.Info("Setting the current header", "Hash", head.Hash(), "Number", head.NumberArray()) hc.currentHeader.Store(head) // If head is the normal extension of canonical head, we can return by just wiring the canonical hash. @@ -322,6 +320,10 @@ func (hc *HeaderChain) SetCurrentHeader(head *types.Header) error { return nil } + //Find a common header + commonHeader := hc.findCommonAncestor(head) + newHeader := head + // Delete each header and rollback state processor until common header // Accumulate the hash slice stack var hashStack []*types.Header @@ -383,15 +385,16 @@ func (hc *HeaderChain) ReadInboundEtxsAndAppendBlock(header *types.Header) error // findCommonAncestor func (hc *HeaderChain) findCommonAncestor(header *types.Header) *types.Header { + current := types.CopyHeader(header) for { - if header == nil { + if current == nil { return nil } - canonicalHash := rawdb.ReadCanonicalHash(hc.headerDb, header.NumberU64()) - if canonicalHash == header.Hash() { + canonicalHash := rawdb.ReadCanonicalHash(hc.headerDb, current.NumberU64()) + if canonicalHash == current.Hash() { return hc.GetHeaderByHash(canonicalHash) } - header = hc.GetHeader(header.ParentHash(), header.NumberU64()-1) + current = hc.GetHeader(current.ParentHash(), current.NumberU64()-1) } } diff --git a/core/slice.go b/core/slice.go index 0448e424d4..9fb532a4d3 100644 --- a/core/slice.go +++ b/core/slice.go @@ -128,11 +128,12 @@ func NewSlice(db ethdb.Database, config *Config, txConfig *TxPoolConfig, txLooku // Append takes a proposed header and constructs a local block and attempts to hierarchically append it to the block graph. // If this is called from a dominant context a domTerminus must be provided else a common.Hash{} should be used and domOrigin should be set to true. -func (sl *Slice) Append(header *types.Header, domPendingHeader *types.Header, domTerminus common.Hash, domOrigin bool, newInboundEtxs types.Transactions) (types.Transactions, bool, error) { +// Return of this function is the Etxs generated in the Zone Block, subReorg bool that tells dom if should be mined on, setHead bool that determines if we should set the block as the current head and the error +func (sl *Slice) Append(header *types.Header, domPendingHeader *types.Header, domTerminus common.Hash, domOrigin bool, newInboundEtxs types.Transactions) (types.Transactions, bool, bool, error) { start := time.Now() if header.Hash() == sl.config.GenesisHash { - return nil, false, nil + return nil, false, false, nil } // Only print in Info level if block is c_startingPrintLimit behind or less @@ -145,7 +146,7 @@ func (sl *Slice) Append(header *types.Header, domPendingHeader *types.Header, do time0_1 := common.PrettyDuration(time.Since(start)) // Check if the header hash exists in the BadHashes list if sl.IsBlockHashABadHash(header.Hash()) { - return nil, false, ErrBadBlockHash + return nil, false, false, ErrBadBlockHash } time0_2 := common.PrettyDuration(time.Since(start)) @@ -153,18 +154,18 @@ func (sl *Slice) Append(header *types.Header, domPendingHeader *types.Header, do location := header.Location() _, order, err := sl.engine.CalcOrder(header) if err != nil { - return nil, false, err + return nil, false, false, err } // Don't append the block which already exists in the database. if sl.hc.HasHeader(header.Hash(), header.NumberU64()) && (sl.hc.GetTerminiByHash(header.Hash()) != nil) { log.Debug("Block has already been appended: ", "Hash: ", header.Hash()) - return nil, false, ErrKnownBlock + return nil, false, false, ErrKnownBlock } time1 := common.PrettyDuration(time.Since(start)) // This is to prevent a crash when we try to insert blocks before domClient is on. // Ideally this check should not exist here and should be fixed before we start the slice. if sl.domClient == nil && nodeCtx != common.PRIME_CTX { - return nil, false, ErrDomClientNotUp + return nil, false, false, ErrDomClientNotUp } batch := sl.sliceDb.NewBatch() @@ -172,7 +173,7 @@ func (sl *Slice) Append(header *types.Header, domPendingHeader *types.Header, do // Run Previous Coincident Reference Check (PCRC) domTerminus, newTermini, err := sl.pcrc(batch, header, domTerminus, domOrigin) if err != nil { - return nil, false, err + return nil, false, false, err } log.Debug("PCRC done", "hash", header.Hash(), "number", header.NumberArray(), "termini", newTermini) @@ -180,14 +181,14 @@ func (sl *Slice) Append(header *types.Header, domPendingHeader *types.Header, do // Append the new block err = sl.hc.AppendHeader(header) if err != nil { - return nil, false, err + return nil, false, false, err } time3 := common.PrettyDuration(time.Since(start)) // Construct the block locally block, err := sl.ConstructLocalBlock(header) if err != nil { - return nil, false, err + return nil, false, false, err } time4 := common.PrettyDuration(time.Since(start)) @@ -196,7 +197,7 @@ func (sl *Slice) Append(header *types.Header, domPendingHeader *types.Header, do // Upate the local pending header pendingHeaderWithTermini, err = sl.generateSlicePendingHeader(block, newTermini, domPendingHeader, domOrigin, true, false) if err != nil { - return nil, false, err + return nil, false, false, err } } @@ -208,7 +209,7 @@ func (sl *Slice) Append(header *types.Header, domPendingHeader *types.Header, do newInboundEtxs, _, err = sl.CollectNewlyConfirmedEtxs(block, block.Location()) if err != nil { log.Error("Error collecting newly confirmed etxs: ", "err", err) - return nil, false, ErrSubNotSyncedToDom + return nil, false, false, ErrSubNotSyncedToDom } } time5 := common.PrettyDuration(time.Since(start)) @@ -216,6 +217,7 @@ func (sl *Slice) Append(header *types.Header, domPendingHeader *types.Header, do time6 := common.PrettyDuration(time.Since(start)) var subPendingEtxs types.Transactions var subReorg bool + var setHead bool var time6_1 common.PrettyDuration var time6_2 common.PrettyDuration var time6_3 common.PrettyDuration @@ -223,9 +225,9 @@ func (sl *Slice) Append(header *types.Header, domPendingHeader *types.Header, do if nodeCtx != common.ZONE_CTX { // How to get the sub pending etxs if not running the full node?. if sl.subClients[location.SubIndex()] != nil { - subPendingEtxs, subReorg, err = sl.subClients[location.SubIndex()].Append(context.Background(), header, block.SubManifest(), pendingHeaderWithTermini.Header(), domTerminus, true, newInboundEtxs) + subPendingEtxs, subReorg, setHead, err = sl.subClients[location.SubIndex()].Append(context.Background(), header, block.SubManifest(), pendingHeaderWithTermini.Header(), domTerminus, true, newInboundEtxs) if err != nil { - return nil, false, err + return nil, false, false, err } time6_1 = common.PrettyDuration(time.Since(start)) // Cache the subordinate's pending ETXs @@ -264,7 +266,7 @@ func (sl *Slice) Append(header *types.Header, domPendingHeader *types.Header, do tempPendingHeader, err := sl.generateSlicePendingHeader(block, newTermini, domPendingHeader, domOrigin, false, false) if err != nil { - return nil, false, err + return nil, false, false, err } subReorg = sl.miningStrategy(bestPh, tempPendingHeader) @@ -275,17 +277,18 @@ func (sl *Slice) Append(header *types.Header, domPendingHeader *types.Header, do rawdb.WriteInboundEtxs(sl.sliceDb, block.Hash(), newInboundEtxs.FilterToLocation(common.NodeLocation)) } - if subReorg { + setHead = sl.poem(sl.engine.TotalLogS(block.Header()), sl.engine.TotalLogS(sl.hc.CurrentHeader())) + if setHead { err := sl.hc.SetCurrentHeader(block.Header()) if err != nil { log.Error("Error setting current header", "err", err, "Hash", block.Hash()) - return nil, false, err + return nil, false, false, err } } // Upate the local pending header pendingHeaderWithTermini, err = sl.generateSlicePendingHeader(block, newTermini, domPendingHeader, domOrigin, subReorg, false) if err != nil { - return nil, false, err + return nil, false, false, err } time9 = common.PrettyDuration(time.Since(start)) @@ -305,15 +308,15 @@ func (sl *Slice) Append(header *types.Header, domPendingHeader *types.Header, do // Append has succeeded write the batch if err := batch.Write(); err != nil { - return nil, false, err + return nil, false, false, err } - if subReorg { + if setHead { if nodeCtx != common.ZONE_CTX { err := sl.hc.SetCurrentHeader(block.Header()) if err != nil { log.Error("Error setting current header", "err", err, "Hash", block.Hash()) - return nil, false, err + return nil, false, false, err } } sl.hc.chainHeadFeed.Send(ChainHeadEvent{Block: block}) @@ -340,9 +343,9 @@ func (sl *Slice) Append(header *types.Header, domPendingHeader *types.Header, do go sl.domClient.UpdateDom(context.Background(), bestPh.Termini().DomTerminus(), pendingHeaderWithTermini, common.NodeLocation) } } - return block.ExtTransactions(), subReorg, nil + return block.ExtTransactions(), subReorg, setHead, nil } else { - return subPendingEtxs, subReorg, nil + return subPendingEtxs, subReorg, setHead, nil } } @@ -648,7 +651,7 @@ func (sl *Slice) pcrc(batch ethdb.Batch, header *types.Header, domTerminus commo // POEM compares externS to the currentHead S and returns true if externS is greater func (sl *Slice) poem(externS *big.Int, currentS *big.Int) bool { - log.Debug("POEM:", "Header hash:", sl.hc.CurrentHeader().Hash(), "currentS:", common.BigBitsToBits(currentS), "externS:", common.BigBitsToBits(externS)) + log.Debug("POEM:", "currentS:", common.BigBitsToBits(currentS), "externS:", common.BigBitsToBits(externS)) reorg := currentS.Cmp(externS) <= 0 return reorg } @@ -795,27 +798,14 @@ func (sl *Slice) updatePhCacheFromDom(pendingHeader types.PendingHeader, termini if (localPendingHeader.Header().Root() != types.EmptyRootHash && nodeCtx == common.ZONE_CTX) || nodeCtx == common.REGION_CTX { block := sl.hc.GetBlockOrCandidateByHash(localPendingHeader.Header().ParentHash()) if block != nil { - err := sl.hc.SetCurrentHeader(block.Header()) - if err != nil { - log.Error("Error setting current header", "err", err, "Hash", block.Hash()) - return err - } log.Info("Choosing phHeader pickPhHead:", "NumberArray:", combinedPendingHeader.NumberArray(), "Number:", combinedPendingHeader.Number(), "ParentHash:", combinedPendingHeader.ParentHash(), "Terminus:", localPendingHeader.Termini().DomTerminus()) sl.WriteBestPhKey(localPendingHeader.Termini().DomTerminus()) - if block.Hash() != sl.hc.CurrentHeader().Hash() { - sl.hc.chainHeadFeed.Send(ChainHeadEvent{block}) - } } else { log.Warn("unable to set the current header after the cord update", "Hash", localPendingHeader.Header().ParentHash()) } } else { block := sl.hc.GetBlockOrCandidateByHash(localPendingHeader.Header().ParentHash()) if block != nil { - err := sl.hc.SetCurrentHeader(block.Header()) - if err != nil { - log.Error("Error setting current header", "err", err, "Hash", block.Hash()) - return err - } newPendingHeader, err := sl.generateSlicePendingHeader(block, localPendingHeader.Termini(), combinedPendingHeader, true, true, false) if err != nil { log.Error("Error generating slice pending header", "err", err) @@ -824,9 +814,6 @@ func (sl *Slice) updatePhCacheFromDom(pendingHeader types.PendingHeader, termini combinedPendingHeader = types.CopyHeader(newPendingHeader.Header()) log.Info("Choosing phHeader pickPhHead:", "NumberArray:", combinedPendingHeader.NumberArray(), "ParentHash:", combinedPendingHeader.ParentHash(), "Terminus:", localPendingHeader.Termini().DomTerminus()) sl.WriteBestPhKey(localPendingHeader.Termini().DomTerminus()) - if block.Hash() != sl.hc.CurrentHeader().Hash() { - sl.hc.chainHeadFeed.Send(ChainHeadEvent{block}) - } } else { log.Warn("unable to set the current header after the cord update", "Hash", localPendingHeader.Header().ParentHash()) } diff --git a/eth/api_backend.go b/eth/api_backend.go index ddaa5ed9fe..559b363cfe 100644 --- a/eth/api_backend.go +++ b/eth/api_backend.go @@ -448,7 +448,7 @@ func (b *QuaiAPIBackend) SyncProgress() quai.SyncProgress { return b.eth.Downloader().Progress() } -func (b *QuaiAPIBackend) Append(header *types.Header, manifest types.BlockManifest, domPendingHeader *types.Header, domTerminus common.Hash, domOrigin bool, newInboundEtxs types.Transactions) (types.Transactions, bool, error) { +func (b *QuaiAPIBackend) Append(header *types.Header, manifest types.BlockManifest, domPendingHeader *types.Header, domTerminus common.Hash, domOrigin bool, newInboundEtxs types.Transactions) (types.Transactions, bool, bool, error) { return b.eth.core.Append(header, manifest, domPendingHeader, domTerminus, domOrigin, newInboundEtxs) } diff --git a/internal/quaiapi/backend.go b/internal/quaiapi/backend.go index 5b4dee34ba..ff30737f9a 100644 --- a/internal/quaiapi/backend.go +++ b/internal/quaiapi/backend.go @@ -72,7 +72,7 @@ type Backend interface { SubscribeChainHeadEvent(ch chan<- core.ChainHeadEvent) event.Subscription SubscribeChainSideEvent(ch chan<- core.ChainSideEvent) event.Subscription WriteBlock(block *types.Block) - Append(header *types.Header, manifest types.BlockManifest, domPendingHeader *types.Header, domTerminus common.Hash, domOrigin bool, newInboundEtxs types.Transactions) (types.Transactions, bool, error) + Append(header *types.Header, manifest types.BlockManifest, domPendingHeader *types.Header, domTerminus common.Hash, domOrigin bool, newInboundEtxs types.Transactions) (types.Transactions, bool, bool, error) DownloadBlocksInManifest(manifest types.BlockManifest, entropy *big.Int) ConstructLocalMinedBlock(header *types.Header) (*types.Block, error) InsertBlock(ctx context.Context, block *types.Block) (int, error) diff --git a/internal/quaiapi/quai_api.go b/internal/quaiapi/quai_api.go index 8f752ae475..ee7fa3b54a 100644 --- a/internal/quaiapi/quai_api.go +++ b/internal/quaiapi/quai_api.go @@ -633,7 +633,7 @@ func (s *PublicBlockChainQuaiAPI) Append(ctx context.Context, raw json.RawMessag return nil, err } - pendingEtxs, subReorg, err := s.b.Append(body.Header, body.Manifest, body.DomPendingHeader, body.DomTerminus, body.DomOrigin, body.NewInboundEtxs) + pendingEtxs, subReorg, setHead, err := s.b.Append(body.Header, body.Manifest, body.DomPendingHeader, body.DomTerminus, body.DomOrigin, body.NewInboundEtxs) if err != nil { return nil, err } @@ -641,6 +641,7 @@ func (s *PublicBlockChainQuaiAPI) Append(ctx context.Context, raw json.RawMessag fields := map[string]interface{}{ "pendingEtxs": pendingEtxs, "subReorg": subReorg, + "setHead": setHead, } return fields, nil diff --git a/quaiclient/quaiclient.go b/quaiclient/quaiclient.go index 90ea6c6604..d8f5c75ddc 100644 --- a/quaiclient/quaiclient.go +++ b/quaiclient/quaiclient.go @@ -86,9 +86,10 @@ type Termini struct { type appendReturns struct { Etxs types.Transactions `json:"pendingEtxs"` SubReorg bool `json:"subReorg"` + SetHead bool `json:"setHead"` } -func (ec *Client) Append(ctx context.Context, header *types.Header, manifest types.BlockManifest, domPendingHeader *types.Header, domTerminus common.Hash, domOrigin bool, newInboundEtxs types.Transactions) (types.Transactions, bool, error) { +func (ec *Client) Append(ctx context.Context, header *types.Header, manifest types.BlockManifest, domPendingHeader *types.Header, domTerminus common.Hash, domOrigin bool, newInboundEtxs types.Transactions) (types.Transactions, bool, bool, error) { fields := map[string]interface{}{ "header": header.RPCMarshalHeader(), "manifest": manifest, @@ -101,16 +102,16 @@ func (ec *Client) Append(ctx context.Context, header *types.Header, manifest typ var raw json.RawMessage err := ec.c.CallContext(ctx, &raw, "quai_append", fields) if err != nil { - return nil, false, err + return nil, false, false, err } // Decode header and transactions. var aReturns appendReturns if err := json.Unmarshal(raw, &aReturns); err != nil { - return nil, false, err + return nil, false, false, err } - return aReturns.Etxs, aReturns.SubReorg, nil + return aReturns.Etxs, aReturns.SubReorg, aReturns.SetHead, nil } func (ec *Client) DownloadBlocksInManifest(ctx context.Context, manifest types.BlockManifest, entropy *big.Int) {