OSDN Git Service

add_casper_process_block (#1868)
authorPoseidon <shenao.78@163.com>
Wed, 7 Apr 2021 13:45:23 +0000 (21:45 +0800)
committerGitHub <noreply@github.com>
Wed, 7 Apr 2021 13:45:23 +0000 (21:45 +0800)
database/store.go
protocol/consensus/casper.go
protocol/consensus/checkpoint.go [deleted file]
protocol/consensus/tree_node.go
protocol/consensus/verfication.go
protocol/state/checkpoint.go [new file with mode: 0644]
protocol/store.go

index ab0bc44..f2e8b39 100644 (file)
@@ -223,3 +223,12 @@ func (s *Store) SaveChainStatus(node *state.BlockNode, view *state.UtxoViewpoint
        batch.Write()
        return nil
 }
+
+func (s *Store) GetCheckpoint(*bc.Hash) (*state.Checkpoint, error) {
+       return nil, nil
+}
+
+// GetCheckpointsByHeight return all checkpoints of specified block height
+func (s *Store) GetCheckpointsByHeight(uint64) ([]*state.Checkpoint, error) {
+       return nil, nil
+}
index e3d952f..98efe82 100644 (file)
@@ -7,11 +7,13 @@ import (
        "github.com/bytom/bytom/protocol"
        "github.com/bytom/bytom/protocol/bc"
        "github.com/bytom/bytom/protocol/bc/types"
+       "github.com/bytom/bytom/protocol/state"
 )
 
 var (
        errVerifySignature          = errors.New("signature of verification message is invalid")
        errPubKeyIsNotValidator     = errors.New("pub key is not in validators of target checkpoint")
+       errVoteToGrowingCheckpoint  = errors.New("validator publish vote to growing checkpoint")
        errSameHeightInVerification = errors.New("validator publish two distinct votes for the same target height")
        errSpanHeightInVerification = errors.New("validator publish vote within the span of its other votes")
 )
@@ -25,19 +27,19 @@ type Casper struct {
 }
 
 // Best chain return the chain containing the justified checkpoint of the largest height
-func (c *Casper) BestChain() (uint64, string) {
+func (c *Casper) BestChain() (uint64, bc.Hash) {
        c.mu.RLock()
        defer c.mu.RUnlock()
 
        // root is init justified
        root := c.tree.checkpoint
-       bestHeight, bestHash, _ := chainOfMaxJustifiedHeight(c.tree, root.height)
+       bestHeight, bestHash, _ := chainOfMaxJustifiedHeight(c.tree, root.Height)
        return bestHeight, bestHash
 }
 
 // Validators return the validators by specified block hash
 // e.g. if the block num of epoch is 100, and the block height corresponding to the block hash is 130, then will return the voting results of height in 0~100
-func (c *Casper) Validators(blockHash *bc.Hash) ([]*Validator, error) {
+func (c *Casper) Validators(blockHash *bc.Hash) ([]*state.Validator, error) {
        hash, err := c.prevCheckpointHash(blockHash)
        if err != nil {
                return nil, err
@@ -46,12 +48,12 @@ func (c *Casper) Validators(blockHash *bc.Hash) ([]*Validator, error) {
        c.mu.RLock()
        defer c.mu.RUnlock()
 
-       checkpoint, err := c.tree.checkpointByHash(hash.String())
+       checkpoint, err := c.store.GetCheckpoint(hash)
        if err != nil {
                return nil, err
        }
 
-       return checkpoint.validators(), nil
+       return checkpoint.Validators(), nil
 }
 
 // AuthVerification verify whether the Verification is legal.
@@ -63,23 +65,25 @@ func (c *Casper) AuthVerification(v *Verification) error {
                return err
        }
 
-       c.mu.Lock()
-       defer c.mu.Unlock()
-
-       source, err := c.tree.checkpointByHash(v.SourceHash)
+       source, err := c.store.GetCheckpoint(&v.SourceHash)
        if err != nil {
-               // the following two cases are not handled
-               // case1: the synchronization block is later than the arrival of the verification message
-               // case2: the tree node was was pruned
+               // the synchronization block is later than the arrival of the verification message
                return err
        }
 
-       target, err := c.tree.checkpointByHash(v.TargetHash)
+       target, err := c.store.GetCheckpoint(&v.TargetHash)
        if err != nil {
                return err
        }
 
-       if !target.containsValidator(v.PubKey) {
+       if source.Status == state.Growing || target.Status == state.Growing {
+               return errVoteToGrowingCheckpoint
+       }
+
+       c.mu.Lock()
+       defer c.mu.Unlock()
+
+       if !target.ContainsValidator(v.PubKey) {
                return errPubKeyIsNotValidator
        }
 
@@ -91,29 +95,77 @@ func (c *Casper) AuthVerification(v *Verification) error {
                return err
        }
 
-       supLink := target.addSupLink(v.SourceHeight, v.SourceHash, v.PubKey)
-       if source.status == justified && supLink.confirmed() {
-               target.status = justified
-               source.status = finalized
+       supLink := target.AddSupLink(v.SourceHeight, v.SourceHash, v.PubKey)
+       if source.Status == state.Justified && supLink.Confirmed() {
+               target.Status = state.Justified
+               source.Status = state.Finalized
                // must notify chain when rollback
-               // pruning the tree
+               if err := c.pruneTree(source.Hash); err != nil {
+                       return err
+               }
        }
        return nil
 }
 
+func (c *Casper) pruneTree(rootHash bc.Hash) error {
+       newRoot, err := c.tree.nodeByHash(rootHash)
+       if err != nil {
+               return err
+       }
+
+       c.tree = newRoot
+       return nil
+}
+
 // ProcessBlock used to receive a new block from upper layer, it provides idempotence
 // and parse the vote and mortgage from the transactions, then save to the checkpoint
 // the tree of checkpoint will grow with the arrival of new blocks
-func (c *Casper) ProcessBlock(block *types.Block) error {
-       return nil
+func (c *Casper) ProcessBlock(block *types.Block) (*state.Checkpoint, error) {
+       c.mu.Lock()
+       defer c.mu.Unlock()
+
+       prevHash := block.PreviousBlockHash
+       node, err := c.tree.nodeByHash(prevHash)
+       if err != nil {
+               return nil, err
+       }
+
+       checkpoint := node.checkpoint
+
+       if mod := block.Height % state.BlocksOfEpoch; mod == 1 {
+               parent := checkpoint
+
+               checkpoint = &state.Checkpoint{
+                       PrevHash:       parent.Hash,
+                       StartTimestamp: block.Timestamp,
+                       Status:         state.Growing,
+                       Votes:          make(map[string]uint64),
+                       Mortgages:      make(map[string]uint64),
+               }
+               node.children = append(node.children, &treeNode{checkpoint: checkpoint})
+       } else if mod == 0 {
+               checkpoint.Status = state.Unverified
+       }
+
+       checkpoint.Height = block.Height
+       checkpoint.Hash = block.Hash()
+
+       for range block.Transactions {
+               // process the votes and mortgages
+       }
+       return checkpoint, nil
 }
 
 // a validator must not publish two distinct votes for the same target height
 func (c *Casper) verifySameHeight(v *Verification) error {
-       nodes := c.tree.checkpointsOfHeight(v.TargetHeight)
-       for _, node := range nodes {
-               for _, supLink := range node.supLinks {
-                       if supLink.pubKeys[v.PubKey] {
+       checkpoints, err := c.store.GetCheckpointsByHeight(v.TargetHeight)
+       if err != nil {
+               return err
+       }
+
+       for _, checkpoint := range checkpoints {
+               for _, supLink := range checkpoint.SupLinks {
+                       if supLink.PubKeys[v.PubKey] {
                                return errSameHeightInVerification
                        }
                }
@@ -123,13 +175,14 @@ func (c *Casper) verifySameHeight(v *Verification) error {
 
 // a validator must not vote within the span of its other votes.
 func (c *Casper) verifySpanHeight(v *Verification) error {
-       if c.tree.findOnlyOne(func(c *checkpoint) bool {
-               if c.height <= v.TargetHeight {
+       // It is best to scan all checkpoints from leveldb
+       if c.tree.findOnlyOne(func(c *state.Checkpoint) bool {
+               if c.Height <= v.TargetHeight {
                        return false
                }
 
-               for _, supLink := range c.supLinks {
-                       if supLink.pubKeys[v.PubKey] && supLink.sourceHeight < v.SourceHeight {
+               for _, supLink := range c.SupLinks {
+                       if supLink.PubKeys[v.PubKey] && supLink.SourceHeight < v.SourceHeight {
                                return true
                        }
                }
@@ -141,13 +194,13 @@ func (c *Casper) verifySpanHeight(v *Verification) error {
 }
 
 // justifiedHeight is the max justified height of checkpoint from node to root
-func chainOfMaxJustifiedHeight(node *treeNode, justifiedHeight uint64) (uint64, string, uint64) {
+func chainOfMaxJustifiedHeight(node *treeNode, justifiedHeight uint64) (uint64, bc.Hash, uint64) {
        checkpoint := node.checkpoint
-       if checkpoint.status == justified || checkpoint.status == finalized {
-               justifiedHeight = checkpoint.height
+       if checkpoint.Status == state.Justified || checkpoint.Status == state.Finalized {
+               justifiedHeight = checkpoint.Height
        }
 
-       bestHeight, bestHash, maxJustifiedHeight := checkpoint.height, checkpoint.hash, justifiedHeight
+       bestHeight, bestHash, maxJustifiedHeight := checkpoint.Height, checkpoint.Hash, justifiedHeight
        for _, child := range node.children {
                if height, hash, justified := chainOfMaxJustifiedHeight(child, justifiedHeight); justified > maxJustifiedHeight {
                        bestHeight, bestHash, maxJustifiedHeight = height, hash, justified
@@ -165,7 +218,7 @@ func (c *Casper) prevCheckpointHash(blockHash *bc.Hash) (*bc.Hash, error) {
 
                height := block.Height - 1
                blockHash = &block.PreviousBlockHash
-               if height%blocksOfEpoch == 0 {
+               if height%state.BlocksOfEpoch == 0 {
                        return blockHash, nil
                }
        }
diff --git a/protocol/consensus/checkpoint.go b/protocol/consensus/checkpoint.go
deleted file mode 100644 (file)
index fc28e0d..0000000
+++ /dev/null
@@ -1,99 +0,0 @@
-package consensus
-
-import (
-       "sort"
-)
-
-const (
-       blocksOfEpoch   = 100
-       minMortgage     = 1000000
-       numOfValidators = 10
-)
-
-type checkpointStatus uint8
-
-const (
-       growing checkpointStatus = iota
-       unverified
-       justified
-       finalized
-)
-
-type supLink struct {
-       sourceHeight uint64
-       sourceHash   string
-       pubKeys      map[string]bool // valid pubKeys of signature
-}
-
-func (s *supLink) confirmed() bool {
-       return len(s.pubKeys) > numOfValidators*2/3
-}
-
-type checkpoint struct {
-       height         uint64
-       hash           string
-       startTimestamp uint64
-       supLinks       []*supLink
-       status         checkpointStatus
-
-       votes     map[string]uint64 // putKey -> num of vote
-       mortgages map[string]uint64 // pubKey -> num of mortgages
-}
-
-func (c *checkpoint) addSupLink(sourceHeight uint64, sourceHash, pubKey string) *supLink {
-       for _, supLink := range c.supLinks {
-               if supLink.sourceHash == sourceHash {
-                       supLink.pubKeys[pubKey] = true
-                       return supLink
-               }
-       }
-
-       supLink := &supLink{
-               sourceHeight: sourceHeight,
-               sourceHash:   sourceHash,
-               pubKeys:      map[string]bool{pubKey: true},
-       }
-       c.supLinks = append(c.supLinks, supLink)
-       return supLink
-}
-
-// Validator represent the participants of the PoS network
-// Responsible for block generation and verification
-type Validator struct {
-       PubKey   string
-       Vote     uint64
-       Mortgage uint64
-}
-
-func (c *checkpoint) validators() []*Validator {
-       var validators []*Validator
-       for pubKey, mortgageNum := range c.mortgages {
-               if mortgageNum >= minMortgage {
-                       validators = append(validators, &Validator{
-                               PubKey:   pubKey,
-                               Vote:     c.votes[pubKey],
-                               Mortgage: mortgageNum,
-                       })
-               }
-       }
-
-       sort.Slice(validators, func(i, j int) bool {
-               return validators[i].Mortgage+validators[i].Vote > validators[j].Mortgage+validators[j].Vote
-       })
-
-       end := numOfValidators
-       if len(validators) < numOfValidators {
-               end = len(validators)
-       }
-       return validators[:end]
-}
-
-
-func (c *checkpoint) containsValidator(pubKey string) bool {
-       for _, v := range c.validators() {
-               if v.PubKey == pubKey {
-                       return true
-               }
-       }
-       return false
-}
index f8378fe..a8fe41c 100644 (file)
@@ -3,47 +3,34 @@ package consensus
 import (
        "errors"
        "fmt"
+
+       "github.com/bytom/bytom/protocol/bc"
+       "github.com/bytom/bytom/protocol/state"
 )
 
 type treeNode struct {
-       checkpoint *checkpoint
+       checkpoint *state.Checkpoint
        children   []*treeNode
 }
 
-func (t *treeNode) checkpointByHash(blockHash string) (*checkpoint, error) {
-       if c := t.findOnlyOne(func(c *checkpoint) bool {
-               return c.hash == blockHash
+func (t *treeNode) nodeByHash(blockHash bc.Hash) (*treeNode, error) {
+       if c := t.findOnlyOne(func(c *state.Checkpoint) bool {
+               return c.Hash == blockHash
        }); c != nil {
                return c, nil
        }
 
-       return nil, errors.New(fmt.Sprintf("fail to find checkpoint of hash:%s", blockHash))
-}
-
-func (t *treeNode) checkpointsOfHeight(blockHeight uint64) []*checkpoint {
-       if blockHeight%blocksOfEpoch != 0 {
-               return nil
-       }
-
-       var result []*checkpoint
-       if t.checkpoint.height == blockHeight {
-               return append(result, t.checkpoint)
-       }
-
-       for _, child := range t.children {
-               result = append(result, child.checkpointsOfHeight(blockHeight)...)
-       }
-       return result
+       return nil, errors.New(fmt.Sprintf("fail to find checkpoint of hash:%s", blockHash.String()))
 }
 
-func (t *treeNode) findOnlyOne(predicate func(*checkpoint) bool) *checkpoint {
+func (t *treeNode) findOnlyOne(predicate func(*state.Checkpoint) bool) *treeNode {
        if predicate(t.checkpoint) {
-               return t.checkpoint
+               return t
        }
 
        for _, child := range t.children {
-               if c := child.findOnlyOne(predicate); c != nil {
-                       return c
+               if node := child.findOnlyOne(predicate); node != nil {
+                       return node
                }
        }
        return nil
index 2a020ca..a845672 100644 (file)
@@ -14,8 +14,8 @@ import (
 // source hash and target hash point to the checkpoint, and the source checkpoint is the target checkpoint's parent(not be directly)
 // the vector <sourceHash, targetHash, sourceHeight, targetHeight, pubKey> as the message of signature
 type Verification struct {
-       SourceHash   string
-       TargetHash   string
+       SourceHash   bc.Hash
+       TargetHash   bc.Hash
        SourceHeight uint64
        TargetHeight uint64
        Signature    string
@@ -25,11 +25,11 @@ type Verification struct {
 // EncodeMessage encode the verification for the validators to sign or verify
 func (v *Verification) EncodeMessage() ([]byte, error) {
        buff := new(bytes.Buffer)
-       if _, err := buff.WriteString(v.SourceHash); err != nil {
+       if _, err := v.SourceHash.WriteTo(buff); err != nil {
                return nil, err
        }
 
-       if _, err := buff.WriteString(v.TargetHash); err != nil {
+       if _, err := v.TargetHash.WriteTo(buff); err != nil {
                return nil, err
        }
 
diff --git a/protocol/state/checkpoint.go b/protocol/state/checkpoint.go
new file mode 100644 (file)
index 0000000..bd0def3
--- /dev/null
@@ -0,0 +1,124 @@
+package state
+
+import (
+       "sort"
+
+       "github.com/bytom/bytom/protocol/bc"
+)
+
+const (
+       // BlocksOfEpoch represent the block num in one epoch
+       BlocksOfEpoch   = 100
+       minMortgage     = 1000000
+       numOfValidators = 10
+)
+
+// CheckpointStatus represent current status of checkpoint
+type CheckpointStatus uint8
+
+const (
+       // Growing means that the checkpoint has not ended the current epoch
+       Growing CheckpointStatus = iota
+
+       // Unverified means thant the checkpoint has ended the current epoch, but not been justified
+       Unverified
+
+       // Justified if checkpoint is the root, or there exists a super link c′ → c where c′ is justified
+       Justified
+
+       // Finalized if checkpoint c is justified and there is a sup link c→c′ where c′is a direct child of c
+       Finalized
+)
+
+// SupLink is an ordered pair of checkpoints (a, b), also written a → b,
+// such that at least 2/3 of validators have published votes with source a and target b.
+type SupLink struct {
+       SourceHeight uint64
+       SourceHash   bc.Hash
+       PubKeys      map[string]bool // valid pubKeys of signature
+}
+
+// Confirmed if at least 2/3 of validators have published votes with sup link
+func (s *SupLink) Confirmed() bool {
+       return len(s.PubKeys) > numOfValidators*2/3
+}
+
+// Checkpoint represent the block/hash under consideration for finality for a given epoch.
+// This block is the last block of the previous epoch. Rather than dealing with every block,
+// Casper only considers checkpoints for finalization. When a checkpoint is explicitly finalized,
+// all ancestor blocks of the checkpoint are implicitly finalized.
+type Checkpoint struct {
+       Height         uint64
+       Hash           bc.Hash
+       PrevHash       bc.Hash
+       StartTimestamp uint64
+       SupLinks       []*SupLink
+       Status         CheckpointStatus
+
+       Votes     map[string]uint64 // putKey -> num of vote
+       Mortgages map[string]uint64 // pubKey -> num of mortgages
+}
+
+// AddSupLink add a valid sup link to checkpoint
+func (c *Checkpoint) AddSupLink(sourceHeight uint64, sourceHash bc.Hash, pubKey string) *SupLink {
+       for _, supLink := range c.SupLinks {
+               if supLink.SourceHash == sourceHash {
+                       supLink.PubKeys[pubKey] = true
+                       return supLink
+               }
+       }
+
+       supLink := &SupLink{
+               SourceHeight: sourceHeight,
+               SourceHash:   sourceHash,
+               PubKeys:      map[string]bool{pubKey: true},
+       }
+       c.SupLinks = append(c.SupLinks, supLink)
+       return supLink
+}
+
+// Validator represent the participants of the PoS network
+// Responsible for block generation and verification
+type Validator struct {
+       PubKey   string
+       Vote     uint64
+       Mortgage uint64
+}
+
+// Validators return next epoch of validators, if the status of checkpoint is growing, return empty
+func (c *Checkpoint) Validators() []*Validator {
+       var validators []*Validator
+       if c.Status == Growing {
+               return validators
+       }
+
+       for pubKey, mortgageNum := range c.Mortgages {
+               if mortgageNum >= minMortgage {
+                       validators = append(validators, &Validator{
+                               PubKey:   pubKey,
+                               Vote:     c.Votes[pubKey],
+                               Mortgage: mortgageNum,
+                       })
+               }
+       }
+
+       sort.Slice(validators, func(i, j int) bool {
+               return validators[i].Mortgage+validators[i].Vote > validators[j].Mortgage+validators[j].Vote
+       })
+
+       end := numOfValidators
+       if len(validators) < numOfValidators {
+               end = len(validators)
+       }
+       return validators[:end]
+}
+
+// ContainsValidator check whether the checkpoint contains the pubKey as validator
+func (c *Checkpoint) ContainsValidator(pubKey string) bool {
+       for _, v := range c.Validators() {
+               if v.PubKey == pubKey {
+                       return true
+               }
+       }
+       return false
+}
index 38903ea..e9b1d18 100644 (file)
@@ -18,6 +18,9 @@ type Store interface {
        GetTransactionsUtxo(*state.UtxoViewpoint, []*bc.Tx) error
        GetUtxo(*bc.Hash) (*storage.UtxoEntry, error)
 
+       GetCheckpoint(*bc.Hash) (*state.Checkpoint, error)
+       GetCheckpointsByHeight(uint64) ([]*state.Checkpoint, error)
+
        LoadBlockIndex(uint64) (*state.BlockIndex, error)
        SaveBlock(*types.Block, *bc.TransactionStatus) error
        SaveChainStatus(*state.BlockNode, *state.UtxoViewpoint) error