OSDN Git Service

Add the implementation for dppos
[bytom/vapor.git] / consensus / consensus / dpos / dpos.go
index 5eaf632..de0ed2e 100644 (file)
 package dpos
 
 import (
-       "bytes"
+       "bufio"
+       "encoding/binary"
        "encoding/json"
        "errors"
-       "math/big"
+       "fmt"
+       "io"
+       "math/rand"
+       "os"
+       "path/filepath"
+       "sort"
+       "strings"
        "sync"
        "time"
 
-       lru "github.com/hashicorp/golang-lru"
-       log "github.com/sirupsen/logrus"
        "github.com/vapor/chain"
        "github.com/vapor/common"
        "github.com/vapor/config"
        "github.com/vapor/consensus"
+       engine "github.com/vapor/consensus/consensus"
        "github.com/vapor/crypto"
-       "github.com/vapor/crypto/ed25519/chainkd"
-       "github.com/vapor/protocol"
        "github.com/vapor/protocol/bc"
        "github.com/vapor/protocol/bc/types"
-       "github.com/vapor/protocol/vm/vmutil"
+       "github.com/vapor/protocol/vm"
 )
 
-const (
-       inMemorySnapshots  = 128             // Number of recent vote snapshots to keep in memory
-       inMemorySignatures = 4096            // Number of recent block signatures to keep in memory
-       secondsPerYear     = 365 * 24 * 3600 // Number of seconds for one year
-       checkpointInterval = 360             // About N hours if config.period is N
-       module             = "dpos"
-)
+type Delegate struct {
+       DelegateAddress string `json:"delegate_address"`
+       Votes           uint64 `json:"votes"`
+}
 
-//delegated-proof-of-stake protocol constants.
-var (
-       SignerBlockReward     = big.NewInt(5e+18) // Block reward in wei for successfully mining a block first year
-       defaultEpochLength    = uint64(3000000)   // Default number of blocks after which vote's period of validity
-       defaultBlockPeriod    = uint64(3)         // Default minimum difference between two consecutive block's timestamps
-       defaultMaxSignerCount = uint64(21)        //
-       //defaultMinVoterBalance           = new(big.Int).Mul(big.NewInt(10000), big.NewInt(1e+18))
-       defaultMinVoterBalance           = uint64(0)
-       extraVanity                      = 32            // Fixed number of extra-data prefix bytes reserved for signer vanity
-       extraSeal                        = 65            // Fixed number of extra-data suffix bytes reserved for signer seal
-       defaultDifficulty                = big.NewInt(1) // Default difficulty
-       defaultLoopCntRecalculateSigners = uint64(10)    // Default loop count to recreate signers from top tally
-       minerRewardPerThousand           = uint64(618)   // Default reward for miner in each block from block reward (618/1000)
-       candidateNeedPD                  = false         // is new candidate need Proposal & Declare process
-)
+type DelegateWrapper struct {
+       delegate []Delegate
+       by       func(p, q *Delegate) bool
+}
 
-var (
-       // errUnknownBlock is returned when the list of signers is requested for a block
-       // that is not part of the local blockchain.
-       errUnknownBlock = errors.New("unknown block")
+func (dw DelegateWrapper) Len() int {
+       return len(dw.delegate)
+}
+func (dw DelegateWrapper) Swap(i, j int) {
+       dw.delegate[i], dw.delegate[j] = dw.delegate[j], dw.delegate[i]
+}
+func (dw DelegateWrapper) Less(i, j int) bool {
+       return dw.by(&dw.delegate[i], &dw.delegate[j])
+}
 
-       // errMissingVanity is returned if a block's extra-data section is shorter than
-       // 32 bytes, which is required to store the signer vanity.
-       errMissingVanity = errors.New("extra-data 32 byte vanity prefix missing")
+type DelegateInfo struct {
+       Delegates []Delegate `json:"delegates"`
+}
 
-       // errMissingSignature is returned if a block's extra-data section doesn't seem
-       // to contain a 65 byte secp256k1 signature.
-       errMissingSignature = errors.New("extra-data 65 byte suffix signature missing")
+func (d *DelegateInfo) ConsensusName() string {
+       return "dpos"
+}
 
-       // errInvalidMixDigest is returned if a block's mix digest is non-zero.
-       errInvalidMixDigest = errors.New("non-zero mix digest")
+const maxConfirmBlockCount = 2
 
-       // errInvalidUncleHash is returned if a block contains an non-empty uncle list.
-       errInvalidUncleHash = errors.New("non empty uncle hash")
+type IrreversibleBlockInfo struct {
+       heights    []int64
+       hashs      []bc.Hash
+       HeightHash map[int64]bc.Hash
+}
 
-       // ErrInvalidTimestamp is returned if the timestamp of a block is lower than
-       // the previous block's timestamp + the minimum block period.
-       ErrInvalidTimestamp = errors.New("invalid timestamp")
+func newIrreversibleBlockInfo() *IrreversibleBlockInfo {
+       o := &IrreversibleBlockInfo{}
+       for i := 0; i < maxConfirmBlockCount; i++ {
+               o.heights = append(o.heights, -1)
+               o.hashs = append(o.hashs, bc.Hash{})
+       }
+       o.HeightHash = make(map[int64]bc.Hash)
+       return o
+}
 
-       // errInvalidVotingChain is returned if an authorization list is attempted to
-       // be modified via out-of-range or non-contiguous headers.
-       errInvalidVotingChain = errors.New("invalid voting chain")
+type DposType struct {
+       c                           chain.Chain
+       vote                        *Vote
+       MaxDelegateNumber           uint64
+       BlockIntervalTime           uint64
+       DposStartHeight             uint64
+       DposStartTime               uint64
+       superForgerAddress          common.Address
+       irreversibleBlockFileName   string
+       irreversibleBlockInfo       IrreversibleBlockInfo
+       lockIrreversibleBlockInfo   sync.Mutex
+       maxIrreversibleCount        int
+       firstIrreversibleThreshold  uint64
+       secondIrreversibleThreshold uint64
+}
 
-       // errUnauthorized is returned if a header is signed by a non-authorized entity.
-       errUnauthorized = errors.New("unauthorized")
+var GDpos = &DposType{
+       maxIrreversibleCount:        10000,
+       firstIrreversibleThreshold:  90,
+       secondIrreversibleThreshold: 67,
+}
 
-       // errPunishedMissing is returned if a header calculate punished signer is wrong.
-       errPunishedMissing = errors.New("punished signer missing")
+func (d *DposType) Init(c chain.Chain, delegateNumber, intervalTime, blockHeight uint64, blockHash bc.Hash) error {
+       d.c = c
+       vote, err := newVote(blockHeight, blockHash)
+       if err != nil {
+               return err
+       }
+       d.vote = vote
+       d.MaxDelegateNumber = delegateNumber
+       d.BlockIntervalTime = intervalTime
+       d.DposStartHeight = 0
+       address, _ := common.DecodeAddress("vsm1qkm743xmgnvh84pmjchq2s4tnfpgu9ae2f9slep", &consensus.ActiveNetParams)
+       d.superForgerAddress = address
 
-       // errWaitTransactions is returned if an empty block is attempted to be sealed
-       // on an instant chain (0 second period). It's important to refuse these as the
-       // block reward is zero, so an empty block just bloats the chain... fast.
-       errWaitTransactions = errors.New("waiting for transactions")
+       GDpos.irreversibleBlockFileName = filepath.Join(config.CommonConfig.RootDir, "dpos", "irreversible_block.dat")
+       GDpos.irreversibleBlockInfo = *newIrreversibleBlockInfo()
+       if err := GDpos.ReadIrreversibleBlockInfo(&GDpos.irreversibleBlockInfo); err != nil {
+               return err
+       }
 
-       // errUnclesNotAllowed is returned if uncles exists
-       errUnclesNotAllowed = errors.New("uncles not allowed")
+       header, _ := c.GetHeaderByHeight(d.DposStartHeight)
+       d.setStartTime(header.Timestamp)
+       return nil
+}
 
-       // errCreateSignerQueueNotAllowed is returned if called in (block number + 1) % maxSignerCount != 0
-       errCreateSignerQueueNotAllowed = errors.New("create signer queue not allowed")
+func (d *DposType) setStartTime(t uint64) {
+       d.DposStartTime = t
+}
 
-       // errInvalidSignerQueue is returned if verify SignerQueue fail
-       errInvalidSignerQueue = errors.New("invalid signer queue")
+func (d *DposType) IsMining(address common.Address, t uint64) (interface{}, error) {
+
+       header := d.c.BestBlockHeader()
+       currentLoopIndex := d.GetLoopIndex(t)
+       currentDelegateIndex := d.GetDelegateIndex(t)
+       prevLoopIndex := d.GetLoopIndex(header.Timestamp)
+       prevDelegateIndex := d.GetDelegateIndex(header.Timestamp)
+       if currentLoopIndex > prevLoopIndex {
+               delegateInfo := d.GetNextDelegates(t)
+               cDelegateInfo := delegateInfo.(*DelegateInfo)
+               if cDelegateInfo.Delegates[currentDelegateIndex].DelegateAddress == address.EncodeAddress() {
+                       return delegateInfo, nil
+               }
+               return nil, errors.New("Is not the current mining node")
+       } else if currentLoopIndex == prevLoopIndex && currentDelegateIndex > prevDelegateIndex {
+               currentDelegateInfo, err := d.GetBlockDelegates(header)
+               if err != nil {
+                       return nil, err
+               }
+               if currentDelegateIndex+1 > uint64(len(currentDelegateInfo.Delegates)) {
+                       return nil, errors.New("Out of the block node list")
+               } else if currentDelegateInfo.Delegates[currentDelegateIndex].DelegateAddress == address.EncodeAddress() {
+                       return nil, nil
+               } else {
+                       return nil, errors.New("Is not the current mining node")
+               }
+       } else {
+               return nil, errors.New("Time anomaly")
+       }
+}
 
-       // errSignerQueueEmpty is returned if no signer when calculate
-       errSignerQueueEmpty = errors.New("signer queue is empty")
-)
+func (d *DposType) ProcessRegister(delegateAddress string, delegateName string, hash bc.Hash, height uint64) bool {
+       return d.vote.ProcessRegister(delegateAddress, delegateName, hash, height)
+}
 
-type Dpos struct {
-       config     *config.DposConfig // Consensus engine configuration parameters
-       store      protocol.Store     // Database to store and retrieve snapshot checkpoints
-       recents    *lru.ARCCache      // Snapshots for recent block to speed up reorgs
-       signatures *lru.ARCCache      // Signatures of recent blocks to speed up mining
-       signer     string             // Ethereum address of the signing key
-       lock       sync.RWMutex       // Protects the signer fields
-       lcsc       uint64             // Last confirmed side chain
-}
-
-//
-func ecrecover(header *types.BlockHeader, sigcache *lru.ARCCache, c chain.Chain) (string, error) {
-       xpub := &chainkd.XPub{}
-       xpub.UnmarshalText(header.Coinbase)
-       derivedPK := xpub.PublicKey()
-       pubHash := crypto.Ripemd160(derivedPK)
-       address, err := common.NewAddressWitnessPubKeyHash(pubHash, &consensus.ActiveNetParams)
-       if err != nil {
-               return "", err
-       }
+func (d *DposType) ProcessVote(voterAddress string, delegates []string, hash bc.Hash, height uint64) bool {
+       return d.vote.ProcessVote(voterAddress, delegates, hash, height)
+}
 
-       return address.EncodeAddress(), nil
+func (d *DposType) ProcessCancelVote(voterAddress string, delegates []string, hash bc.Hash, height uint64) bool {
+       return d.vote.ProcessCancelVote(voterAddress, delegates, hash, height)
 }
 
-//
-func New(config *config.DposConfig, store protocol.Store) *Dpos {
-       conf := *config
-       if conf.Epoch == 0 {
-               conf.Epoch = defaultEpochLength
-       }
-       if conf.Period == 0 {
-               conf.Period = defaultBlockPeriod
-       }
-       if conf.MaxSignerCount == 0 {
-               conf.MaxSignerCount = defaultMaxSignerCount
+func (d *DposType) UpdateAddressBalance(addressBalance []engine.AddressBalance) {
+       d.vote.UpdateAddressBalance(addressBalance)
+}
+
+func (d *DposType) GetLoopIndex(time uint64) uint64 {
+       if time < d.DposStartTime {
+               return 0
        }
-       if conf.MinVoterBalance == 0 {
-               conf.MinVoterBalance = defaultMinVoterBalance
+       return (time - d.DposStartTime) / (d.MaxDelegateNumber * d.BlockIntervalTime)
+}
+
+func (d *DposType) GetDelegateIndex(time uint64) uint64 {
+       if time < d.DposStartTime {
+               return 0
        }
-       // Allocate the snapshot caches and create the engine
-       recents, _ := lru.NewARC(inMemorySnapshots)
-       signatures, _ := lru.NewARC(inMemorySignatures)
-       return &Dpos{
-               config:     &conf,
-               store:      store,
-               recents:    recents,
-               signatures: signatures,
+       return (time - d.DposStartTime) % (d.MaxDelegateNumber * d.BlockIntervalTime) / d.BlockIntervalTime
+}
+
+func (d *DposType) GetNextDelegates(t uint64) interface{} {
+       delegates := d.vote.GetTopDelegateInfo(config.CommonConfig.Consensus.MinVoterBalance, d.MaxDelegateNumber-1)
+       delegate := Delegate{
+               DelegateAddress: d.superForgerAddress.EncodeAddress(),
+               Votes:           7,
        }
+       delegates = append(delegates, delegate)
+       delegateInfo := DelegateInfo{}
+       delegateInfo.Delegates = SortDelegate(delegates, t)
+       return &delegateInfo
 }
 
-// Authorize injects a private key into the consensus engine to mint new blocks with.
-func (d *Dpos) Authorize(signer string) {
-       d.lock.Lock()
-       defer d.lock.Unlock()
-       d.signer = signer
+func (d *DposType) GetBlockDelegates(header *types.BlockHeader) (*DelegateInfo, error) {
+       loopIndex := d.GetLoopIndex(header.Timestamp)
+       for {
+               preHeader, err := d.c.GetHeaderByHash(&header.PreviousBlockHash)
+               if err != nil {
+                       return nil, err
+               }
+               if header.Height == d.DposStartHeight || d.GetLoopIndex(preHeader.Timestamp) < loopIndex {
+                       block, err := d.c.GetBlockByHeight(header.Height)
+                       if err != nil {
+                               return nil, err
+                       }
+                       delegateInfo, err := d.GetBlockDelegate(block)
+                       if err != nil {
+                               return nil, err
+                       }
+                       return delegateInfo, nil
+               }
+               header = preHeader
+       }
 }
 
-func (d *Dpos) VerifySeal(c chain.Chain, header *types.BlockHeader) error {
-       return d.verifyCascadingFields(c, header, nil)
+func (d *DposType) GetBlockDelegate(block *types.Block) (*DelegateInfo, error) {
+       tx := block.Transactions[0]
+       if len(tx.TxData.Inputs) == 1 && tx.TxData.Inputs[0].InputType() == types.CoinbaseInputType {
+               msg := &DposMsg{}
+               if err := json.Unmarshal(tx.TxData.ReferenceData, msg); err != nil {
+                       return nil, err
+               }
+               if msg.Type == vm.OP_DELEGATE {
+                       delegateInfo := &DelegateInfoList{}
+                       if err := json.Unmarshal(msg.Data, delegateInfo); err != nil {
+                               return nil, err
+                       }
+                       return &delegateInfo.Delegate, nil
+               }
+
+       }
+       return nil, errors.New("The first transaction is not a coinbase transaction")
 }
 
-func (d *Dpos) verifyCascadingFields(c chain.Chain, header *types.BlockHeader, parents []*types.BlockHeader) error {
-       // The genesis block is the always valid dead-end
-       height := header.Height
-       if height == 0 {
-               return nil
+func (d *DposType) CheckCoinbase(tx types.TxData, t uint64, Height uint64) error {
+       msg := &DposMsg{}
+       if err := json.Unmarshal(tx.ReferenceData, msg); err != nil {
+               return err
        }
+       if msg.Type == vm.OP_DELEGATE {
+               delegateInfo := &DelegateInfoList{}
+               if err := json.Unmarshal(msg.Data, delegateInfo); err != nil {
+                       return err
+               }
+               buf := [8]byte{}
+               binary.LittleEndian.PutUint64(buf[:], t)
 
-       var (
-               parent *types.BlockHeader
-               err    error
-       )
+               if !delegateInfo.Xpub.Verify(buf[:], delegateInfo.SigTime) {
+                       return errors.New("CheckBlock CheckCoinbase: Verification signature error")
+               }
+               var (
+                       address common.Address
+                       err     error
+               )
+               address, err = common.NewAddressWitnessPubKeyHash(tx.Outputs[0].ControlProgram[2:], &consensus.ActiveNetParams)
+               if err != nil {
+                       return err
+               }
+               derivedPK := delegateInfo.Xpub.PublicKey()
+               pubHash := crypto.Ripemd160(derivedPK)
 
-       if len(parents) > 0 {
-               parent = parents[len(parents)-1]
-       } else {
-               parent, err = c.GetHeaderByHeight(height - 1)
+               addressDet, err := common.NewAddressWitnessPubKeyHash(pubHash, &consensus.ActiveNetParams)
                if err != nil {
                        return err
                }
+
+               if addressDet.EncodeAddress() == address.EncodeAddress() {
+                       return nil
+               }
        }
+       return errors.New("CheckBlock CheckCoinbase error")
+}
+
+func (d *DposType) CheckBlockHeader(header types.BlockHeader) error {
+       blockT := time.Unix(int64(header.Timestamp), 0)
 
-       if parent == nil {
-               return errors.New("unknown ancestor")
+       if blockT.Sub(time.Now()).Seconds() > float64(d.BlockIntervalTime) {
+               return errors.New("block time is error")
        }
 
-       if _, err = d.snapshot(c, height-1, header.PreviousBlockHash, parents, nil, defaultLoopCntRecalculateSigners); err != nil {
+       if header.Height > d.DposStartHeight {
+               header, _ := d.c.GetHeaderByHeight(d.DposStartHeight)
+               d.setStartTime(header.Timestamp)
+       }
+
+       preHeader, err := d.c.GetHeaderByHash(&header.PreviousBlockHash)
+       if err != nil {
                return err
        }
 
-       return d.verifySeal(c, header, parents)
+       currentLoopIndex := d.GetLoopIndex(header.Timestamp)
+       currentDelegateIndex := d.GetDelegateIndex(header.Timestamp)
+       prevLoopIndex := d.GetLoopIndex(preHeader.Timestamp)
+       prevDelegateIndex := d.GetDelegateIndex(preHeader.Timestamp)
+       if currentLoopIndex > prevLoopIndex ||
+               (currentLoopIndex == prevLoopIndex && currentDelegateIndex > prevDelegateIndex) {
+               return nil
+       }
+
+       return errors.New("DPoS CheckBlockHeader error")
 }
 
-// verifySeal checks whether the signature contained in the header satisfies the
-// consensus protocol requirements. The method accepts an optional list of parent
-// headers that aren't yet part of the local blockchain to generate the snapshots
-// from.
-func (d *Dpos) verifySeal(c chain.Chain, header *types.BlockHeader, parents []*types.BlockHeader) error {
-       height := header.Height
-       if height == 0 {
-               return errUnknownBlock
+func (d *DposType) CheckBlock(block types.Block, fIsCheckDelegateInfo bool) error {
+       if block.Height > d.DposStartHeight {
+               header, _ := d.c.GetHeaderByHeight(d.DposStartHeight)
+               d.setStartTime(header.Timestamp)
+       }
+
+       blockT := time.Unix(int64(block.Timestamp), 0)
+       if blockT.Sub(time.Now()).Seconds() > float64(d.BlockIntervalTime) {
+               return errors.New("block time is error")
+       }
+       if err := d.CheckCoinbase(block.Transactions[0].TxData, block.Timestamp, block.Height); err != nil {
+               return err
        }
-       // Retrieve the snapshot needed to verify this header and cache it
-       snap, err := d.snapshot(c, height-1, header.PreviousBlockHash, parents, nil, defaultLoopCntRecalculateSigners)
+
+       preBlock, err := d.c.GetBlockByHash(&block.PreviousBlockHash)
        if err != nil {
                return err
        }
 
-       signer := ""
+       currentLoopIndex := d.GetLoopIndex(block.Timestamp)
+       currentDelegateIndex := d.GetDelegateIndex(block.Timestamp)
+       prevLoopIndex := d.GetLoopIndex(preBlock.Timestamp)
+       prevDelegateIndex := d.GetDelegateIndex(preBlock.Timestamp)
 
-       if height > d.config.MaxSignerCount {
-               var (
-                       parent *types.BlockHeader
-                       err    error
-               )
-               if len(parents) > 0 {
-                       parent = parents[len(parents)-1]
-               } else {
-                       if parent, err = c.GetHeaderByHeight(height - 1); err != nil {
+       delegateInfo := &DelegateInfo{}
+
+       if currentLoopIndex < prevLoopIndex {
+               return errors.New("Block time exception")
+       } else if currentLoopIndex > prevLoopIndex {
+               if fIsCheckDelegateInfo {
+                       if err := d.CheckBlockDelegate(block); err != nil {
                                return err
                        }
+                       d.ProcessIrreversibleBlock(block.Height, block.Hash())
                }
-
-               //parent
-               xpub := &chainkd.XPub{}
-               xpub.UnmarshalText(parent.Coinbase)
-               derivedPK := xpub.PublicKey()
-               pubHash := crypto.Ripemd160(derivedPK)
-               parentCoinbase, err := common.NewAddressWitnessPubKeyHash(pubHash, &consensus.ActiveNetParams)
+               delegateInfo, err = d.GetBlockDelegate(&block)
                if err != nil {
                        return err
                }
+       } else {
+               if currentDelegateIndex < prevDelegateIndex {
+                       return errors.New("Block time exception")
+               }
 
-               //current
-               xpub.UnmarshalText(header.Coinbase)
-               derivedPK = xpub.PublicKey()
-               pubHash = crypto.Ripemd160(derivedPK)
-               currentCoinbase, err := common.NewAddressWitnessPubKeyHash(pubHash, &consensus.ActiveNetParams)
+               delegateInfo, err = d.GetBlockDelegates(&preBlock.BlockHeader)
                if err != nil {
                        return err
                }
-               signer = currentCoinbase.EncodeAddress()
+       }
 
-               parentHeaderExtra := HeaderExtra{}
-               if err = json.Unmarshal(parent.Extra[extraVanity:len(parent.Extra)-extraSeal], &parentHeaderExtra); err != nil {
-                       return err
-               }
+       delegateAddress := d.getBlockForgerAddress(block)
+       if currentDelegateIndex < uint64(len(delegateInfo.Delegates)) &&
+               delegateInfo.Delegates[currentDelegateIndex].DelegateAddress == delegateAddress.EncodeAddress() {
+               return nil
+       }
+       h := block.Hash()
+       return fmt.Errorf("CheckBlock GetDelegateID blockhash:%s error", h.String())
+}
 
-               currentHeaderExtra := HeaderExtra{}
-               if err = json.Unmarshal(header.Extra[extraVanity:len(header.Extra)-extraSeal], &currentHeaderExtra); err != nil {
-                       return err
+func (d *DposType) CheckBlockDelegate(block types.Block) error {
+       delegateInfo, err := d.GetBlockDelegate(&block)
+       if err != nil {
+               return err
+       }
+       nextDelegateInfoInterface := d.GetNextDelegates(block.Timestamp)
+       nextDelegateInfo := nextDelegateInfoInterface.(*DelegateInfo)
+       if len(delegateInfo.Delegates) != len(nextDelegateInfo.Delegates) {
+               return errors.New("The delegates num is not correct in block")
+       }
+       for index, v := range delegateInfo.Delegates {
+               if v.DelegateAddress != nextDelegateInfo.Delegates[index].DelegateAddress {
+                       return errors.New("The delegates address is not correct in block")
                }
+       }
 
-               // verify signerqueue
-               if height%d.config.MaxSignerCount == 0 {
-                       err := snap.verifySignerQueue(currentHeaderExtra.SignerQueue)
-                       if err != nil {
-                               return err
-                       }
+       return nil
+}
 
+func (d *DposType) ProcessIrreversibleBlock(height uint64, hash bc.Hash) {
+       d.lockIrreversibleBlockInfo.Lock()
+       defer d.lockIrreversibleBlockInfo.Unlock()
+       i := 0
+       for i = maxConfirmBlockCount - 1; i >= 0; i-- {
+               if d.irreversibleBlockInfo.heights[i] < 0 || int64(height) < d.irreversibleBlockInfo.heights[i] {
+                       d.irreversibleBlockInfo.heights[i] = -1
                } else {
-                       for i := 0; i < int(d.config.MaxSignerCount); i++ {
-                               if parentHeaderExtra.SignerQueue[i] != currentHeaderExtra.SignerQueue[i] {
-                                       return errInvalidSignerQueue
+                       level := (height - uint64(d.irreversibleBlockInfo.heights[i])) * 100
+                       if level >= d.MaxDelegateNumber*d.firstIrreversibleThreshold {
+                               d.AddIrreversibleBlock(int64(height), hash)
+                       } else if level >= d.MaxDelegateNumber*d.secondIrreversibleThreshold {
+                               if i == maxConfirmBlockCount-1 {
+                                       d.AddIrreversibleBlock(int64(height), hash)
+                                       for k := 0; k < maxConfirmBlockCount-1; k++ {
+                                               d.irreversibleBlockInfo.heights[k] = d.irreversibleBlockInfo.heights[k+1]
+                                               d.irreversibleBlockInfo.hashs[k] = d.irreversibleBlockInfo.hashs[k+1]
+                                       }
+                                       d.irreversibleBlockInfo.heights[i] = int64(height)
+                                       d.irreversibleBlockInfo.hashs[i] = hash
+                                       return
+                               } else {
+                                       d.irreversibleBlockInfo.heights[i+1] = int64(height)
+                                       d.irreversibleBlockInfo.hashs[i+1] = hash
+                                       return
                                }
+
                        }
-               }
-               // verify missing signer for punish
-               parentSignerMissing := getSignerMissing(parentCoinbase.EncodeAddress(), currentCoinbase.EncodeAddress(), parentHeaderExtra)
-               if len(parentSignerMissing) != len(currentHeaderExtra.SignerMissing) {
-                       return errPunishedMissing
-               }
-               for i, signerMissing := range currentHeaderExtra.SignerMissing {
-                       if parentSignerMissing[i] != signerMissing {
-                               return errPunishedMissing
+                       for k := 0; k < maxConfirmBlockCount; k++ {
+                               d.irreversibleBlockInfo.heights[k] = -1
                        }
-               }
+                       d.irreversibleBlockInfo.heights[0] = int64(height)
+                       d.irreversibleBlockInfo.hashs[0] = hash
+                       return
 
+               }
        }
-
-       if !snap.inturn(signer, header.Timestamp) {
-               return errUnauthorized
+       if i < 0 {
+               d.irreversibleBlockInfo.heights[0] = int64(height)
+               d.irreversibleBlockInfo.hashs[0] = hash
        }
-
-       return nil
 }
 
-// Prepare implements consensus.Engine, preparing all the consensus fields of the header for running the transactions on top.
-func (d *Dpos) Prepare(c chain.Chain, header *types.BlockHeader) error {
-       if d.config.GenesisTimestamp < uint64(time.Now().Unix()) {
-               return nil
-       }
+func (d *DposType) getBlockForgerAddress(block types.Block) common.Address {
+       tx := block.Transactions[0].TxData
 
-       if header.Height == 1 {
-               for {
-                       delay := time.Unix(int64(d.config.GenesisTimestamp-2), 0).Sub(time.Now())
-                       if delay <= time.Duration(0) {
-                               log.WithFields(log.Fields{"module": module, "time": time.Now()}).Info("Ready for seal block")
-                               break
-                       } else if delay > time.Duration(d.config.Period)*time.Second {
-                               delay = time.Duration(d.config.Period) * time.Second
-                       }
-                       log.WithFields(log.Fields{"module": module, "delay": time.Duration(time.Unix(int64(d.config.GenesisTimestamp-2), 0).Sub(time.Now()))}).Info("Waiting for seal block")
-                       select {
-                       case <-time.After(delay):
-                               continue
+       if len(tx.Inputs) == 1 && tx.Inputs[0].InputType() == types.CoinbaseInputType {
+               address, err := common.NewAddressWitnessPubKeyHash(tx.Outputs[0].ControlProgram[2:], &consensus.ActiveNetParams)
+               if err != nil {
+                       address, err := common.NewAddressWitnessScriptHash(tx.Outputs[0].ControlProgram[2:], &consensus.ActiveNetParams)
+                       if err != nil {
+                               return nil
                        }
+                       return address
                }
+               return address
        }
+
        return nil
 }
 
-func (d *Dpos) Finalize(c chain.Chain, header *types.BlockHeader, txs []*bc.Tx) error {
-       height := header.Height
-       parent, err := c.GetHeaderByHeight(height - 1)
-       if parent == nil {
-               return err
-       }
-       //parent
-       var xpub chainkd.XPub
-       xpub.UnmarshalText(parent.Coinbase)
-       pubHash := crypto.Ripemd160(xpub.PublicKey())
-       parentCoinbase, err := common.NewAddressWitnessPubKeyHash(pubHash, &consensus.ActiveNetParams)
-       if err != nil {
-               return err
-       }
+func (d *DposType) IsValidBlockCheckIrreversibleBlock(height uint64, hash bc.Hash) error {
+       d.lockIrreversibleBlockInfo.Lock()
+       defer d.lockIrreversibleBlockInfo.Unlock()
 
-       //current
-       xpub.UnmarshalText(header.Coinbase)
-       pubHash = crypto.Ripemd160(xpub.PublicKey())
-       currentCoinbase, err := common.NewAddressWitnessPubKeyHash(pubHash, &consensus.ActiveNetParams)
-       if err != nil {
-               return err
+       if h, ok := d.irreversibleBlockInfo.HeightHash[int64(height)]; ok {
+               if h != hash {
+                       return fmt.Errorf("invalid block[%d:%s]", height, hash.String())
+               }
        }
 
-       //header.Timestamp
-       t := new(big.Int).Add(new(big.Int).SetUint64(parent.Timestamp), new(big.Int).SetUint64(d.config.Period))
-       header.Timestamp = t.Uint64()
-
-       if header.Timestamp < uint64(time.Now().Unix()) {
-               header.Timestamp = uint64(time.Now().Unix())
-       }
+       return nil
+}
 
-       if len(header.Extra) < extraVanity {
-               header.Extra = append(header.Extra, bytes.Repeat([]byte{0x00}, extraVanity-len(header.Extra))...)
+func (d *DposType) ReadIrreversibleBlockInfo(info *IrreversibleBlockInfo) error {
+       f, err := os.Open(d.irreversibleBlockFileName)
+       if err != nil {
+               return err
        }
-
-       header.Extra = header.Extra[:extraVanity]
-       // genesisVotes write direct into snapshot, which number is 1
-       var genesisVotes []*Vote
-       parentHeaderExtra := HeaderExtra{}
-       currentHeaderExtra := HeaderExtra{}
-       if height == 1 {
-               alreadyVote := make(map[string]struct{})
-               for _, voter := range d.config.SelfVoteSigners {
-                       if _, ok := alreadyVote[voter]; !ok {
-                               genesisVotes = append(genesisVotes, &Vote{
-                                       Voter:     voter,
-                                       Candidate: voter,
-                                       Stake:     0,
-                                       //Stake:     state.GetBalance(voter),
-                               })
-                               alreadyVote[voter] = struct{}{}
+       defer f.Close()
+       buf := bufio.NewReader(f)
+       for {
+               line, err := buf.ReadString('\n')
+               if err != nil {
+                       if err == io.EOF {
+                               return nil
                        }
+                       return err
                }
-       } else {
-               parentHeaderExtraByte := parent.Extra[extraVanity : len(parent.Extra)-extraSeal]
-               if err := json.Unmarshal(parentHeaderExtraByte, &parentHeaderExtra); err != nil {
+               line = strings.TrimSpace(line)
+               var height int64
+               var hashString string
+               n, err := fmt.Sscanf(line, "%d;%s\n", &height, &hashString)
+               if err != nil || n != 2 {
+                       return errors.New("parse error for ReadIrreversibleBlockInfo ")
+               }
+               var hash bc.Hash
+               if err := hash.UnmarshalText([]byte(hashString)); err != nil {
                        return err
                }
-               currentHeaderExtra.ConfirmedBlockNumber = parentHeaderExtra.ConfirmedBlockNumber
-               currentHeaderExtra.SignerQueue = parentHeaderExtra.SignerQueue
-               currentHeaderExtra.LoopStartTime = parentHeaderExtra.LoopStartTime
-               currentHeaderExtra.SignerMissing = getSignerMissing(parentCoinbase.EncodeAddress(), currentCoinbase.EncodeAddress(), parentHeaderExtra)
+               d.AddIrreversibleBlock(height, hash)
        }
+}
 
-       // calculate votes write into header.extra
-       currentHeaderExtra, err = d.processCustomTx(currentHeaderExtra, c, header, txs)
-       if err != nil {
-               return err
+type Int64Slice []int64
+
+func (a Int64Slice) Len() int {
+       return len(a)
+}
+func (a Int64Slice) Swap(i, j int) {
+       a[i], a[j] = a[j], a[i]
+}
+func (a Int64Slice) Less(i, j int) bool {
+       return a[i] < a[j]
+}
+
+func (d *DposType) WriteIrreversibleBlockInfo() error {
+       if len(d.irreversibleBlockInfo.HeightHash) == 0 {
+               return nil
        }
-       // Assemble the voting snapshot to check which votes make sense
-       snap, err := d.snapshot(c, height-1, header.PreviousBlockHash, nil, genesisVotes, defaultLoopCntRecalculateSigners)
+
+       f, err := os.Create(d.irreversibleBlockFileName)
        if err != nil {
                return err
        }
-
-       currentHeaderExtra.ConfirmedBlockNumber = snap.getLastConfirmedBlockNumber(currentHeaderExtra.CurrentBlockConfirmations).Uint64()
-       // write signerQueue in first header, from self vote signers in genesis block
-       if height == 1 {
-               currentHeaderExtra.LoopStartTime = d.config.GenesisTimestamp
-               for i := 0; i < int(d.config.MaxSignerCount); i++ {
-                       currentHeaderExtra.SignerQueue = append(currentHeaderExtra.SignerQueue, d.config.SelfVoteSigners[i%len(d.config.SelfVoteSigners)])
-               }
+       defer f.Close()
+       w := bufio.NewWriter(f)
+       var keys []int64
+       for k := range d.irreversibleBlockInfo.HeightHash {
+               keys = append(keys, k)
        }
-       if height%d.config.MaxSignerCount == 0 {
-               currentHeaderExtra.LoopStartTime = currentHeaderExtra.LoopStartTime + d.config.Period*d.config.MaxSignerCount
-               // create random signersQueue in currentHeaderExtra by snapshot.Tally
-               currentHeaderExtra.SignerQueue = []string{}
-               newSignerQueue, err := snap.createSignerQueue()
-               if err != nil {
-                       return err
-               }
 
-               currentHeaderExtra.SignerQueue = newSignerQueue
+       sort.Sort(Int64Slice(keys))
 
+       for _, k := range keys {
+               data, _ := d.irreversibleBlockInfo.HeightHash[k].MarshalText()
+               line := fmt.Sprintf("%d;%s\n", k, string(data))
+               w.WriteString(line)
        }
-       // encode header.extra
-       currentHeaderExtraEnc, err := json.Marshal(currentHeaderExtra)
-       if err != nil {
+
+       if err := w.Flush(); err != nil {
                return err
        }
-       header.Extra = append(header.Extra, currentHeaderExtraEnc...)
-       header.Extra = append(header.Extra, make([]byte, extraSeal)...)
+
        return nil
 }
 
-func (d *Dpos) Seal(c chain.Chain, block *types.Block) (*types.Block, error) {
-       header := block.BlockHeader
-       height := header.Height
-       if height == 0 {
-               return nil, errUnknownBlock
+func (d *DposType) AddIrreversibleBlock(height int64, hash bc.Hash) {
+       for k, _ := range d.irreversibleBlockInfo.HeightHash {
+               if len(d.irreversibleBlockInfo.HeightHash) > d.maxIrreversibleCount {
+                       delete(d.irreversibleBlockInfo.HeightHash, k)
+               } else {
+                       break
+               }
        }
+       d.irreversibleBlockInfo.HeightHash[height] = hash
+       d.vote.DeleteInvalidVote(uint64(height))
+}
 
-       if d.config.Period == 0 && len(block.Transactions) == 0 {
-               return nil, errWaitTransactions
-       }
-       // Bail out if we're unauthorized to sign a block
-       snap, err := d.snapshot(c, height-1, header.PreviousBlockHash, nil, nil, defaultLoopCntRecalculateSigners)
-       if err != nil {
-               return nil, err
-       }
-       if !snap.inturn(d.signer, header.Timestamp) {
-               return nil, errUnauthorized
-       }
+func (d *DposType) GetSuperForgerAddress() common.Address {
+       return d.superForgerAddress
+}
 
-       var xPrv chainkd.XPrv
-       if config.CommonConfig.Consensus.Dpos.XPrv == "" {
-               return nil, errors.New("Signer is empty")
-       }
-       xPrv.UnmarshalText([]byte(config.CommonConfig.Consensus.Dpos.XPrv))
-       sign := xPrv.Sign(block.BlockCommitment.TransactionsMerkleRoot.Bytes())
-       pubHash := crypto.Ripemd160(xPrv.XPub().PublicKey())
+func (d *DposType) GetIrreversibleBlock() {
 
-       control, err := vmutil.P2WPKHProgram([]byte(pubHash))
-       if err != nil {
-               return nil, err
-       }
+}
 
-       block.Proof = types.Proof{Sign: sign, ControlProgram: control}
-       return block, nil
+func (d *DposType) GetOldBlockHeight() uint64 {
+       return d.vote.GetOldBlockHeight()
 }
 
-func (d *Dpos) IsSealer(c chain.Chain, hash bc.Hash, header *types.BlockHeader, headerTime uint64) (bool, error) {
-       var (
-               snap    *Snapshot
-               headers []*types.BlockHeader
-       )
-       h := hash
-       height := header.Height
-       for snap == nil {
-               // If an in-memory snapshot was found, use that
-               if s, ok := d.recents.Get(h); ok {
-                       snap = s.(*Snapshot)
-                       break
-               }
-               // If an on-disk checkpoint snapshot can be found, use that
-               if height%checkpointInterval == 0 {
-                       if s, err := loadSnapshot(d.config, d.signatures, d.store, h); err == nil {
-                               log.WithFields(log.Fields{"func": "IsSealer", "number": height, "hash": h}).Warn("Loaded voting snapshot from disk")
-                               snap = s
-                               break
-                       } else {
-                               log.Warn("loadSnapshot:", err)
-                       }
-               }
+func (d *DposType) GetOldBlockHash() bc.Hash {
+       return d.vote.GetOldBlockHash()
+}
 
-               if height == 0 {
-                       genesis, err := c.GetHeaderByHeight(0)
-                       if err != nil {
-                               return false, err
-                       }
-                       var genesisVotes []*Vote
-                       alreadyVote := make(map[string]struct{})
-                       for _, voter := range d.config.SelfVoteSigners {
-                               if _, ok := alreadyVote[voter]; !ok {
-                                       genesisVotes = append(genesisVotes, &Vote{
-                                               Voter:     voter,
-                                               Candidate: voter,
-                                               Stake:     0,
-                                               //Stake:     state.GetBalance(voter),
-                                       })
-                                       alreadyVote[voter] = struct{}{}
-                               }
-                       }
-                       snap = newSnapshot(d.config, d.signatures, genesis.Hash(), genesisVotes, defaultLoopCntRecalculateSigners)
-                       if err := snap.store(d.store); err != nil {
-                               return false, err
-                       }
-                       log.Info("Stored genesis voting snapshot to disk")
-                       break
-               }
+func (d *DposType) ListDelegates() map[string]string {
+       return d.vote.ListDelegates()
+}
 
-               header, err := c.GetHeaderByHeight(height)
-               if header == nil || err != nil {
-                       return false, errors.New("unknown ancestor")
-               }
+func (d *DposType) GetDelegateVotes(delegate string) uint64 {
+       return d.vote.GetDelegateVotes(delegate)
+}
 
-               height, h = height-1, header.PreviousBlockHash
-       }
+func (d *DposType) GetDelegateVoters(delegate string) []string {
+       return d.vote.GetDelegateVoters(delegate)
+}
 
-       snap, err := snap.apply(headers)
-       if err != nil {
-               return false, err
-       }
+func (d *DposType) GetDelegate(name string) string {
+       return d.vote.GetDelegate(name)
 
-       d.recents.Add(snap.Hash, snap)
+}
 
-       if snap != nil {
-               loopIndex := int((headerTime-snap.LoopStartTime)/snap.config.Period) % len(snap.Signers)
-               if loopIndex >= len(snap.Signers) {
-                       return false, nil
-               } else if *snap.Signers[loopIndex] != d.signer {
-                       return false, nil
+func (d *DposType) GetDelegateName(address string) string {
+       return d.vote.GetDelegateName(address)
+}
 
-               }
-               return true, nil
-       } else {
-               return false, nil
-       }
+func (d *DposType) GetAddressBalance(address string) uint64 {
+       return d.vote.GetAddressBalance(address)
 }
 
-// snapshot retrieves the authorization snapshot at a given point in time.
-func (d *Dpos) snapshot(c chain.Chain, number uint64, hash bc.Hash, parents []*types.BlockHeader, genesisVotes []*Vote, lcrs uint64) (*Snapshot, error) {
+func (d *DposType) GetVotedDelegates(voter string) []string {
+       return d.vote.GetVotedDelegates(voter)
+}
 
-       var (
-               headers []*types.BlockHeader
-               snap    *Snapshot
-       )
-       h := hash
+func (d *DposType) HaveVote(voter, delegate string) bool {
+       return d.vote.HaveVote(voter, delegate)
+}
 
-       for snap == nil {
-               // If an in-memory snapshot was found, use that
-               if s, ok := d.recents.Get(h); ok {
-                       snap = s.(*Snapshot)
-                       break
-               }
-               // If an on-disk checkpoint snapshot can be found, use that
-               if number%checkpointInterval == 0 {
-                       if s, err := loadSnapshot(d.config, d.signatures, d.store, h); err == nil {
-                               log.WithFields(log.Fields{"number": number, "hash": h}).Warn("Loaded voting snapshot from disk")
-                               snap = s
-                               break
-                       }
-               }
-               if number == 0 {
-                       genesis, err := c.GetHeaderByHeight(0)
-                       if err != nil {
-                               return nil, err
-                       }
-                       if err := d.VerifySeal(c, genesis); err != nil {
-                               return nil, err
-                       }
+func (d *DposType) HaveDelegate(name, delegate string) bool {
+       return d.vote.HaveDelegate(name, delegate)
+}
 
-                       snap = newSnapshot(d.config, d.signatures, genesis.Hash(), genesisVotes, lcrs)
-                       if err := snap.store(d.store); err != nil {
-                               return nil, err
-                       }
-                       log.Info("Stored genesis voting snapshot to disk")
-                       break
-               }
-               var header *types.BlockHeader
-               if len(parents) > 0 {
-                       header = parents[len(parents)-1]
-                       if header.Hash() != h || header.Height != number {
-                               return nil, errors.New("unknown ancestor")
-                       }
-                       parents = parents[:len(parents)-1]
-               } else {
-                       var err error
-                       header, err = c.GetHeaderByHeight(number)
-                       if header == nil || err != nil {
-                               return nil, errors.New("unknown ancestor")
-                       }
-               }
-               headers = append(headers, header)
-               number, h = number-1, header.PreviousBlockHash
+func (d *DposType) Finish() error {
+       header := d.c.BestBlockHeader()
+       if err := d.vote.Store(header.Height, header.Hash()); err != nil {
+               return err
        }
 
-       // Previous snapshot found, apply any pending headers on top of it
-       for i := 0; i < len(headers)/2; i++ {
-               headers[i], headers[len(headers)-1-i] = headers[len(headers)-1-i], headers[i]
-       }
-       snap, err := snap.apply(headers)
-       if err != nil {
-               return nil, err
+       if err := d.WriteIrreversibleBlockInfo(); err != nil {
+               return err
        }
-       d.recents.Add(snap.Hash, snap)
 
-       // If we've generated a new checkpoint snapshot, save to disk
-       if snap.Number%checkpointInterval == 0 && len(headers) > 0 {
-               if err = snap.store(d.store); err != nil {
-                       return nil, err
-               }
-               log.Info("Stored voting snapshot to disk", "number", snap.Number, "hash", snap.Hash)
-       }
-       return snap, err
+       return nil
 }
 
-// Get the signer missing from last signer till header.Coinbase
-func getSignerMissing(lastSigner string, currentSigner string, extra HeaderExtra) []string {
+func SortDelegate(delegates []Delegate, t uint64) []Delegate {
+       var result []Delegate
+       r := getRand(uint64(len(delegates)), int64(t))
+       for _, i := range r {
+               result = append(result, delegates[i])
+       }
+       return result
+}
 
-       var signerMissing []string
-       recordMissing := false
-       for _, signer := range extra.SignerQueue {
-               if signer == lastSigner {
-                       recordMissing = true
+func getRand(num uint64, seed int64) []uint64 {
+       rand.Seed(seed)
+       var r []uint64
+       s := make(map[uint64]bool)
+       for {
+               v := rand.Uint64()
+               v %= num
+               if _, ok := s[v]; ok {
                        continue
                }
-               if signer == currentSigner {
+               s[v] = true
+               r = append(r, v)
+               if uint64(len(r)) >= num {
                        break
                }
-               if recordMissing {
-                       signerMissing = append(signerMissing, signer)
-               }
        }
-       return signerMissing
+
+       return r
 }