package protocol
import (
+ "encoding/json"
+
log "github.com/sirupsen/logrus"
+ "github.com/vapor/common"
+ "github.com/vapor/consensus"
+ engine "github.com/vapor/consensus/consensus"
+ dpos "github.com/vapor/consensus/consensus/dpos"
"github.com/vapor/errors"
"github.com/vapor/protocol/bc"
"github.com/vapor/protocol/bc/types"
"github.com/vapor/protocol/state"
"github.com/vapor/protocol/validation"
+ "github.com/vapor/protocol/vm"
+ "github.com/vapor/protocol/vm/vmutil"
)
var (
for key, value := range tx.Entries {
switch value.(type) {
case *bc.Claim:
- c.store.SetWithdrawSpent(&key)
+ if err := c.store.SetWithdrawSpent(&key); err != nil {
+ return err
+ }
default:
continue
}
return c.setState(node, utxoView)
}
+func (c *Chain) consensusCheck(block *types.Block) error {
+ if err := dpos.GDpos.CheckBlockHeader(block.BlockHeader); err != nil {
+ return err
+ }
+
+ if err := dpos.GDpos.IsValidBlockCheckIrreversibleBlock(block.Height, block.Hash()); err != nil {
+ return err
+ }
+
+ if err := dpos.GDpos.CheckBlock(*block, true); err != nil {
+ return err
+ }
+ return nil
+}
+
// SaveBlock will validate and save block into storage
func (c *Chain) saveBlock(block *types.Block) error {
bcBlock := types.MapBlock(block)
parent := c.index.GetNode(&block.PreviousBlockHash)
- if err := validation.ValidateBlock(bcBlock, parent, block, c, c.engine, c.Authoritys, c.position); err != nil {
+ if err := c.consensusCheck(block); err != nil {
+ return err
+ }
+
+ if err := validation.ValidateBlock(bcBlock, parent, block); err != nil {
return errors.Sub(ErrBadBlock, err)
}
+
+ if err := c.ProcessDPoSConnectBlock(block); err != nil {
+ return err
+ }
+
if err := c.store.SaveBlock(block, bcBlock.TransactionStatus); err != nil {
return err
}
return false, c.connectBlock(bestBlock)
}
- if bestNode.Height > c.bestNode.Height && bestNode.WorkSum.Cmp(c.bestNode.WorkSum) >= 0 {
+ if bestNode.Height > c.bestNode.Height {
log.Debug("start to reorganize chain")
return false, c.reorganizeChain(bestNode)
}
return false, nil
}
+
+func (c *Chain) ProcessDPoSConnectBlock(block *types.Block) error {
+ mapTxFee := c.CalculateBalance(block, true)
+ if err := c.DoVoting(block, mapTxFee); err != nil {
+ return err
+ }
+ return nil
+}
+
+func (c *Chain) DoVoting(block *types.Block, mapTxFee map[bc.Hash]uint64) error {
+ for _, tx := range block.Transactions {
+ to := tx.Outputs[0]
+ msg := &dpos.DposMsg{}
+
+ if err := json.Unmarshal(tx.TxData.ReferenceData, &msg); err != nil {
+ continue
+ }
+ var (
+ address common.Address
+ err error
+ )
+ address, err = common.NewAddressWitnessPubKeyHash(to.ControlProgram[2:], &consensus.ActiveNetParams)
+ if err != nil {
+ address, err = common.NewAddressWitnessScriptHash(to.ControlProgram[2:], &consensus.ActiveNetParams)
+ if err != nil {
+ return errors.New("ControlProgram cannot be converted to address")
+ }
+ }
+ hash := block.Hash()
+ height := block.Height
+ switch msg.Type {
+ case vm.OP_DELEGATE:
+ continue
+ case vm.OP_REGISTE:
+ if mapTxFee[tx.Tx.ID] >= consensus.RegisrerForgerFee {
+ data := &dpos.RegisterForgerData{}
+ if err := json.Unmarshal(msg.Data, data); err != nil {
+ return err
+ }
+ c.Engine.ProcessRegister(address.EncodeAddress(), data.Name, hash, height)
+ }
+ case vm.OP_VOTE:
+ if mapTxFee[tx.Tx.ID] >= consensus.VoteForgerFee {
+ data := &dpos.VoteForgerData{}
+ if err := json.Unmarshal(msg.Data, data); err != nil {
+ return err
+ }
+ c.Engine.ProcessVote(address.EncodeAddress(), data.Forgers, hash, height)
+ }
+ case vm.OP_REVOKE:
+ if mapTxFee[tx.Tx.ID] >= consensus.CancelVoteForgerFee {
+ data := &dpos.CancelVoteForgerData{}
+ if err := json.Unmarshal(msg.Data, data); err != nil {
+ return err
+ }
+ c.Engine.ProcessCancelVote(address.EncodeAddress(), data.Forgers, hash, height)
+ }
+ }
+ }
+ return nil
+}
+
+func (c *Chain) CalculateBalance(block *types.Block, fIsAdd bool) map[bc.Hash]uint64 {
+
+ addressBalances := []engine.AddressBalance{}
+ mapTxFee := make(map[bc.Hash]uint64)
+ var (
+ address common.Address
+ err error
+ )
+
+ for _, tx := range block.Transactions {
+ fee := uint64(0)
+ for _, input := range tx.Inputs {
+ if input.AssetID() != *consensus.BTMAssetID {
+ continue
+ }
+
+ if len(tx.TxData.Inputs) == 1 &&
+ (tx.TxData.Inputs[0].InputType() == types.CoinbaseInputType ||
+ tx.TxData.Inputs[0].InputType() == types.ClainPeginInputType) {
+ continue
+ }
+
+ fee += input.Amount()
+ value := int64(input.Amount())
+ address, err = common.NewAddressWitnessPubKeyHash(input.ControlProgram()[2:], &consensus.ActiveNetParams)
+ if err != nil {
+ address, err = common.NewAddressWitnessScriptHash(input.ControlProgram()[2:], &consensus.ActiveNetParams)
+ if err != nil {
+ continue
+ }
+ }
+ if fIsAdd {
+ value = 0 - value
+ }
+ addressBalances = append(addressBalances, engine.AddressBalance{address.EncodeAddress(), value})
+ }
+ for _, output := range tx.Outputs {
+ fee -= output.Amount
+ if vmutil.IsUnspendable(output.ControlProgram) {
+ continue
+ }
+ if *output.AssetId != *consensus.BTMAssetID {
+ continue
+ }
+ value := int64(output.Amount)
+ address, err = common.NewAddressWitnessPubKeyHash(output.ControlProgram[2:], &consensus.ActiveNetParams)
+ if err != nil {
+ address, err = common.NewAddressWitnessScriptHash(output.ControlProgram[2:], &consensus.ActiveNetParams)
+ if err != nil {
+ continue
+ }
+ }
+ if !fIsAdd {
+ value = 0 - value
+ }
+ addressBalances = append(addressBalances, engine.AddressBalance{address.EncodeAddress(), value})
+ }
+ mapTxFee[tx.Tx.ID] = fee
+ }
+
+ c.Engine.UpdateAddressBalance(addressBalances)
+ return mapTxFee
+}
+
+func (c *Chain) RepairDPoSData(oldBlockHeight uint64, oldBlockHash bc.Hash) error {
+ block, err := c.GetBlockByHash(&oldBlockHash)
+ if err != nil {
+ return err
+ }
+ if block.Height != oldBlockHeight {
+ return errors.New("The module vote records data with a problem")
+ }
+ for i := block.Height + 1; i <= c.bestNode.Height; i++ {
+ b, err := c.GetBlockByHeight(i)
+ if err != nil {
+ return err
+ }
+ if err := c.ProcessDPoSConnectBlock(b); err != nil {
+ return err
+ }
+
+ }
+ return nil
+}