OSDN Git Service

rename (#465)
[bytom/vapor.git] / protocol / state / consensus_result.go
index e136848..85b812a 100644 (file)
@@ -4,16 +4,25 @@ import (
        "encoding/hex"
        "sort"
 
-       "github.com/vapor/common/arithmetic"
-       "github.com/vapor/config"
-       "github.com/vapor/consensus"
-       "github.com/vapor/crypto/ed25519/chainkd"
-       "github.com/vapor/errors"
-       "github.com/vapor/math/checked"
-       "github.com/vapor/protocol/bc"
-       "github.com/vapor/protocol/bc/types"
+       "github.com/bytom/vapor/common/arithmetic"
+       "github.com/bytom/vapor/config"
+       "github.com/bytom/vapor/consensus"
+       "github.com/bytom/vapor/crypto/ed25519/chainkd"
+       "github.com/bytom/vapor/errors"
+       "github.com/bytom/vapor/math/checked"
+       "github.com/bytom/vapor/protocol/bc"
+       "github.com/bytom/vapor/protocol/bc/types"
 )
 
+// fedConsensusPath is used to derive federation root xpubs for signing blocks
+var fedConsensusPath = [][]byte{
+       []byte{0xff, 0xff, 0xff, 0xff},
+       []byte{0xff, 0x00, 0x00, 0x00},
+       []byte{0xff, 0xff, 0xff, 0xff},
+       []byte{0xff, 0x00, 0x00, 0x00},
+       []byte{0xff, 0x00, 0x00, 0x00},
+}
+
 // ConsensusNode represents a consensus node
 type ConsensusNode struct {
        XPub    chainkd.XPub
@@ -29,30 +38,6 @@ func (c byVote) Less(i, j int) bool {
 }
 func (c byVote) Swap(i, j int) { c[i], c[j] = c[j], c[i] }
 
-// CalcVoteSeq calculate the vote sequence
-// seq 0 is the genesis block
-// seq 1 is the the block height 1, to block height RoundVoteBlockNums
-// seq 2 is the block height RoundVoteBlockNums + 1 to block height 2 * RoundVoteBlockNums
-// consensus node of the current round is the final result of previous round
-func CalcVoteSeq(blockHeight uint64) uint64 {
-       if blockHeight == 0 {
-               return 0
-       }
-       return (blockHeight-1)/consensus.RoundVoteBlockNums + 1
-}
-
-// ConsensusResult represents a snapshot of each round of DPOS voting
-// Seq indicates the sequence of current votes, which start from zero
-// NumOfVote indicates the number of votes each consensus node receives, the key of map represent public key
-// CoinbaseReward indicates the coinbase receiver and reward
-type ConsensusResult struct {
-       Seq            uint64
-       NumOfVote      map[string]uint64
-       CoinbaseReward map[string]uint64
-       BlockHash      bc.Hash
-       BlockHeight    uint64
-}
-
 // CoinbaseReward contains receiver and reward
 type CoinbaseReward struct {
        Amount         uint64
@@ -62,34 +47,55 @@ type CoinbaseReward struct {
 // SortByAmount implements sort.Interface for CoinbaseReward slices
 type SortByAmount []CoinbaseReward
 
-func (a SortByAmount) Len() int           { return len(a) }
-func (a SortByAmount) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
-func (a SortByAmount) Less(i, j int) bool { return a[i].Amount < a[j].Amount }
+func (a SortByAmount) Len() int      { return len(a) }
+func (a SortByAmount) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
+func (a SortByAmount) Less(i, j int) bool {
+       return a[i].Amount > a[j].Amount || (a[i].Amount == a[j].Amount && hex.EncodeToString(a[i].ControlProgram) > hex.EncodeToString(a[j].ControlProgram))
+}
 
 // CalCoinbaseReward calculate the coinbase reward for block
 func CalCoinbaseReward(block *types.Block) (*CoinbaseReward, error) {
-       var coinbaseReceiver []byte
+       result := &CoinbaseReward{}
        if len(block.Transactions) > 0 && len(block.Transactions[0].Outputs) > 0 {
-               coinbaseReceiver = block.Transactions[0].Outputs[0].ControlProgram()
-       }
-
-       if coinbaseReceiver == nil {
+               result.ControlProgram = block.Transactions[0].Outputs[0].ControlProgram()
+       } else {
                return nil, errors.New("not found coinbase receiver")
        }
 
-       coinbaseAmount := consensus.BlockSubsidy(block.BlockHeader.Height)
+       result.Amount = consensus.BlockSubsidy(block.BlockHeader.Height)
        for _, tx := range block.Transactions {
                txFee, err := arithmetic.CalculateTxFee(tx)
                if err != nil {
                        return nil, errors.Wrap(checked.ErrOverflow, "calculate transaction fee")
                }
-               coinbaseAmount += txFee
+
+               result.Amount += txFee
        }
+       return result, nil
+}
 
-       return &CoinbaseReward{
-               Amount:         coinbaseAmount,
-               ControlProgram: coinbaseReceiver,
-       }, nil
+// CalcVoteSeq calculate the vote sequence
+// seq 0 is the genesis block
+// seq 1 is the the block height 1, to block height RoundVoteBlockNums
+// seq 2 is the block height RoundVoteBlockNums + 1 to block height 2 * RoundVoteBlockNums
+// consensus node of the current round is the final result of previous round
+func CalcVoteSeq(blockHeight uint64) uint64 {
+       if blockHeight == 0 {
+               return 0
+       }
+       return (blockHeight-1)/consensus.ActiveNetParams.RoundVoteBlockNums + 1
+}
+
+// ConsensusResult represents a snapshot of each round of DPOS voting
+// Seq indicates the sequence of current votes, which start from zero
+// NumOfVote indicates the number of votes each consensus node receives, the key of map represent public key
+// CoinbaseReward indicates the coinbase receiver and reward
+type ConsensusResult struct {
+       Seq            uint64
+       NumOfVote      map[string]uint64
+       CoinbaseReward map[string]uint64
+       BlockHash      bc.Hash
+       BlockHeight    uint64
 }
 
 // ApplyBlock calculate the consensus result for new block
@@ -103,39 +109,67 @@ func (c *ConsensusResult) ApplyBlock(block *types.Block) error {
        }
 
        for _, tx := range block.Transactions {
-               for _, input := range tx.Inputs {
-                       vetoInput, ok := input.TypedInput.(*types.VetoInput)
-                       if !ok {
-                               continue
-                       }
+               if err := c.ApplyTransaction(tx); err != nil {
+                       return err
+               }
+       }
 
-                       pubkey := hex.EncodeToString(vetoInput.Vote)
-                       c.NumOfVote[pubkey], ok = checked.SubUint64(c.NumOfVote[pubkey], vetoInput.Amount)
-                       if !ok {
-                               return checked.ErrOverflow
-                       }
+       c.BlockHash = block.Hash()
+       c.BlockHeight = block.Height
+       c.Seq = CalcVoteSeq(block.Height)
+       return nil
+}
 
-                       if c.NumOfVote[pubkey] == 0 {
-                               delete(c.NumOfVote, pubkey)
-                       }
+// ApplyTransaction calculate the consensus result for transaction
+func (c *ConsensusResult) ApplyTransaction(tx *types.Tx) error {
+       for _, input := range tx.Inputs {
+               vetoInput, ok := input.TypedInput.(*types.VetoInput)
+               if !ok {
+                       continue
                }
 
-               for _, output := range tx.Outputs {
-                       voteOutput, ok := output.TypedOutput.(*types.VoteOutput)
-                       if !ok {
-                               continue
-                       }
+               pubkey := hex.EncodeToString(vetoInput.Vote)
+               c.NumOfVote[pubkey], ok = checked.SubUint64(c.NumOfVote[pubkey], vetoInput.Amount)
+               if !ok {
+                       return checked.ErrOverflow
+               }
 
-                       pubkey := hex.EncodeToString(voteOutput.Vote)
-                       if c.NumOfVote[pubkey], ok = checked.AddUint64(c.NumOfVote[pubkey], voteOutput.Amount); !ok {
-                               return checked.ErrOverflow
-                       }
+               if c.NumOfVote[pubkey] == 0 {
+                       delete(c.NumOfVote, pubkey)
                }
        }
 
-       c.BlockHash = block.Hash()
-       c.BlockHeight = block.Height
-       c.Seq = CalcVoteSeq(block.Height)
+       for _, output := range tx.Outputs {
+               voteOutput, ok := output.TypedOutput.(*types.VoteOutput)
+               if !ok {
+                       continue
+               }
+
+               pubkey := hex.EncodeToString(voteOutput.Vote)
+               if c.NumOfVote[pubkey], ok = checked.AddUint64(c.NumOfVote[pubkey], voteOutput.Amount); !ok {
+                       return checked.ErrOverflow
+               }
+       }
+       return nil
+}
+
+// AttachCoinbaseReward attach coinbase reward
+func (c *ConsensusResult) AttachCoinbaseReward(block *types.Block) error {
+       reward, err := CalCoinbaseReward(block)
+       if err != nil {
+               return err
+       }
+
+       if block.Height%consensus.ActiveNetParams.RoundVoteBlockNums == 1 {
+               c.CoinbaseReward = map[string]uint64{}
+       }
+
+       var ok bool
+       program := hex.EncodeToString(reward.ControlProgram)
+       c.CoinbaseReward[program], ok = checked.AddUint64(c.CoinbaseReward[program], reward.Amount)
+       if !ok {
+               return checked.ErrOverflow
+       }
        return nil
 }
 
@@ -143,7 +177,7 @@ func (c *ConsensusResult) ApplyBlock(block *types.Block) error {
 func (c *ConsensusResult) ConsensusNodes() (map[string]*ConsensusNode, error) {
        var nodes []*ConsensusNode
        for pubkey, voteNum := range c.NumOfVote {
-               if voteNum >= consensus.MinConsensusNodeVoteNum {
+               if voteNum >= consensus.ActiveNetParams.MinConsensusNodeVoteNum {
                        var xpub chainkd.XPub
                        if err := xpub.UnmarshalText([]byte(pubkey)); err != nil {
                                return nil, err
@@ -156,7 +190,7 @@ func (c *ConsensusResult) ConsensusNodes() (map[string]*ConsensusNode, error) {
        // if there is a performance problem, consider the optimization later.
        sort.Sort(byVote(nodes))
        result := make(map[string]*ConsensusNode)
-       for i := 0; i < len(nodes) && i < consensus.NumOfConsensusNode; i++ {
+       for i := 0; i < len(nodes) && int64(i) < consensus.ActiveNetParams.NumOfConsensusNode; i++ {
                nodes[i].Order = uint64(i)
                result[nodes[i].XPub.String()] = nodes[i]
        }
@@ -167,14 +201,6 @@ func (c *ConsensusResult) ConsensusNodes() (map[string]*ConsensusNode, error) {
        return federationNodes(), nil
 }
 
-func federationNodes() map[string]*ConsensusNode {
-       consensusResult := map[string]*ConsensusNode{}
-       for i, xpub := range config.CommonConfig.Federation.Xpubs {
-               consensusResult[xpub.String()] = &ConsensusNode{XPub: xpub, VoteNum: 0, Order: uint64(i)}
-       }
-       return consensusResult
-}
-
 // DetachBlock calculate the consensus result for detach block
 func (c *ConsensusResult) DetachBlock(block *types.Block) error {
        if c.BlockHash != block.Hash() {
@@ -223,52 +249,25 @@ func (c *ConsensusResult) DetachBlock(block *types.Block) error {
        return nil
 }
 
-func (c *ConsensusResult) Fork() *ConsensusResult {
-       f := &ConsensusResult{
-               Seq:            c.Seq,
-               NumOfVote:      map[string]uint64{},
-               CoinbaseReward: map[string]uint64{},
-               BlockHash:      c.BlockHash,
-               BlockHeight:    c.BlockHeight,
-       }
-
-       for key, value := range c.NumOfVote {
-               f.NumOfVote[key] = value
-       }
-
-       for key, value := range c.CoinbaseReward {
-               f.CoinbaseReward[key] = value
-       }
-       return f
-}
-
-func (c *ConsensusResult) IsFinalize() bool {
-       return c.BlockHeight%consensus.RoundVoteBlockNums == 0
-}
-
-// AttachCoinbaseReward attach coinbase reward
-func (c *ConsensusResult) AttachCoinbaseReward(block *types.Block) error {
+// DetachCoinbaseReward detach coinbase reward
+func (c *ConsensusResult) DetachCoinbaseReward(block *types.Block) error {
        reward, err := CalCoinbaseReward(block)
        if err != nil {
                return err
        }
 
-       if block.Height%consensus.RoundVoteBlockNums == 1 {
-               c.CoinbaseReward = map[string]uint64{}
-       }
-
        var ok bool
        program := hex.EncodeToString(reward.ControlProgram)
-       c.CoinbaseReward[program], ok = checked.AddUint64(c.CoinbaseReward[program], reward.Amount)
-       if !ok {
+       if c.CoinbaseReward[program], ok = checked.SubUint64(c.CoinbaseReward[program], reward.Amount); !ok {
                return checked.ErrOverflow
        }
-       return nil
-}
 
-// DetachCoinbaseReward detach coinbase reward
-func (c *ConsensusResult) DetachCoinbaseReward(block *types.Block) error {
-       if block.Height%consensus.RoundVoteBlockNums == 0 {
+       if c.CoinbaseReward[program] == 0 {
+               delete(c.CoinbaseReward, program)
+       }
+
+       if block.Height%consensus.ActiveNetParams.RoundVoteBlockNums == 1 {
+               c.CoinbaseReward = map[string]uint64{}
                for i, output := range block.Transactions[0].Outputs {
                        if i == 0 {
                                continue
@@ -277,43 +276,61 @@ func (c *ConsensusResult) DetachCoinbaseReward(block *types.Block) error {
                        c.CoinbaseReward[hex.EncodeToString(program)] = output.AssetAmount().Amount
                }
        }
+       return nil
+}
 
-       reward, err := CalCoinbaseReward(block)
-       if err != nil {
-               return err
+// Fork copy the ConsensusResult struct
+func (c *ConsensusResult) Fork() *ConsensusResult {
+       f := &ConsensusResult{
+               Seq:            c.Seq,
+               NumOfVote:      map[string]uint64{},
+               CoinbaseReward: map[string]uint64{},
+               BlockHash:      c.BlockHash,
+               BlockHeight:    c.BlockHeight,
        }
 
-       var ok bool
-       program := hex.EncodeToString(reward.ControlProgram)
-       if c.CoinbaseReward[program], ok = checked.SubUint64(c.CoinbaseReward[program], reward.Amount); !ok {
-               return checked.ErrOverflow
+       for key, value := range c.NumOfVote {
+               f.NumOfVote[key] = value
        }
 
-       if c.CoinbaseReward[program] == 0 {
-               delete(c.CoinbaseReward, program)
+       for key, value := range c.CoinbaseReward {
+               f.CoinbaseReward[key] = value
        }
-       return nil
+       return f
+}
+
+// IsFinalize check if the result is end of consensus round
+func (c *ConsensusResult) IsFinalize() bool {
+       return c.BlockHeight%consensus.ActiveNetParams.RoundVoteBlockNums == 0
 }
 
 // GetCoinbaseRewards convert into CoinbaseReward array and sort it by amount
 func (c *ConsensusResult) GetCoinbaseRewards(blockHeight uint64) ([]CoinbaseReward, error) {
        rewards := []CoinbaseReward{}
-       if blockHeight%consensus.RoundVoteBlockNums != 0 {
+       if blockHeight%consensus.ActiveNetParams.RoundVoteBlockNums != 0 {
                return rewards, nil
        }
 
        for p, amount := range c.CoinbaseReward {
-               coinbaseAmount := amount
                program, err := hex.DecodeString(p)
                if err != nil {
                        return nil, err
                }
 
                rewards = append(rewards, CoinbaseReward{
-                       Amount:         coinbaseAmount,
+                       Amount:         amount,
                        ControlProgram: program,
                })
        }
        sort.Sort(SortByAmount(rewards))
        return rewards, nil
 }
+
+func federationNodes() map[string]*ConsensusNode {
+       consensusResult := map[string]*ConsensusNode{}
+       for i, xpub := range config.CommonConfig.Federation.Xpubs {
+               derivedXPub := xpub.Derive(fedConsensusPath)
+               consensusResult[derivedXPub.String()] = &ConsensusNode{XPub: derivedXPub, VoteNum: 0, Order: uint64(i)}
+       }
+       return consensusResult
+}