From: Paladz Date: Wed, 7 Jul 2021 02:21:21 +0000 (+0800) Subject: mov some logic to tree folder (#2019) X-Git-Tag: bytom2-prerelease~1^2~60 X-Git-Url: http://git.osdn.net/view?a=commitdiff_plain;h=2839d6a6724ca174619981ddade1e3acfea9c15f;hp=b04ad1c511e6475f425401c12ed29793a85f2475;p=bytom%2Fbytom.git mov some logic to tree folder (#2019) * mov some logic to tree folder * less code * fix bug * edit parameter name * elegant * edit for code review Co-authored-by: paladz --- diff --git a/protocol/casper/apply_block.go b/protocol/casper/apply_block.go index f180699c..419419ba 100644 --- a/protocol/casper/apply_block.go +++ b/protocol/casper/apply_block.go @@ -61,11 +61,11 @@ func (c *Casper) applyBlockToCheckpoint(block *types.Block) (*state.Checkpoint, return nil, err } - checkpoint := node.checkpoint + checkpoint := node.Checkpoint if mod := block.Height % consensus.ActiveNetParams.BlocksOfEpoch; mod == 1 { parent := checkpoint checkpoint = state.NewCheckpoint(parent) - node.addChild(&treeNode{checkpoint: checkpoint}) + node.addChild(&treeNode{Checkpoint: checkpoint}) } return checkpoint, checkpoint.Increase(block) @@ -103,10 +103,10 @@ func (c *Casper) replayCheckpoint(hash bc.Hash) (*treeNode, error) { return nil, err } - node := &treeNode{checkpoint: state.NewCheckpoint(parent.checkpoint)} + node := &treeNode{Checkpoint: state.NewCheckpoint(parent.Checkpoint)} parent.addChild(node) for _, attachBlock := range attachBlocks { - if err := node.checkpoint.Increase(attachBlock); err != nil { + if err := node.Increase(attachBlock); err != nil { return nil, err } } diff --git a/protocol/casper/auth_verification.go b/protocol/casper/auth_verification.go index 8368113e..e69abda6 100644 --- a/protocol/casper/auth_verification.go +++ b/protocol/casper/auth_verification.go @@ -23,7 +23,7 @@ func (c *Casper) AuthVerification(v *Verification) error { defer c.mu.Unlock() // root of tree is the last finalized checkpoint - if v.TargetHeight < c.tree.checkpoint.Height { + if v.TargetHeight < c.tree.Height { // discard the verification message which height of target less than height of last finalized checkpoint // is for simplify check the vote within the span of its other votes return nil @@ -44,12 +44,12 @@ func (c *Casper) AuthVerification(v *Verification) error { return errPubKeyIsNotValidator } - if targetNode.checkpoint.ContainsVerification(validators[v.PubKey].Order, &v.SourceHash) { + if targetNode.ContainsVerification(validators[v.PubKey].Order, &v.SourceHash) { return nil } oldBestHash := c.bestChain() - if err := c.authVerification(v, targetNode.checkpoint, validators); err != nil { + if err := c.authVerification(v, targetNode.Checkpoint, validators); err != nil { return err } @@ -121,8 +121,8 @@ func (c *Casper) setFinalized(checkpoint *state.Checkpoint) { } // update the checkpoint state in memory - newRoot.checkpoint.Status = state.Finalized - newRoot.checkpoint.Parent = nil + newRoot.Status = state.Finalized + newRoot.Parent = nil c.tree = newRoot } diff --git a/protocol/casper/casper.go b/protocol/casper/casper.go index d0b098fc..79ad78d7 100644 --- a/protocol/casper/casper.go +++ b/protocol/casper/casper.go @@ -31,15 +31,17 @@ type RollbackMsg struct { // Casper is BFT based proof of stack consensus algorithm, it provides safety and liveness in theory, // it's design mainly refers to https://github.com/ethereum/research/blob/master/papers/casper-basics/casper_basics.pdf type Casper struct { - mu sync.RWMutex - tree *treeNode - rollbackCh chan *RollbackMsg - newEpochCh chan bc.Hash - store state.Store + mu sync.RWMutex + store state.Store + tree *treeNode + // block hash -> previous checkpoint hash prevCheckpointCache *common.Cache // block hash + pubKey -> verification verificationCache *common.Cache + + rollbackCh chan *RollbackMsg + newEpochCh chan bc.Hash } // NewCasper create a new instance of Casper @@ -52,12 +54,12 @@ func NewCasper(store state.Store, checkpoints []*state.Checkpoint, rollbackCh ch } casper := &Casper{ - tree: makeTree(checkpoints[0], checkpoints[1:]), - rollbackCh: rollbackCh, - newEpochCh: make(chan bc.Hash), store: store, + tree: makeTree(checkpoints[0], checkpoints[1:]), prevCheckpointCache: common.NewCache(1024), verificationCache: common.NewCache(1024), + rollbackCh: rollbackCh, + newEpochCh: make(chan bc.Hash), } go casper.authVerificationLoop() return casper @@ -68,7 +70,7 @@ func (c *Casper) LastFinalized() (uint64, bc.Hash) { c.mu.RLock() defer c.mu.RUnlock() - root := c.tree.checkpoint + root := c.tree.Checkpoint return root.Height, root.Hash } @@ -76,8 +78,8 @@ func (c *Casper) LastFinalized() (uint64, bc.Hash) { func (c *Casper) LastJustified() (uint64, bc.Hash) { c.mu.RLock() defer c.mu.RUnlock() - - return lastJustified(c.tree) + node := c.tree.lastJustified() + return node.Height, node.Hash } // Validators return the validators by specified block hash @@ -111,41 +113,8 @@ func (c *Casper) ParentCheckpointByPrevHash(prevBlockHash *bc.Hash) (*state.Chec func (c *Casper) bestChain() bc.Hash { // root is init justified - root := c.tree.checkpoint - _, bestHash, _ := chainOfMaxJustifiedHeight(c.tree, root.Height) - return bestHash -} - -func lastJustified(node *treeNode) (uint64, bc.Hash) { - lastJustifiedHeight, lastJustifiedHash := uint64(0), bc.Hash{} - if node.checkpoint.Status == state.Justified { - lastJustifiedHeight, lastJustifiedHash = node.checkpoint.Height, node.checkpoint.Hash - } - - for _, child := range node.children { - if justifiedHeight, justifiedHash := lastJustified(child); justifiedHeight > lastJustifiedHeight { - lastJustifiedHeight, lastJustifiedHash = justifiedHeight, justifiedHash - } - } - return lastJustifiedHeight, lastJustifiedHash -} - -// justifiedHeight is the max justified height of checkpoint from node to root -func chainOfMaxJustifiedHeight(node *treeNode, justifiedHeight uint64) (uint64, bc.Hash, uint64) { - checkpoint := node.checkpoint - if checkpoint.Status == state.Justified { - justifiedHeight = checkpoint.Height - } - - bestHeight, bestHash, maxJustifiedHeight := checkpoint.Height, checkpoint.Hash, justifiedHeight - for _, child := range node.children { - height, hash, justified := chainOfMaxJustifiedHeight(child, justifiedHeight) - if justified > maxJustifiedHeight || (justified == maxJustifiedHeight && height > bestHeight) || - (justified == maxJustifiedHeight && height == bestHeight && hash.String() > bestHash.String()) { - bestHeight, bestHash, maxJustifiedHeight = height, hash, justified - } - } - return bestHeight, bestHash, maxJustifiedHeight + bestNode, _ := c.tree.bestNode(c.tree.Height) + return bestNode.Hash } func (c *Casper) parentCheckpointHash(blockHash *bc.Hash) (*bc.Hash, error) { diff --git a/protocol/casper/casper_test.go b/protocol/casper/casper_test.go index 9a22fc59..4e123f45 100644 --- a/protocol/casper/casper_test.go +++ b/protocol/casper/casper_test.go @@ -17,7 +17,7 @@ func TestBestChain(t *testing.T) { { desc: "only root node", tree: &treeNode{ - checkpoint: &state.Checkpoint{ + Checkpoint: &state.Checkpoint{ Height: 0, Hash: testutil.MustDecodeHash("f5d687e6a5b60fb533c4296e55260803c54e70ad4898682541da15e894971769"), Status: state.Justified, @@ -28,28 +28,28 @@ func TestBestChain(t *testing.T) { { desc: "best chain is not the longest chain", tree: &treeNode{ - checkpoint: &state.Checkpoint{ + Checkpoint: &state.Checkpoint{ Height: 0, Hash: testutil.MustDecodeHash("f5d687e6a5b60fb533c4296e55260803c54e70ad4898682541da15e894971769"), Status: state.Finalized, }, children: []*treeNode{ { - checkpoint: &state.Checkpoint{ + Checkpoint: &state.Checkpoint{ Height: 5, Hash: testutil.MustDecodeHash("750408823bea9666f263870daded75d8f3d878606ec103bc9401b73714d77729"), Status: state.Justified, }, }, { - checkpoint: &state.Checkpoint{ + Checkpoint: &state.Checkpoint{ Height: 5, Hash: testutil.MustDecodeHash("049f012c12b94d34b13163eddbd31866f97b38c5b742e4c170a44d80ff503166"), Status: state.Unjustified, }, children: []*treeNode{ { - checkpoint: &state.Checkpoint{ + Checkpoint: &state.Checkpoint{ Height: 8, Hash: testutil.MustDecodeHash("8315f5337978076b3097230570484e36586ee27564ebcbf74c2093cd763e32e7"), Status: state.Growing, @@ -64,21 +64,21 @@ func TestBestChain(t *testing.T) { { desc: "two distinct chain has same justified height, the longest chain is the best chain", tree: &treeNode{ - checkpoint: &state.Checkpoint{ + Checkpoint: &state.Checkpoint{ Height: 0, Hash: testutil.MustDecodeHash("f5d687e6a5b60fb533c4296e55260803c54e70ad4898682541da15e894971769"), Status: state.Justified, }, children: []*treeNode{ { - checkpoint: &state.Checkpoint{ + Checkpoint: &state.Checkpoint{ Height: 5, Hash: testutil.MustDecodeHash("750408823bea9666f263870daded75d8f3d878606ec103bc9401b73714d77729"), Status: state.Unjustified, }, children: []*treeNode{ { - checkpoint: &state.Checkpoint{ + Checkpoint: &state.Checkpoint{ Height: 7, Hash: testutil.MustDecodeHash("0bf26d17ff2a578c1a733a1e969184d695e8f3ac6834150acc5c1e9edeb84de9"), Status: state.Growing, @@ -87,14 +87,14 @@ func TestBestChain(t *testing.T) { }, }, { - checkpoint: &state.Checkpoint{ + Checkpoint: &state.Checkpoint{ Height: 5, Hash: testutil.MustDecodeHash("049f012c12b94d34b13163eddbd31866f97b38c5b742e4c170a44d80ff503166"), Status: state.Unjustified, }, children: []*treeNode{ { - checkpoint: &state.Checkpoint{ + Checkpoint: &state.Checkpoint{ Height: 8, Hash: testutil.MustDecodeHash("8315f5337978076b3097230570484e36586ee27564ebcbf74c2093cd763e32e7"), Status: state.Growing, @@ -109,8 +109,8 @@ func TestBestChain(t *testing.T) { } for i, c := range cases { - if _, bestHash, _ := chainOfMaxJustifiedHeight(c.tree, c.tree.checkpoint.Height); bestHash != c.wantBestHash { - t.Errorf("case #%d(%s) want best hash:%s, got best hash:%s\n", i, c.desc, c.wantBestHash.String(), bestHash.String()) + if bestNode, _ := c.tree.bestNode(0); bestNode.Hash != c.wantBestHash { + t.Errorf("case #%d(%s) want best hash:%s, got best hash:%s\n", i, c.desc, c.wantBestHash.String(), bestNode.Hash.String()) } } } diff --git a/protocol/casper/tree_node.go b/protocol/casper/tree_node.go index 351ed5bc..e00cbd44 100644 --- a/protocol/casper/tree_node.go +++ b/protocol/casper/tree_node.go @@ -9,8 +9,8 @@ import ( ) type treeNode struct { - checkpoint *state.Checkpoint - children []*treeNode + *state.Checkpoint + children []*treeNode } func makeTree(root *state.Checkpoint, successors []*state.Checkpoint) *treeNode { @@ -19,13 +19,13 @@ func makeTree(root *state.Checkpoint, successors []*state.Checkpoint) *treeNode parentToSuccessors[successor.ParentHash] = append(parentToSuccessors[successor.ParentHash], successor) } - rootNode := &treeNode{checkpoint: root} + rootNode := &treeNode{Checkpoint: root} nodes := []*treeNode{rootNode} for len(nodes) != 0 { node := nodes[0] - for _, successor := range parentToSuccessors[node.checkpoint.Hash] { - child := &treeNode{checkpoint: successor} - successor.Parent = node.checkpoint + for _, successor := range parentToSuccessors[node.Hash] { + child := &treeNode{Checkpoint: successor} + successor.Parent = node.Checkpoint node.addChild(child) nodes = append(nodes, child) } @@ -38,6 +38,39 @@ func (t *treeNode) addChild(child *treeNode) { t.children = append(t.children, child) } +func (t *treeNode) bestNode(justifiedHeight uint64) (*treeNode, uint64) { + if t.Status == state.Justified { + justifiedHeight = t.Height + } + + bestNode, bestJustified := t, justifiedHeight + for _, child := range t.children { + bestChild, childJustified := child.bestNode(justifiedHeight) + if childJustified > bestJustified || (childJustified == bestJustified && bestChild.Height > bestNode.Height) || + (childJustified == bestJustified && bestChild.Height == bestNode.Height && bestChild.Hash.String() > bestNode.Hash.String()) { + bestNode, bestJustified = bestChild, childJustified + } + } + + return bestNode, bestJustified +} + +func (t *treeNode) lastJustified() *treeNode { + var selected *treeNode + if t.Status == state.Justified { + selected = t + } + + for _, child := range t.children { + if childSelected := child.lastJustified(); childSelected == nil { + continue + } else if selected == nil || childSelected.Height > selected.Height { + selected = childSelected + } + } + return selected +} + func (t *treeNode) nodeByHash(blockHash bc.Hash) (*treeNode, error) { if c := t.findOnlyOne(func(c *state.Checkpoint) bool { return c.Hash == blockHash @@ -49,7 +82,7 @@ func (t *treeNode) nodeByHash(blockHash bc.Hash) (*treeNode, error) { } func (t *treeNode) findOnlyOne(predicate func(*state.Checkpoint) bool) *treeNode { - if predicate(t.checkpoint) { + if predicate(t.Checkpoint) { return t }