Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add reorg method and update HLCR to use termini #2

Open
wants to merge 4 commits into
base: bottom-up
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions consensus/misc/gaslimit.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ package misc

import (
"errors"
"fmt"

"github.com/dominant-strategies/go-quai/log"
"github.com/dominant-strategies/go-quai/params"
)

Expand All @@ -33,7 +33,8 @@ func VerifyGaslimit(parentGasLimit, headerGasLimit uint64) error {
}
limit := parentGasLimit / params.GasLimitBoundDivisor
if uint64(diff) >= limit {
return fmt.Errorf("invalid gas limit: have %d, want %d +-= %d", headerGasLimit, parentGasLimit, limit-1)
log.Warn("invalid gas limit:", "have", headerGasLimit, "want", parentGasLimit, "+-=", limit-1)
return nil
}
if headerGasLimit < params.MinGasLimit {
return errors.New("invalid gas limit below 5000")
Expand Down
171 changes: 98 additions & 73 deletions core/slice.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,7 @@ const (
maxPendingEtxBlocks = 256
pendingHeaderCacheLimit = 500
pendingHeaderGCTime = 5

// Termini Index reference to the index of Termini struct that has the
// previous coincident block hash
terminiIndex = 3
TerminusIndex = 3
)

type Slice struct {
Expand Down Expand Up @@ -113,11 +110,13 @@ func NewSlice(db ethdb.Database, config *Config, txConfig *TxPoolConfig, isLocal

// 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, td *big.Int, domOrigin bool, reorg bool, newInboundEtxs types.Transactions) ([]types.Transactions, error) {
func (sl *Slice) Append(header *types.Header, domPendingHeader *types.Header, domTerminus common.Hash, domTd *big.Int, domOrigin bool, reorg bool, newInboundEtxs types.Transactions) ([]types.Transactions, error) {
// The compute and write of the phCache is split starting here so we need to get the lock
sl.phCachemu.Lock()
defer sl.phCachemu.Unlock()

log.Info("Starting slice append", "hash", header.Hash(), "number", header.NumberArray(), "location", header.Location(), "parent hash", header.ParentHash())

nodeCtx := common.NodeLocation.Context()
location := header.Location()
isDomCoincident := sl.engine.IsDomCoincident(header)
Expand Down Expand Up @@ -146,8 +145,6 @@ func (sl *Slice) Append(header *types.Header, domPendingHeader *types.Header, do
return nil, err
}

log.Info("Starting slice append", "hash", block.Hash(), "number", block.Header().NumberArray(), "location", block.Header().Location(), "parent hash", block.ParentHash())

batch := sl.sliceDb.NewBatch()

// Run Previous Coincident Reference Check (PCRC)
Expand Down Expand Up @@ -179,16 +176,26 @@ func (sl *Slice) Append(header *types.Header, domPendingHeader *types.Header, do
return nil, err
}

// CalcTd on the new block
td, err := sl.calcTd(block.Header(), domTd)
if err != nil {
return nil, err
}
fmt.Println("TD: ", td, "domTd: ", domTd, "domOrigin: ", domOrigin)
if !domOrigin {
// CalcTd on the new block
td, err = sl.calcTd(block.Header())
if err != nil {
return nil, err
}
// HLCR
reorg = sl.hlcr(td)
currTermini := sl.hc.GetTerminiByHash(sl.hc.CurrentHeader().Hash())
fmt.Println("New Termini", newTermini)
fmt.Println("Curr Termini", currTermini)
if(nodeCtx != common.PRIME_CTX && currTermini[TerminusIndex] != newTermini[TerminusIndex]) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is already accomplished recursively by passing reorg from the dom.

reorg = sl.hlcr(currTermini[TerminusIndex], newTermini[TerminusIndex])
} else {
reorg = sl.reorg(td)
}
}

fmt.Println("Reorg: ", reorg)

// Upate the local pending header
localPendingHeader, err := sl.miner.worker.GeneratePendingHeader(block)
if err != nil {
Expand Down Expand Up @@ -253,11 +260,11 @@ func (sl *Slice) Append(header *types.Header, domPendingHeader *types.Header, do
return nil, err
}

sl.writeToPhCache(pendingHeaderWithTermini)
updateMiner := sl.pickPhCacheHead(reorg, pendingHeaderWithTermini, domOrigin)
sl.writeToPhCache(pendingHeaderWithTermini, true, TerminusIndex)
sl.pickPhCacheHead(reorg, pendingHeaderWithTermini, TerminusIndex)

// Relay the new pendingHeader
sl.relayPh(pendingHeaderWithTermini, updateMiner, reorg, domOrigin, block.Location())
sl.relayPh(pendingHeaderWithTermini, reorg, domOrigin, block.Location())

log.Info("Appended new block", "number", block.Header().Number(), "hash", block.Hash(),
"uncles", len(block.Uncles()), "txs", len(block.Transactions()), "etxs", len(block.ExtTransactions()), "gas", block.GasUsed(),
Expand All @@ -279,13 +286,14 @@ func (sl *Slice) backfillPETXs(header *types.Header, subManifest types.BlockMani
}

// relayPh sends pendingHeaderWithTermini to subordinates
func (sl *Slice) relayPh(pendingHeaderWithTermini types.PendingHeader, updateMiner bool, reorg bool, domOrigin bool, location common.Location) {
func (sl *Slice) relayPh(pendingHeaderWithTermini types.PendingHeader, reorg bool, domOrigin bool, location common.Location) {
nodeCtx := common.NodeLocation.Context()

if nodeCtx == common.ZONE_CTX {
if updateMiner {
sl.phCache[sl.pendingHeaderHeadHash].Header.SetLocation(common.NodeLocation)
sl.miner.worker.pendingHeaderFeed.Send(sl.phCache[sl.pendingHeaderHeadHash].Header)
bestPh, exists := sl.phCache[sl.pendingHeaderHeadHash]
if exists {
bestPh.Header.SetLocation(common.NodeLocation)
sl.miner.worker.pendingHeaderFeed.Send(bestPh.Header)
return
}
} else if !domOrigin {
Expand Down Expand Up @@ -435,14 +443,14 @@ func (sl *Slice) pcrc(batch ethdb.Batch, header *types.Header, domTerminus commo

// Set the terminus
if nodeCtx == common.PRIME_CTX || isDomCoincident {
newTermini[terminiIndex] = header.Hash()
newTermini[TerminusIndex] = header.Hash()
} else {
newTermini[terminiIndex] = termini[terminiIndex]
newTermini[TerminusIndex] = termini[TerminusIndex]
}

// Check for a graph cyclic reference
if isDomCoincident {
if termini[terminiIndex] != domTerminus {
if termini[TerminusIndex] != domTerminus {
log.Warn("Cyclic Block:", "block number", header.NumberArray(), "hash", header.Hash(), "terminus", domTerminus, "termini", termini)
return common.Hash{}, []common.Hash{}, errors.New("termini do not match, block rejected due to cyclic reference")
}
Expand All @@ -458,28 +466,43 @@ func (sl *Slice) pcrc(batch ethdb.Batch, header *types.Header, domTerminus commo
return termini[location.SubIndex()], newTermini, nil
}

// HLCR Hierarchical Longest Chain Rule compares externTd to the currentHead Td and returns true if externTd is greater
func (sl *Slice) hlcr(externTd *big.Int) bool {
// Reorg externTd to the currentHead Td and returns true if externTd is greater
func (sl *Slice) reorg(externTd *big.Int) bool {
currentTd := sl.hc.GetTdByHash(sl.hc.CurrentHeader().Hash())
log.Debug("HLCR:", "Header hash:", sl.hc.CurrentHeader().Hash(), "currentTd:", currentTd, "externTd:", externTd)
log.Info("Local reorg:", "Header hash:", sl.hc.CurrentHeader().Hash(), "currentTd:", currentTd, "externTd:", externTd)
reorg := currentTd.Cmp(externTd) < 0
//TODO need to handle the equal td case
// https://github.com/dominant-strategies/go-quai/issues/430
return reorg
}

// HLCR Hierarchical Longest Chain Rule compares externTd to the currentHead Td and returns true if externTd is greater
func (sl *Slice) hlcr(currTermini common.Hash, newTermini common.Hash) bool {
currentTd := sl.hc.GetTdByHash(currTermini)
externTd := sl.hc.GetTdByHash(newTermini)
log.Info("HLCR:", "Header hash:", sl.hc.CurrentHeader().Hash(), "currentTd:", currentTd, "externTd:", externTd)
reorg := currentTd.Cmp(externTd) < 0
//TODO need to handle the equal td case
// https://github.com/dominant-strategies/go-quai/issues/430
return reorg
}

// CalcTd calculates the TD of the given header using PCRC.
func (sl *Slice) calcTd(header *types.Header) (*big.Int, error) {
// Stop from
isDomCoincident := sl.engine.IsDomCoincident(header)
if isDomCoincident {
return nil, errors.New("td on a dom block cannot be calculated by a sub")
}
func (sl *Slice) calcTd(header *types.Header, domTd *big.Int) (*big.Int, error) {
priorTd := sl.hc.GetTd(header.ParentHash(), header.NumberU64()-1)
if priorTd == nil {
return nil, consensus.ErrFutureBlock
}

Td := priorTd.Add(priorTd, header.Difficulty())

isDomCoincident := sl.engine.IsDomCoincident(header)
if isDomCoincident {
// If its a dom block we don't compute the td, instead just return the
// td given by dom
return domTd, nil
}

return Td, nil
}

Expand Down Expand Up @@ -556,6 +579,7 @@ func (sl *Slice) SubRelayPendingHeader(pendingHeader types.PendingHeader, reorg
}
bestPh, exists := sl.phCache[sl.pendingHeaderHeadHash]
if exists {
bestPh.Header.SetLocation(common.NodeLocation)
sl.miner.worker.pendingHeaderFeed.Send(bestPh.Header)
}
}
Expand All @@ -567,7 +591,7 @@ func (sl *Slice) computePendingHeader(localPendingHeaderWithTermini types.Pendin
nodeCtx := common.NodeLocation.Context()

var cachedPendingHeaderWithTermini types.PendingHeader
hash := localPendingHeaderWithTermini.Termini[terminiIndex]
hash := localPendingHeaderWithTermini.Termini[TerminusIndex]
cachedPendingHeaderWithTermini, exists := sl.phCache[hash]
var newPh *types.Header

Expand All @@ -586,69 +610,70 @@ func (sl *Slice) computePendingHeader(localPendingHeaderWithTermini types.Pendin
// updatePhCacheFromDom combines the recieved pending header with the pending header stored locally at a given terminus for specified context
func (sl *Slice) updatePhCacheFromDom(pendingHeader types.PendingHeader, terminiIndex int, indices []int, reorg bool) error {

var localPendingHeader types.PendingHeader
hash := pendingHeader.Termini[terminiIndex]
localPendingHeader, exists := sl.phCache[hash]

if exists {
combinedPendingHeader := types.CopyHeader(localPendingHeader.Header)
for _, i := range indices {
localPendingHeader.Header = sl.combinePendingHeader(pendingHeader.Header, localPendingHeader.Header, i)
combinedPendingHeader = sl.combinePendingHeader(pendingHeader.Header, combinedPendingHeader, i)
}
localPendingHeader.Header.SetLocation(common.NodeLocation)
sl.phCache[hash] = localPendingHeader
combinedPendingHeader.SetLocation(common.NodeLocation)

sl.writeToPhCache(types.PendingHeader{Header: combinedPendingHeader, Termini: localPendingHeader.Termini}, false, TerminusIndex)
sl.pickPhCacheHead(reorg, types.PendingHeader{Header: combinedPendingHeader, Termini: localPendingHeader.Termini}, TerminusIndex)

if reorg {
sl.pendingHeaderHeadHash = hash
}
return nil
}
log.Warn("no pending header found for", "terminus", hash)
return errors.New("no pending header found in cache")
}

// writePhCache dom writes a given pendingHeaderWithTermini to the cache with the terminus used as the key.
func (sl *Slice) writeToPhCache(pendingHeaderWithTermini types.PendingHeader) {
sl.phCache[pendingHeaderWithTermini.Termini[terminiIndex]] = pendingHeaderWithTermini
}

// pickPhCacheHead determines if the provided pendingHeader should be selected and returns true if selected
func (sl *Slice) pickPhCacheHead(reorg bool, externPendingHeaderWithTermini types.PendingHeader, domOrigin bool) bool {
if reorg {
sl.pendingHeaderHeadHash = externPendingHeaderWithTermini.Termini[terminiIndex]
return true
}

localPendingHeader, exists := sl.phCache[sl.pendingHeaderHeadHash]

if domOrigin {
//calc local cache head reorg
localCacheReorg := true
for i := 0; i < common.NodeLocation.Context(); i++ {
localCacheReorg = (externPendingHeaderWithTermini.Header.NumberArray()[i].Cmp(localPendingHeader.Header.NumberArray()[i]) >= 0) && localCacheReorg
}

if exists && localCacheReorg && (externPendingHeaderWithTermini.Header.NumberU64() > localPendingHeader.Header.NumberU64()) {
return sl.updateCurrentPendingHeader(externPendingHeaderWithTermini)
}
}

return false
}

// updateCurrentPendingHeader compares the externPh parent td to the sl.pendingHeader parent td and sets sl.pendingHeader to the exterPh if the td is greater
func (sl *Slice) updateCurrentPendingHeader(externPendingHeader types.PendingHeader) bool {
func (sl *Slice) updatePendingHeader(externPendingHeader types.PendingHeader, local bool) bool {
externTd := sl.hc.GetTdByHash(externPendingHeader.Header.ParentHash())
currentTd := sl.hc.GetTdByHash(sl.phCache[sl.pendingHeaderHeadHash].Header.ParentHash())
log.Debug("updateCurrentPendingHeader:", "currentParent:", sl.phCache[sl.pendingHeaderHeadHash].Header.ParentHash(), "currentTd:", currentTd, "externParent:", externPendingHeader.Header.ParentHash(), "externTd:", externTd)
if currentTd != nil && externTd != nil {
if currentTd.Cmp(externTd) < 0 {
sl.pendingHeaderHeadHash = externPendingHeader.Termini[terminiIndex]
if local {
if currentTd.Cmp(externTd) < 0 {
return true
}
} else {
if currentTd.Cmp(externTd) <= 0 {
return true
}
}
} else {
log.Warn("updateCurrentPendingHeader:", "currentParent:", sl.phCache[sl.pendingHeaderHeadHash].Header.ParentHash(), "currentTd:", currentTd, "externParent:", externPendingHeader.Header.ParentHash(), "externTd:", externTd)
return false
}
return true
return false
}

// writePhCache dom writes a given pendingHeaderWithTermini to the cache with the terminus used as the key.
func (sl *Slice) writeToPhCache(pendingHeaderWithTermini types.PendingHeader, local bool, terminiIndex int) {
_, exist := sl.phCache[pendingHeaderWithTermini.Termini[terminiIndex]]
if !exist {
sl.phCache[pendingHeaderWithTermini.Termini[terminiIndex]] = pendingHeaderWithTermini
return
}
//Only write iff our context is better than current ie > td
if sl.updatePendingHeader(pendingHeaderWithTermini, local) {
sl.phCache[pendingHeaderWithTermini.Termini[terminiIndex]] = pendingHeaderWithTermini
}
}

// pickPhCacheHead determines if the provided pendingHeader should be selected and returns true if selected
func (sl *Slice) pickPhCacheHead(reorg bool, externPendingHeaderWithTermini types.PendingHeader, terminiIndex int) {
_, exist := sl.phCache[sl.pendingHeaderHeadHash]
if !exist {
sl.pendingHeaderHeadHash = externPendingHeaderWithTermini.Termini[terminiIndex]
return
}
if reorg {
sl.pendingHeaderHeadHash = externPendingHeaderWithTermini.Termini[terminiIndex]
}
}

// init checks if the headerchain is empty and if it's empty appends the Knot
Expand Down