OSDN Git Service

Add consensus result test case
[bytom/vapor.git] / protocol / state / consensus_result.go
index ff08429..d392e1c 100644 (file)
@@ -4,6 +4,8 @@ import (
        "encoding/hex"
        "sort"
 
+       log "github.com/sirupsen/logrus"
+
        "github.com/vapor/common/arithmetic"
        "github.com/vapor/config"
        "github.com/vapor/consensus"
@@ -14,6 +16,17 @@ import (
        "github.com/vapor/protocol/bc/types"
 )
 
+const logModule = "state/consensus"
+
+// 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
@@ -38,9 +51,11 @@ 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) {
@@ -58,7 +73,11 @@ func CalCoinbaseReward(block *types.Block) (*CoinbaseReward, error) {
                        return nil, errors.Wrap(checked.ErrOverflow, "calculate transaction fee")
                }
 
-               result.Amount += txFee
+               ok := false
+               result.Amount, ok = checked.AddUint64(result.Amount, txFee)
+               if !ok {
+                       return nil, checked.ErrOverflow
+               }
        }
        return result, nil
 }
@@ -98,39 +117,47 @@ 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
 }
 
@@ -161,7 +188,9 @@ func (c *ConsensusResult) ConsensusNodes() (map[string]*ConsensusNode, error) {
                if voteNum >= consensus.ActiveNetParams.MinConsensusNodeVoteNum {
                        var xpub chainkd.XPub
                        if err := xpub.UnmarshalText([]byte(pubkey)); err != nil {
-                               return nil, err
+                               log.WithFields(log.Fields{"module": logModule, "err": err, "XPub": xpub.String()}).Debug("failed on unmarshal XPub")
+                               delete(c.NumOfVote, pubkey)
+                               continue
                        }
 
                        nodes = append(nodes, &ConsensusNode{XPub: xpub, VoteNum: voteNum})
@@ -232,16 +261,6 @@ func (c *ConsensusResult) DetachBlock(block *types.Block) error {
 
 // DetachCoinbaseReward detach coinbase reward
 func (c *ConsensusResult) DetachCoinbaseReward(block *types.Block) error {
-       if block.Height%consensus.ActiveNetParams.RoundVoteBlockNums == 0 {
-               for i, output := range block.Transactions[0].Outputs {
-                       if i == 0 {
-                               continue
-                       }
-                       program := output.ControlProgram()
-                       c.CoinbaseReward[hex.EncodeToString(program)] = output.AssetAmount().Amount
-               }
-       }
-
        reward, err := CalCoinbaseReward(block)
        if err != nil {
                return err
@@ -256,6 +275,17 @@ func (c *ConsensusResult) DetachCoinbaseReward(block *types.Block) error {
        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
+                       }
+                       program := output.ControlProgram()
+                       c.CoinbaseReward[hex.EncodeToString(program)] = output.AssetAmount().Amount
+               }
+       }
        return nil
 }
 
@@ -309,7 +339,8 @@ func (c *ConsensusResult) GetCoinbaseRewards(blockHeight uint64) ([]CoinbaseRewa
 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)}
+               derivedXPub := xpub.Derive(fedConsensusPath)
+               consensusResult[derivedXPub.String()] = &ConsensusNode{XPub: derivedXPub, VoteNum: 0, Order: uint64(i)}
        }
        return consensusResult
 }