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)
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
}
}
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
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
}
}
// 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
}
// 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
}
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
c.mu.RLock()
defer c.mu.RUnlock()
- root := c.tree.checkpoint
+ root := c.tree.Checkpoint
return root.Height, root.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
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) {
{
desc: "only root node",
tree: &treeNode{
- checkpoint: &state.Checkpoint{
+ Checkpoint: &state.Checkpoint{
Height: 0,
Hash: testutil.MustDecodeHash("f5d687e6a5b60fb533c4296e55260803c54e70ad4898682541da15e894971769"),
Status: state.Justified,
{
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,
{
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,
},
},
{
- 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,
}
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())
}
}
}
)
type treeNode struct {
- checkpoint *state.Checkpoint
- children []*treeNode
+ *state.Checkpoint
+ children []*treeNode
}
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)
}
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
}
func (t *treeNode) findOnlyOne(predicate func(*state.Checkpoint) bool) *treeNode {
- if predicate(t.checkpoint) {
+ if predicate(t.Checkpoint) {
return t
}