Skip to content

Commit

Permalink
eth: Using Tuple TD and HLCR in peer/handler/downloader
Browse files Browse the repository at this point in the history
  • Loading branch information
shreekarashastry authored and 0xalank committed Jun 30, 2022
1 parent 9d93372 commit d93ef48
Show file tree
Hide file tree
Showing 6 changed files with 66 additions and 22 deletions.
15 changes: 9 additions & 6 deletions eth/downloader/downloader.go
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,9 @@ type BlockChain interface {

// Adds ExternalBlock to external block cache
AddExternalBlock(block *types.ExternalBlock) error

// HLCR does hierarchical comparison of two difficulty tuples and returns true if second tuple is greater than the first
HLCR(localDifficulties []*big.Int, externDifficulties []*big.Int) bool
}

// New creates a new downloader to fetch hashes and blocks from remote peers.
Expand Down Expand Up @@ -331,7 +334,7 @@ func (d *Downloader) UnregisterPeer(id string) error {

// Synchronise tries to sync up our local block chain with a remote peer, both
// adding various sanity checks as well as wrapping it with various log entries.
func (d *Downloader) Synchronise(id string, head common.Hash, td *big.Int, mode SyncMode) error {
func (d *Downloader) Synchronise(id string, head common.Hash, td []*big.Int, mode SyncMode) error {
err := d.synchronise(id, head, td, mode)

switch err {
Expand All @@ -358,7 +361,7 @@ func (d *Downloader) Synchronise(id string, head common.Hash, td *big.Int, mode
// synchronise will select the peer and use it for synchronising. If an empty string is given
// it will use the best peer possible and synchronize if its TD is higher than our own. If any of the
// checks fail an error will be returned. This method is synchronous
func (d *Downloader) synchronise(id string, hash common.Hash, td *big.Int, mode SyncMode) error {
func (d *Downloader) synchronise(id string, hash common.Hash, td []*big.Int, mode SyncMode) error {
// Mock out the synchronisation if testing
if d.synchroniseMock != nil {
return d.synchroniseMock(id, hash)
Expand Down Expand Up @@ -446,7 +449,7 @@ func (d *Downloader) getMode() SyncMode {

// syncWithPeer starts a block synchronization based on the hash chain from the
// specified peer and head hash.
func (d *Downloader) syncWithPeer(p *peerConnection, hash common.Hash, td *big.Int) (err error) {
func (d *Downloader) syncWithPeer(p *peerConnection, hash common.Hash, td []*big.Int) (err error) {
d.mux.Post(StartEvent{})
defer func() {
// reset on error
Expand Down Expand Up @@ -1552,7 +1555,7 @@ func (d *Downloader) fetchParts(deliveryCh chan dataPack, deliver func(dataPack)
// processHeaders takes batches of retrieved headers from an input channel and
// keeps processing and scheduling them into the header chain and downloader's
// queue until the stream ends or a failure occurs.
func (d *Downloader) processHeaders(origin uint64, td *big.Int) error {
func (d *Downloader) processHeaders(origin uint64, td []*big.Int) error {
// Keep a count of uncertain headers to roll back
var (
rollback uint64 // Zero means no rollback (fine as you can't unroll the genesis)
Expand Down Expand Up @@ -1614,7 +1617,7 @@ func (d *Downloader) processHeaders(origin uint64, td *big.Int) error {
// R: Nothing to give
if mode != LightSync {
head := d.blockchain.CurrentBlock()
if !gotHeaders && td.Cmp(d.blockchain.GetTd(head.Hash(), head.NumberU64())[types.QuaiNetworkContext]) > 0 {
if !gotHeaders && d.blockchain.HLCR(d.blockchain.GetTd(head.Hash(), head.NumberU64()), td) {
return errStallingPeer
}
}
Expand All @@ -1627,7 +1630,7 @@ func (d *Downloader) processHeaders(origin uint64, td *big.Int) error {
// peer gave us something useful, we're already happy/progressed (above check).
if mode == FastSync || mode == LightSync {
head := d.lightchain.CurrentHeader()
if td.Cmp(d.lightchain.GetTd(head.Hash(), head.Number[types.QuaiNetworkContext].Uint64())[types.QuaiNetworkContext]) > 0 {
if d.blockchain.HLCR(d.lightchain.GetTd(head.Hash(), head.Number[types.QuaiNetworkContext].Uint64()), td) {
return errStallingPeer
}
}
Expand Down
4 changes: 2 additions & 2 deletions eth/downloader/peer.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ type peerConnection struct {

// LightPeer encapsulates the methods required to synchronise with a remote light peer.
type LightPeer interface {
Head() (common.Hash, *big.Int)
Head() (common.Hash, []*big.Int)
RequestHeadersByHash(common.Hash, int, int, bool) error
RequestHeadersByNumber(uint64, int, int, bool) error
}
Expand All @@ -91,7 +91,7 @@ type lightPeerWrapper struct {
peer LightPeer
}

func (w *lightPeerWrapper) Head() (common.Hash, *big.Int) { return w.peer.Head() }
func (w *lightPeerWrapper) Head() (common.Hash, []*big.Int) { return w.peer.Head() }
func (w *lightPeerWrapper) RequestHeadersByHash(h common.Hash, amount int, skip int, reverse bool) error {
return w.peer.RequestHeadersByHash(h, amount, skip, reverse)
}
Expand Down
25 changes: 22 additions & 3 deletions eth/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -260,7 +260,7 @@ func (h *handler) runEthPeer(peer *eth.Peer, handler eth.Handler) error {
td = h.chain.GetTd(hash, number)
)
forkID := forkid.NewID(h.chain.Config(), h.chain.Genesis().Hash(), h.chain.CurrentHeader().Number[types.QuaiNetworkContext].Uint64())
if err := peer.Handshake(h.networkID, td[types.QuaiNetworkContext], hash, genesis.Hash(), forkID, h.forkFilter); err != nil {
if err := peer.Handshake(h.networkID, td, hash, genesis.Hash(), forkID, h.forkFilter); err != nil {
peer.Log().Debug("Ethereum handshake failed", "err", err)
return err
}
Expand Down Expand Up @@ -441,9 +441,28 @@ func (h *handler) BroadcastBlock(block *types.Block, extBlocks []*types.External
// If propagation is requested, send to a subset of the peer
if propagate {
// Calculate the TD of the block (it's not imported yet, so block.Td is not valid)
var td *big.Int
var td []*big.Int
if parent := h.chain.GetBlock(block.ParentHash(), block.NumberU64()-1); parent != nil {
td = new(big.Int).Add(block.Difficulty(), h.chain.GetTd(block.ParentHash(), block.NumberU64()-1)[types.QuaiNetworkContext])
order, err := h.chain.Engine().GetDifficultyOrder(block.Header())
if err != nil {
log.Error("Error calculating block order in BroadcastBlock, err: ", err)
return
}
var tempTD = big.NewInt(0)
parentPrimeTd := h.chain.GetTd(block.Header().ParentHash[types.QuaiNetworkContext], block.Header().Number[types.QuaiNetworkContext].Uint64())[0]
parentRegionTd := h.chain.GetTd(block.Header().ParentHash[types.QuaiNetworkContext], block.Header().Number[types.QuaiNetworkContext].Uint64())[1]
parentZoneTd := h.chain.GetTd(block.Header().ParentHash[types.QuaiNetworkContext], block.Header().Number[types.QuaiNetworkContext].Uint64())[2]
switch order {
case params.PRIME:
tempTD = new(big.Int).Add(block.Header().Difficulty[0], parentPrimeTd)
td = []*big.Int{tempTD, tempTD, tempTD}
case params.REGION:
tempTD = new(big.Int).Add(block.Header().Difficulty[1], parentRegionTd)
td = []*big.Int{parentPrimeTd, tempTD, tempTD}
case params.ZONE:
tempTD = new(big.Int).Add(block.Header().Difficulty[2], parentZoneTd)
td = []*big.Int{parentPrimeTd, parentRegionTd, tempTD}
}
} else {
log.Error("Propagating dangling block", "number", block.Number(), "hash", hash)
return
Expand Down
6 changes: 3 additions & 3 deletions eth/peer.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,9 @@ import (
// ethPeerInfo represents a short summary of the `eth` sub-protocol metadata known
// about a connected peer.
type ethPeerInfo struct {
Version uint `json:"version"` // Ethereum protocol version negotiated
Difficulty *big.Int `json:"difficulty"` // Total difficulty of the peer's blockchain
Head string `json:"head"` // Hex hash of the peer's best owned block
Version uint `json:"version"` // Ethereum protocol version negotiated
Difficulty []*big.Int `json:"difficulty"` // Total difficulty of the peer's blockchain
Head string `json:"head"` // Hex hash of the peer's best owned block
}

// ethPeer is a wrapper around eth.Peer to maintain a few extra metadata.
Expand Down
26 changes: 24 additions & 2 deletions eth/peerset.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import (
"github.com/spruce-solutions/go-quai/common"
"github.com/spruce-solutions/go-quai/eth/protocols/eth"
"github.com/spruce-solutions/go-quai/eth/protocols/snap"
"github.com/spruce-solutions/go-quai/log"
"github.com/spruce-solutions/go-quai/p2p"
)

Expand Down Expand Up @@ -237,10 +238,10 @@ func (ps *peerSet) peerWithHighestTD() *eth.Peer {

var (
bestPeer *eth.Peer
bestTd *big.Int
bestTd []*big.Int
)
for _, p := range ps.peers {
if _, td := p.Head(); bestPeer == nil || td.Cmp(bestTd) > 0 {
if _, td := p.Head(); bestPeer == nil || HLCR(bestTd, td) {
bestPeer, bestTd = p.Peer, td
}
}
Expand All @@ -257,3 +258,24 @@ func (ps *peerSet) close() {
}
ps.closed = true
}

// HLCR does hierarchical comparison of two difficulty tuples and returns true if second tuple is greater than the first
func HLCR(localDifficulties []*big.Int, externDifficulties []*big.Int) bool {
log.Info("HLCR", "localDiff", localDifficulties, "externDiff", externDifficulties)
if localDifficulties[0].Cmp(externDifficulties[0]) < 0 {
return true
} else if localDifficulties[0].Cmp(externDifficulties[0]) > 0 {
return false
}
if localDifficulties[1].Cmp(externDifficulties[1]) < 0 {
return true
} else if localDifficulties[1].Cmp(externDifficulties[1]) > 0 {
return false
}
if localDifficulties[2].Cmp(externDifficulties[2]) < 0 {
return true
} else if localDifficulties[2].Cmp(externDifficulties[2]) > 0 {
return false
}
return false
}
12 changes: 6 additions & 6 deletions eth/sync.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ type chainSyncer struct {
type chainSyncOp struct {
mode downloader.SyncMode
peer *eth.Peer
td *big.Int
td []*big.Int
head common.Hash
}

Expand Down Expand Up @@ -167,7 +167,7 @@ func (cs *chainSyncer) nextSyncOp() *chainSyncOp {
mode = downloader.SnapSync
}
op := peerToSyncOp(mode, peer)
if op.td.Cmp(ourTD) <= 0 {
if cs.handler.chain.HLCR(op.td, ourTD) {
return nil // We're in sync.
}
return op
Expand All @@ -178,26 +178,26 @@ func peerToSyncOp(mode downloader.SyncMode, p *eth.Peer) *chainSyncOp {
return &chainSyncOp{mode: mode, peer: p, td: peerTD, head: peerHead}
}

func (cs *chainSyncer) modeAndLocalHead() (downloader.SyncMode, *big.Int) {
func (cs *chainSyncer) modeAndLocalHead() (downloader.SyncMode, []*big.Int) {
// If we're in fast sync mode, return that directly
if atomic.LoadUint32(&cs.handler.fastSync) == 1 {
block := cs.handler.chain.CurrentFastBlock()
td := cs.handler.chain.GetTdByHash(block.Hash())
return downloader.FastSync, td[types.QuaiNetworkContext]
return downloader.FastSync, td
}
// We are probably in full sync, but we might have rewound to before the
// fast sync pivot, check if we should reenable
if pivot := rawdb.ReadLastPivotNumber(cs.handler.database); pivot != nil {
if head := cs.handler.chain.CurrentBlock(); head.NumberU64() < *pivot {
block := cs.handler.chain.CurrentFastBlock()
td := cs.handler.chain.GetTdByHash(block.Hash())
return downloader.FastSync, td[types.QuaiNetworkContext]
return downloader.FastSync, td
}
}
// Nope, we're really full syncing
head := cs.handler.chain.CurrentBlock()
td := cs.handler.chain.GetTd(head.Hash(), head.NumberU64())
return downloader.FullSync, td[types.QuaiNetworkContext]
return downloader.FullSync, td
}

// startSync launches doSync in a new goroutine.
Expand Down

0 comments on commit d93ef48

Please sign in to comment.