Skip to content

Commit

Permalink
Merge pull request #419 from libotony/vip193-dev
Browse files Browse the repository at this point in the history
node: new function for canonical chain selection
  • Loading branch information
qianbin committed Dec 3, 2020
2 parents befef12 + b3c02aa commit fa01d45
Show file tree
Hide file tree
Showing 4 changed files with 263 additions and 102 deletions.
19 changes: 0 additions & 19 deletions block/header.go
Expand Up @@ -6,7 +6,6 @@
package block

import (
"bytes"
"encoding/binary"
"fmt"
"io"
Expand Down Expand Up @@ -248,24 +247,6 @@ func (h *Header) String() string {
h.body.TxsRootFeatures.Root, h.body.TxsRootFeatures.Features, h.body.StateRoot, h.body.ReceiptsRoot, h.body.Extension.BackerSignaturesRoot, h.body.Extension.TotalQuality, h.body.Signature)
}

// BetterThan return if this block is better than other one.
func (h *Header) BetterThan(other *Header) bool {
s1 := h.TotalScore()
s2 := other.TotalScore()

if s1 > s2 {
return true
}
if s1 < s2 {
return false
}
// total scores are equal

// smaller ID is preferred, since block with smaller ID usually has larger average score.
// also, it's a deterministic decision.
return bytes.Compare(h.ID().Bytes(), other.ID().Bytes()) < 0
}

// Number extract block number from block id.
func Number(blockID thor.Bytes32) uint32 {
// first 4 bytes are over written by block number (big endian).
Expand Down
57 changes: 0 additions & 57 deletions block/header_test.go

This file was deleted.

153 changes: 127 additions & 26 deletions cmd/thor/node/node.go
Expand Up @@ -6,6 +6,7 @@
package node

import (
"bytes"
"context"
"fmt"
"sort"
Expand Down Expand Up @@ -254,7 +255,6 @@ func (n *Node) txStashLoop(ctx context.Context) {
}

func (n *Node) processBlock(blk *block.Block, stats *blockStats) (bool, error) {

// consensus object is not thread-safe
n.consLock.Lock()
startTime := mclock.Now()
Expand Down Expand Up @@ -298,53 +298,154 @@ func (n *Node) commitBlock(stage *state.Stage, newBlock *block.Block, receipts t
n.commitLock.Lock()
defer n.commitLock.Unlock()

var (
prevBest = n.repo.BestBlock()
becomeNewBest = newBlock.Header().BetterThan(prevBest.Header())
awaitLog = func() {}
)
defer awaitLog()

if becomeNewBest && !n.skipLogs && !n.logDBFailed {
done := make(chan struct{})
awaitLog = func() { <-done }
if _, err := stage.Commit(); err != nil {
return nil, nil, errors.Wrap(err, "commit state")
}
if err := n.repo.AddBlock(newBlock, receipts); err != nil {
return nil, nil, errors.Wrap(err, "add block")
}

go func() {
defer close(done)
var prevBest = n.repo.BestBlock()
becomeNewBest, err := n.compare(newBlock.Header(), prevBest.Header())
if err != nil {
return nil, nil, errors.Wrap(err, "compare head")
}

if becomeNewBest {
if !n.skipLogs && !n.logDBFailed {
diff, err := n.repo.NewChain(newBlock.Header().ParentID()).Exclude(
n.repo.NewChain(prevBest.Header().ID()))
if err != nil {
n.logDBFailed = true
log.Warn("failed to write logs", "err", err)
return
}

if err := n.writeLogs(diff, newBlock, receipts); err != nil {
n.logDBFailed = true
log.Warn("failed to write logs", "err", err)
return
}
}()
}
if err := n.repo.SetBestBlockID(newBlock.Header().ID()); err != nil {
return nil, nil, err
}
}

if _, err := stage.Commit(); err != nil {
return nil, nil, errors.Wrap(err, "commit state")
return n.repo.NewChain(prevBest.Header().ID()), n.repo.NewBestChain(), nil
}

// build forks comparing two heads.
func (n *Node) buildFork(b1 *block.Header, b2 *block.Header) (ancestor *block.Header, br1 []thor.Bytes32, br2 []thor.Bytes32, err error) {
c1 := n.repo.NewChain(b1.ID())
c2 := n.repo.NewChain(b2.ID())

br1, err = c1.Exclude(c2)
if err != nil {
return
}
br2, err = c2.Exclude(c1)
if err != nil {
return
}

if err := n.repo.AddBlock(newBlock, receipts); err != nil {
return nil, nil, errors.Wrap(err, "add block")
var ancestorNumber uint32
if len(br1) > 0 {
ancestorNumber = block.Number(br1[0]) - 1
} else {
ancestorNumber = block.Number(br2[0]) - 1
}

if becomeNewBest {
// to wait for log written
awaitLog()
if err := n.repo.SetBestBlockID(newBlock.Header().ID()); err != nil {
return nil, nil, err
ancestor, err = c1.GetBlockHeader(ancestorNumber)
if err != nil {
return
}

return
}

// giving the list of blockID in ascending order, find the latest heavy block.
func (n *Node) findLatestHeavyBlock(ids []thor.Bytes32) (*block.Header, error) {
for i := len(ids) - 1; i >= 0; i-- {
sum, err := n.repo.GetBlockSummary(ids[i])
if err != nil {
return nil, err
}

parent, err := n.repo.GetBlockSummary(sum.Header.ParentID())
if err != nil {
return nil, err
}

if sum.Header.TotalQuality() > parent.Header.TotalQuality() {
return sum.Header, nil
}
}
return nil, nil
}

return n.repo.NewChain(prevBest.Header().ID()), n.repo.NewBestChain(), nil
// compare compares two chains, returns true if a>b.
func (n *Node) compare(b1 *block.Header, b2 *block.Header) (bool, error) {
q1 := b1.TotalQuality()
q2 := b2.TotalQuality()

if q1 > q2 {
return true, nil
}
if q1 < q2 {
return false, nil
}

// total quality are equal, find the latest heavy block on both branches
// later heavy block is better

if q1 > 0 {
// Non-zero quality means blocks are at post VIP-193 stage
ancestor, br1, br2, err := n.buildFork(b1, b2)
if err != nil {
return false, errors.Wrap(err, "build fork")
}
if len(br2) == 0 {
// c1 includes c2
return true, nil
}
if len(br1) == 0 {
// c2 includes c1
return false, nil
}

if q1 > ancestor.TotalQuality() {
h1, err := n.findLatestHeavyBlock(br1)
if err != nil {
return false, err
}

h2, err := n.findLatestHeavyBlock(br2)
if err != nil {
return false, err
}

if h1.Timestamp() > h2.Timestamp() {
return true, nil
}

if h1.Timestamp() < h2.Timestamp() {
return false, nil
}
}
}

s1 := b1.TotalScore()
s2 := b2.TotalScore()
if s1 > s2 {
return true, nil
}
if s2 < s1 {
return false, nil
}
// total scores are equal

// smaller ID is preferred, since block with smaller ID usually has larger average score.
// also, it's a deterministic decision.
return bytes.Compare(b1.ID().Bytes(), b2.ID().Bytes()) < 0, nil
}

func (n *Node) writeLogs(diff []thor.Bytes32, newBlock *block.Block, newReceipts tx.Receipts) error {
Expand Down

0 comments on commit fa01d45

Please sign in to comment.