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
+}
"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")
)
}
// 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
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.
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
}
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
}
}
// 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
}
}
}
// 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
height := block.Height - 1
blockHash = &block.PreviousBlockHash
- if height%blocksOfEpoch == 0 {
+ if height%state.BlocksOfEpoch == 0 {
return blockHash, nil
}
}
+++ /dev/null
-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
-}
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
// 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
// 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
}
--- /dev/null
+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
+}
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