diff --git a/consensus/consensus.go b/consensus/consensus.go index 1687bf95e9..4a9908691e 100644 --- a/consensus/consensus.go +++ b/consensus/consensus.go @@ -150,7 +150,7 @@ type PoSA interface { IsLocalBlock(header *types.Header) bool AllowLightProcess(chain ChainReader, currentHeader *types.Header) bool GetJustifiedHeader(chain ChainHeaderReader, header *types.Header) *types.Header - GetFinalizedHeader(chain ChainHeaderReader, header *types.Header) *types.Header + GetFinalizedHeader(chain ChainHeaderReader, header *types.Header, backward uint64) *types.Header VerifyVote(chain ChainHeaderReader, vote *types.VoteEnvelope) error IsActiveValidatorAt(chain ChainHeaderReader, header *types.Header) bool } diff --git a/consensus/parlia/parlia.go b/consensus/parlia/parlia.go index c151264583..4fa855db6c 100644 --- a/consensus/parlia/parlia.go +++ b/consensus/parlia/parlia.go @@ -1685,11 +1685,6 @@ func (p *Parlia) GetJustifiedHeader(chain consensus.ChainHeaderReader, header *t return nil } - // There won't any attestation in first two blocks, return root. - if header.Number.Uint64() < 2 { - return chain.GetHeaderByNumber(0) - } - snap, err := p.snapshot(chain, header.Number.Uint64(), header.Hash(), nil) if err != nil { log.Error("Unexpected error when getting snapshot", @@ -1697,11 +1692,16 @@ func (p *Parlia) GetJustifiedHeader(chain consensus.ChainHeaderReader, header *t return nil } - // If there is no vote justified block, then return root. + // If there is no vote justified block, then return root or naturally justified block. if snap.Attestation == nil { - return chain.GetHeaderByNumber(0) + if header.Number.Uint64() <= naturallyJustifiedDist { + return chain.GetHeaderByNumber(0) + } + // Return naturally justified block. + return FindAncientHeader(header, naturallyJustifiedDist, chain, nil) } - // Return naturally justified block. + + // If the latest vote justified block is too far, return naturally justified block. if snap.Number-snap.Attestation.TargetNumber > naturallyJustifiedDist { return FindAncientHeader(header, naturallyJustifiedDist, chain, nil) } @@ -1709,14 +1709,18 @@ func (p *Parlia) GetJustifiedHeader(chain consensus.ChainHeaderReader, header *t return chain.GetHeaderByHash(snap.Attestation.TargetHash) } -// GetFinalizedHeader returns highest finalized block header before the specific block, -// the attestation within the specific block will be taken into account. -func (p *Parlia) GetFinalizedHeader(chain consensus.ChainHeaderReader, header *types.Header) *types.Header { +// GetFinalizedHeader returns highest finalized block header before the specific block. +// It will first to find vote finalized block within the specific backward blocks, the suggested backward blocks is 21. +// If the vote finalized block not found, return its previous backward block. +func (p *Parlia) GetFinalizedHeader(chain consensus.ChainHeaderReader, header *types.Header, backward uint64) *types.Header { + if chain == nil || header == nil { + return nil + } if !chain.Config().IsLynn(header.Number) { return chain.GetHeaderByNumber(0) } - if chain == nil || header == nil || header.Number.Uint64() < 2 { - return chain.GetHeaderByNumber(0) + if header.Number.Uint64() < backward { + backward = header.Number.Uint64() } snap, err := p.snapshot(chain, header.Number.Uint64(), header.Hash(), nil) @@ -1726,7 +1730,7 @@ func (p *Parlia) GetFinalizedHeader(chain consensus.ChainHeaderReader, header *t return nil } - for snap.Attestation != nil { + for snap.Attestation != nil && snap.Attestation.SourceNumber >= header.Number.Uint64()-backward { if snap.Attestation.TargetNumber == snap.Attestation.SourceNumber+1 { return chain.GetHeaderByHash(snap.Attestation.SourceHash) } @@ -1739,7 +1743,7 @@ func (p *Parlia) GetFinalizedHeader(chain consensus.ChainHeaderReader, header *t } } - return chain.GetHeaderByNumber(0) + return FindAncientHeader(header, backward, chain, nil) } // =========================== utility function ========================== diff --git a/core/blockchain.go b/core/blockchain.go index 4e3e03bf8c..d50cf43ebd 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -545,7 +545,7 @@ func (bc *BlockChain) getJustifiedNumber(header *types.Header) uint64 { // getFinalizedNumber returns the highest finalized number before the specific block. func (bc *BlockChain) getFinalizedNumber(header *types.Header) uint64 { if p, ok := bc.engine.(consensus.PoSA); ok { - if finalizedHeader := p.GetFinalizedHeader(bc, header); finalizedHeader != nil { + if finalizedHeader := p.GetFinalizedHeader(bc, header, types.NaturallyFinalizedDist); finalizedHeader != nil { return finalizedHeader.Number.Uint64() } } @@ -553,6 +553,27 @@ func (bc *BlockChain) getFinalizedNumber(header *types.Header) uint64 { return 0 } +// isFinalizedBlockHigher returns true when the new block's finalized block is higher than current block. +func (bc *BlockChain) isFinalizedBlockHigher(header *types.Header, curHeader *types.Header) bool { + p, ok := bc.engine.(consensus.PoSA) + if !ok { + return false + } + + ancestor := rawdb.FindCommonAncestor(bc.db, header, curHeader) + if ancestor == nil { + return false + } + + finalized := p.GetFinalizedHeader(bc, header, header.Number.Uint64()-ancestor.Number.Uint64()) + curFinalized := p.GetFinalizedHeader(bc, curHeader, curHeader.Number.Uint64()-ancestor.Number.Uint64()) + if finalized == nil || curFinalized == nil { + return false + } + + return finalized.Number.Uint64() > curFinalized.Number.Uint64() +} + // loadLastState loads the last known chain state from the database. This method // assumes that the chain manager mutex is held. func (bc *BlockChain) loadLastState() error { @@ -1842,7 +1863,7 @@ func (bc *BlockChain) writeBlockWithState(block *types.Block, receipts []*types. // Please refer to http://www.cs.cornell.edu/~ie53/publications/btcProcFC.pdf reorg := externTd.Cmp(localTd) > 0 currentBlock = bc.CurrentBlock() - if bc.getFinalizedNumber(block.Header()) > bc.getFinalizedNumber(currentBlock.Header()) { + if bc.isFinalizedBlockHigher(block.Header(), currentBlock.Header()) { reorg = true } if !reorg && externTd.Cmp(localTd) == 0 { @@ -1890,7 +1911,7 @@ func (bc *BlockChain) writeBlockWithState(block *types.Block, receipts []*types. if emitHeadEvent { bc.chainHeadFeed.Send(ChainHeadEvent{Block: block}) if posa, ok := bc.Engine().(consensus.PoSA); ok { - if finalizedHeader := posa.GetFinalizedHeader(bc, block.Header()); finalizedHeader != nil { + if finalizedHeader := posa.GetFinalizedHeader(bc, block.Header(), types.NaturallyFinalizedDist); finalizedHeader != nil { bc.finalizedHeaderFeed.Send(FinalizedHeaderEvent{finalizedHeader}) } } @@ -1997,7 +2018,7 @@ func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals bool) (int, er if lastCanon != nil && bc.CurrentBlock().Hash() == lastCanon.Hash() { bc.chainHeadFeed.Send(ChainHeadEvent{lastCanon}) if posa, ok := bc.Engine().(consensus.PoSA); ok { - if finalizedHeader := posa.GetFinalizedHeader(bc, lastCanon.Header()); finalizedHeader != nil { + if finalizedHeader := posa.GetFinalizedHeader(bc, lastCanon.Header(), types.NaturallyFinalizedDist); finalizedHeader != nil { bc.finalizedHeaderFeed.Send(FinalizedHeaderEvent{finalizedHeader}) } } diff --git a/core/headerchain.go b/core/headerchain.go index 9bccbfcbe8..30d40d0557 100644 --- a/core/headerchain.go +++ b/core/headerchain.go @@ -133,7 +133,7 @@ func (hc *HeaderChain) getJustifiedNumber(header *types.Header) uint64 { // getFinalizedNumber returns the highest finalized number before the specific block. func (hc *HeaderChain) getFinalizedNumber(header *types.Header) uint64 { if p, ok := hc.engine.(consensus.PoSA); ok { - if finalizedHeader := p.GetFinalizedHeader(hc, header); finalizedHeader != nil { + if finalizedHeader := p.GetFinalizedHeader(hc, header, types.NaturallyFinalizedDist); finalizedHeader != nil { return finalizedHeader.Number.Uint64() } } @@ -141,6 +141,27 @@ func (hc *HeaderChain) getFinalizedNumber(header *types.Header) uint64 { return 0 } +// isFinalizedBlockHigher returns true when the new block's finalized block is higher than current block. +func (hc *HeaderChain) isFinalizedBlockHigher(header *types.Header, curHeader *types.Header) bool { + p, ok := hc.engine.(consensus.PoSA) + if !ok { + return false + } + + ancestor := rawdb.FindCommonAncestor(hc.chainDb, header, curHeader) + if ancestor == nil { + return false + } + + finalized := p.GetFinalizedHeader(hc, header, header.Number.Uint64()-ancestor.Number.Uint64()) + curFinalized := p.GetFinalizedHeader(hc, curHeader, curHeader.Number.Uint64()-ancestor.Number.Uint64()) + if finalized == nil || curFinalized == nil { + return false + } + + return finalized.Number.Uint64() > curFinalized.Number.Uint64() +} + // GetBlockNumber retrieves the block number belonging to the given hash // from the cache or database func (hc *HeaderChain) GetBlockNumber(hash common.Hash) *uint64 { @@ -241,7 +262,7 @@ func (hc *HeaderChain) writeHeaders(headers []*types.Header) (result *headerWrit // Second clause in the if statement reduces the vulnerability to selfish mining. // Please refer to http://www.cs.cornell.edu/~ie53/publications/btcProcFC.pdf reorg := newTD.Cmp(localTD) > 0 - if hc.getFinalizedNumber(lastHeader) > hc.getFinalizedNumber(hc.CurrentHeader()) { + if hc.isFinalizedBlockHigher(lastHeader, hc.CurrentHeader()) { reorg = true } if !reorg && newTD.Cmp(localTD) == 0 { diff --git a/core/types/vote.go b/core/types/vote.go index 6f5f6227bd..1fbc094b7f 100644 --- a/core/types/vote.go +++ b/core/types/vote.go @@ -14,13 +14,14 @@ const ( BLSSignatureLength = 96 MaxAttestationExtraLength = 256 + NaturallyFinalizedDist = 21 // The distance to naturally finalized a block ) type BLSPublicKey [BLSPublicKeyLength]byte type BLSSignature [BLSSignatureLength]byte type ValidatorsBitSet uint64 -// VoteData represents the vote data that validator voted for fast finality. +// VoteData represents the vote range that validator voted for fast finality. type VoteData struct { SourceNumber uint64 // The source block number should be the latest justified block number. SourceHash common.Hash // The block hash of the source block. diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index e839508c55..25b024fae6 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -1328,7 +1328,7 @@ func (s *PublicBlockChainAPI) GetFinalizedHeader(ctx context.Context, blockNrOrH } if posa, ok := s.b.Engine().(consensus.PoSA); ok { - return posa.GetFinalizedHeader(s.b.Chain(), header), nil + return posa.GetFinalizedHeader(s.b.Chain(), header, types.NaturallyFinalizedDist), nil } // Not support. diff --git a/light/lightchain.go b/light/lightchain.go index 5a6b1c07a5..f2ce94b92f 100644 --- a/light/lightchain.go +++ b/light/lightchain.go @@ -419,7 +419,7 @@ func (lc *LightChain) InsertHeaderChain(chain []*types.Header, checkFreq int) (i case core.CanonStatTy: events = append(events, core.ChainEvent{Block: block, Hash: block.Hash()}) if posa, ok := lc.Engine().(consensus.PoSA); ok { - events = append(events, core.FinalizedHeaderEvent{Header: posa.GetFinalizedHeader(lc, block.Header())}) + events = append(events, core.FinalizedHeaderEvent{Header: posa.GetFinalizedHeader(lc, block.Header(), types.NaturallyFinalizedDist)}) } case core.SideStatTy: events = append(events, core.ChainSideEvent{Block: block})