11 lru "github.com/hashicorp/golang-lru"
12 log "github.com/sirupsen/logrus"
13 "github.com/vapor/chain"
14 "github.com/vapor/common"
15 "github.com/vapor/config"
16 "github.com/vapor/consensus"
17 "github.com/vapor/crypto"
18 "github.com/vapor/crypto/ed25519/chainkd"
19 "github.com/vapor/protocol"
20 "github.com/vapor/protocol/bc"
21 "github.com/vapor/protocol/bc/types"
22 "github.com/vapor/protocol/vm/vmutil"
26 inMemorySnapshots = 128 // Number of recent vote snapshots to keep in memory
27 inMemorySignatures = 4096 // Number of recent block signatures to keep in memory
28 secondsPerYear = 365 * 24 * 3600 // Number of seconds for one year
29 checkpointInterval = 360 // About N hours if config.period is N
33 //delegated-proof-of-stake protocol constants.
35 SignerBlockReward = big.NewInt(5e+18) // Block reward in wei for successfully mining a block first year
36 defaultEpochLength = uint64(3000000) // Default number of blocks after which vote's period of validity
37 defaultBlockPeriod = uint64(3) // Default minimum difference between two consecutive block's timestamps
38 defaultMaxSignerCount = uint64(21) //
39 //defaultMinVoterBalance = new(big.Int).Mul(big.NewInt(10000), big.NewInt(1e+18))
40 defaultMinVoterBalance = uint64(0)
41 extraVanity = 32 // Fixed number of extra-data prefix bytes reserved for signer vanity
42 extraSeal = 65 // Fixed number of extra-data suffix bytes reserved for signer seal
43 defaultDifficulty = big.NewInt(1) // Default difficulty
44 defaultLoopCntRecalculateSigners = uint64(10) // Default loop count to recreate signers from top tally
45 minerRewardPerThousand = uint64(618) // Default reward for miner in each block from block reward (618/1000)
46 candidateNeedPD = false // is new candidate need Proposal & Declare process
50 // errUnknownBlock is returned when the list of signers is requested for a block
51 // that is not part of the local blockchain.
52 errUnknownBlock = errors.New("unknown block")
54 // errMissingVanity is returned if a block's extra-data section is shorter than
55 // 32 bytes, which is required to store the signer vanity.
56 errMissingVanity = errors.New("extra-data 32 byte vanity prefix missing")
58 // errMissingSignature is returned if a block's extra-data section doesn't seem
59 // to contain a 65 byte secp256k1 signature.
60 errMissingSignature = errors.New("extra-data 65 byte suffix signature missing")
62 // errInvalidMixDigest is returned if a block's mix digest is non-zero.
63 errInvalidMixDigest = errors.New("non-zero mix digest")
65 // errInvalidUncleHash is returned if a block contains an non-empty uncle list.
66 errInvalidUncleHash = errors.New("non empty uncle hash")
68 // ErrInvalidTimestamp is returned if the timestamp of a block is lower than
69 // the previous block's timestamp + the minimum block period.
70 ErrInvalidTimestamp = errors.New("invalid timestamp")
72 // errInvalidVotingChain is returned if an authorization list is attempted to
73 // be modified via out-of-range or non-contiguous headers.
74 errInvalidVotingChain = errors.New("invalid voting chain")
76 // errUnauthorized is returned if a header is signed by a non-authorized entity.
77 errUnauthorized = errors.New("unauthorized")
79 // errPunishedMissing is returned if a header calculate punished signer is wrong.
80 errPunishedMissing = errors.New("punished signer missing")
82 // errWaitTransactions is returned if an empty block is attempted to be sealed
83 // on an instant chain (0 second period). It's important to refuse these as the
84 // block reward is zero, so an empty block just bloats the chain... fast.
85 errWaitTransactions = errors.New("waiting for transactions")
87 // errUnclesNotAllowed is returned if uncles exists
88 errUnclesNotAllowed = errors.New("uncles not allowed")
90 // errCreateSignerQueueNotAllowed is returned if called in (block number + 1) % maxSignerCount != 0
91 errCreateSignerQueueNotAllowed = errors.New("create signer queue not allowed")
93 // errInvalidSignerQueue is returned if verify SignerQueue fail
94 errInvalidSignerQueue = errors.New("invalid signer queue")
96 // errSignerQueueEmpty is returned if no signer when calculate
97 errSignerQueueEmpty = errors.New("signer queue is empty")
101 config *config.DposConfig // Consensus engine configuration parameters
102 store protocol.Store // Database to store and retrieve snapshot checkpoints
103 recents *lru.ARCCache // Snapshots for recent block to speed up reorgs
104 signatures *lru.ARCCache // Signatures of recent blocks to speed up mining
105 signer string // Ethereum address of the signing key
106 signFn SignerFn // Signer function to authorize hashes with
107 signTxFn SignTxFn // Sign transaction function to sign tx
108 lock sync.RWMutex // Protects the signer fields
109 lcsc uint64 // Last confirmed side chain
112 // SignerFn is a signer callback function to request a hash to be signed by a backing account.
113 type SignerFn func(string, []byte) ([]byte, error)
115 // SignTxFn is a signTx
116 type SignTxFn func(string, *bc.Tx, *big.Int) (*bc.Tx, error)
119 func ecrecover(header *types.BlockHeader, sigcache *lru.ARCCache, c chain.Chain) (string, error) {
121 xpub := &chainkd.XPub{}
122 xpub.UnmarshalText(header.Coinbase)
123 derivedPK := xpub.PublicKey()
124 pubHash := crypto.Ripemd160(derivedPK)
125 address, err := common.NewAddressWitnessPubKeyHash(pubHash, &consensus.ActiveNetParams)
130 return address.EncodeAddress(), nil
134 func New(config *config.DposConfig, store protocol.Store) *Dpos {
137 conf.Epoch = defaultEpochLength
139 if conf.Period == 0 {
140 conf.Period = defaultBlockPeriod
142 if conf.MaxSignerCount == 0 {
143 conf.MaxSignerCount = defaultMaxSignerCount
145 if conf.MinVoterBalance == 0 {
146 conf.MinVoterBalance = defaultMinVoterBalance
148 // Allocate the snapshot caches and create the engine
149 recents, _ := lru.NewARC(inMemorySnapshots)
150 signatures, _ := lru.NewARC(inMemorySignatures)
155 signatures: signatures,
159 // Authorize injects a private key into the consensus engine to mint new blocks with.
160 func (d *Dpos) Authorize(signer string /*, signFn SignerFn*/) {
162 defer d.lock.Unlock()
168 // 从BLockHeader中获取到地址
169 func (d *Dpos) Author(header *types.BlockHeader, c chain.Chain) (string, error) {
170 return ecrecover(header, d.signatures, c)
173 func (d *Dpos) VerifyHeader(c chain.Chain, header *types.BlockHeader, seal bool) error {
174 return d.verifyCascadingFields(c, header, nil)
177 func (d *Dpos) VerifyHeaders(c chain.Chain, headers []*types.BlockHeader, seals []bool) (chan<- struct{}, <-chan error) {
181 func (d *Dpos) VerifySeal(c chain.Chain, header *types.BlockHeader) error {
185 func (d *Dpos) verifyHeader(c chain.Chain, header *types.BlockHeader, parents []*types.BlockHeader) error {
189 func (d *Dpos) verifyCascadingFields(c chain.Chain, header *types.BlockHeader, parents []*types.BlockHeader) error {
190 // The genesis block is the always valid dead-end
191 height := header.Height
197 parent *types.BlockHeader
201 if len(parents) > 0 {
202 parent = parents[len(parents)-1]
204 parent, err = c.GetHeaderByHeight(height - 1)
211 return errors.New("unknown ancestor")
214 if _, err = d.snapshot(c, height-1, header.PreviousBlockHash, parents, nil, defaultLoopCntRecalculateSigners); err != nil {
218 return d.verifySeal(c, header, parents)
221 // verifySeal checks whether the signature contained in the header satisfies the
222 // consensus protocol requirements. The method accepts an optional list of parent
223 // headers that aren't yet part of the local blockchain to generate the snapshots
225 func (d *Dpos) verifySeal(c chain.Chain, header *types.BlockHeader, parents []*types.BlockHeader) error {
226 height := header.Height
228 return errUnknownBlock
230 // Retrieve the snapshot needed to verify this header and cache it
231 snap, err := d.snapshot(c, height-1, header.PreviousBlockHash, parents, nil, defaultLoopCntRecalculateSigners)
236 // Resolve the authorization key and check against signers
237 signer, err := ecrecover(header, d.signatures, c)
242 if height > d.config.MaxSignerCount {
244 parent *types.BlockHeader
247 if len(parents) > 0 {
248 parent = parents[len(parents)-1]
250 if parent, err = c.GetHeaderByHeight(height - 1); err != nil {
256 xpub := &chainkd.XPub{}
257 xpub.UnmarshalText(parent.Coinbase)
258 derivedPK := xpub.PublicKey()
259 pubHash := crypto.Ripemd160(derivedPK)
260 parentCoinbase, err := common.NewAddressWitnessPubKeyHash(pubHash, &consensus.ActiveNetParams)
266 xpub.UnmarshalText(header.Coinbase)
267 derivedPK = xpub.PublicKey()
268 pubHash = crypto.Ripemd160(derivedPK)
269 currentCoinbase, err := common.NewAddressWitnessPubKeyHash(pubHash, &consensus.ActiveNetParams)
274 parentHeaderExtra := HeaderExtra{}
275 if err = json.Unmarshal(parent.Extra[extraVanity:len(parent.Extra)-extraSeal], &parentHeaderExtra); err != nil {
279 currentHeaderExtra := HeaderExtra{}
280 if err = json.Unmarshal(header.Extra[extraVanity:len(header.Extra)-extraSeal], ¤tHeaderExtra); err != nil {
284 // verify signerqueue
285 if height%d.config.MaxSignerCount == 0 {
286 err := snap.verifySignerQueue(currentHeaderExtra.SignerQueue)
292 for i := 0; i < int(d.config.MaxSignerCount); i++ {
293 if parentHeaderExtra.SignerQueue[i] != currentHeaderExtra.SignerQueue[i] {
294 return errInvalidSignerQueue
298 // verify missing signer for punish
299 parentSignerMissing := getSignerMissing(parentCoinbase.EncodeAddress(), currentCoinbase.EncodeAddress(), parentHeaderExtra)
300 if len(parentSignerMissing) != len(currentHeaderExtra.SignerMissing) {
301 return errPunishedMissing
303 for i, signerMissing := range currentHeaderExtra.SignerMissing {
304 if parentSignerMissing[i] != signerMissing {
305 return errPunishedMissing
311 if !snap.inturn(signer, header.Timestamp) {
312 return errUnauthorized
318 // Prepare implements consensus.Engine, preparing all the consensus fields of the header for running the transactions on top.
319 func (d *Dpos) Prepare(c chain.Chain, header *types.BlockHeader) error {
320 if d.config.GenesisTimestamp < uint64(time.Now().Unix()) {
324 if header.Height == 1 {
326 delay := time.Unix(int64(d.config.GenesisTimestamp-2), 0).Sub(time.Now())
327 if delay <= time.Duration(0) {
328 log.WithFields(log.Fields{"module": module, "time": time.Now()}).Info("Ready for seal block")
330 } else if delay > time.Duration(d.config.Period)*time.Second {
331 delay = time.Duration(d.config.Period) * time.Second
333 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")
335 case <-time.After(delay):
343 func (d *Dpos) Finalize(c chain.Chain, header *types.BlockHeader, txs []*bc.Tx) error {
344 height := header.Height
345 parent, err := c.GetHeaderByHeight(height - 1)
350 var xpub chainkd.XPub
351 xpub.UnmarshalText(parent.Coinbase)
352 pubHash := crypto.Ripemd160(xpub.PublicKey())
353 parentCoinbase, err := common.NewAddressWitnessPubKeyHash(pubHash, &consensus.ActiveNetParams)
359 xpub.UnmarshalText(header.Coinbase)
360 pubHash = crypto.Ripemd160(xpub.PublicKey())
361 currentCoinbase, err := common.NewAddressWitnessPubKeyHash(pubHash, &consensus.ActiveNetParams)
367 t := new(big.Int).Add(new(big.Int).SetUint64(parent.Timestamp), new(big.Int).SetUint64(d.config.Period))
368 header.Timestamp = t.Uint64()
370 if header.Timestamp < uint64(time.Now().Unix()) {
371 header.Timestamp = uint64(time.Now().Unix())
374 if len(header.Extra) < extraVanity {
375 header.Extra = append(header.Extra, bytes.Repeat([]byte{0x00}, extraVanity-len(header.Extra))...)
378 header.Extra = header.Extra[:extraVanity]
379 // genesisVotes write direct into snapshot, which number is 1
380 var genesisVotes []*Vote
381 parentHeaderExtra := HeaderExtra{}
382 currentHeaderExtra := HeaderExtra{}
384 alreadyVote := make(map[string]struct{})
385 for _, voter := range d.config.SelfVoteSigners {
386 if _, ok := alreadyVote[voter]; !ok {
387 genesisVotes = append(genesisVotes, &Vote{
391 //Stake: state.GetBalance(voter),
393 alreadyVote[voter] = struct{}{}
397 parentHeaderExtraByte := parent.Extra[extraVanity : len(parent.Extra)-extraSeal]
398 if err := json.Unmarshal(parentHeaderExtraByte, &parentHeaderExtra); err != nil {
401 currentHeaderExtra.ConfirmedBlockNumber = parentHeaderExtra.ConfirmedBlockNumber
402 currentHeaderExtra.SignerQueue = parentHeaderExtra.SignerQueue
403 currentHeaderExtra.LoopStartTime = parentHeaderExtra.LoopStartTime
404 currentHeaderExtra.SignerMissing = getSignerMissing(parentCoinbase.EncodeAddress(), currentCoinbase.EncodeAddress(), parentHeaderExtra)
407 // calculate votes write into header.extra
408 currentHeaderExtra, err = d.processCustomTx(currentHeaderExtra, c, header, txs)
412 // Assemble the voting snapshot to check which votes make sense
413 snap, err := d.snapshot(c, height-1, header.PreviousBlockHash, nil, genesisVotes, defaultLoopCntRecalculateSigners)
418 currentHeaderExtra.ConfirmedBlockNumber = snap.getLastConfirmedBlockNumber(currentHeaderExtra.CurrentBlockConfirmations).Uint64()
419 // write signerQueue in first header, from self vote signers in genesis block
421 currentHeaderExtra.LoopStartTime = d.config.GenesisTimestamp
422 for i := 0; i < int(d.config.MaxSignerCount); i++ {
423 currentHeaderExtra.SignerQueue = append(currentHeaderExtra.SignerQueue, d.config.SelfVoteSigners[i%len(d.config.SelfVoteSigners)])
426 if height%d.config.MaxSignerCount == 0 {
427 //currentHeaderExtra.LoopStartTime = header.Time.Uint64()
428 currentHeaderExtra.LoopStartTime = currentHeaderExtra.LoopStartTime + d.config.Period*d.config.MaxSignerCount
429 // create random signersQueue in currentHeaderExtra by snapshot.Tally
430 currentHeaderExtra.SignerQueue = []string{}
431 newSignerQueue, err := snap.createSignerQueue()
436 currentHeaderExtra.SignerQueue = newSignerQueue
439 // encode header.extra
440 currentHeaderExtraEnc, err := json.Marshal(currentHeaderExtra)
444 header.Extra = append(header.Extra, currentHeaderExtraEnc...)
445 header.Extra = append(header.Extra, make([]byte, extraSeal)...)
449 func (d *Dpos) Seal(c chain.Chain, block *types.Block) (*types.Block, error) {
450 header := block.BlockHeader
451 height := header.Height
453 return nil, errUnknownBlock
456 if d.config.Period == 0 && len(block.Transactions) == 0 {
457 return nil, errWaitTransactions
459 // Bail out if we're unauthorized to sign a block
460 snap, err := d.snapshot(c, height-1, header.PreviousBlockHash, nil, nil, defaultLoopCntRecalculateSigners)
464 if !snap.inturn(d.signer, header.Timestamp) {
465 return nil, errUnauthorized
468 var xPrv chainkd.XPrv
469 if config.CommonConfig.Consensus.Dpos.XPrv == "" {
470 return nil, errors.New("Signer is empty")
472 xPrv.UnmarshalText([]byte(config.CommonConfig.Consensus.Dpos.XPrv))
473 sign := xPrv.Sign(block.BlockCommitment.TransactionsMerkleRoot.Bytes())
474 pubHash := crypto.Ripemd160(xPrv.XPub().PublicKey())
476 control, err := vmutil.P2WPKHProgram([]byte(pubHash))
481 block.Proof = types.Proof{Sign: sign, ControlProgram: control}
485 func (d *Dpos) IsSealer(c chain.Chain, hash bc.Hash, header *types.BlockHeader, headerTime uint64) (bool, error) {
488 headers []*types.BlockHeader
491 height := header.Height
493 // If an in-memory snapshot was found, use that
494 if s, ok := d.recents.Get(h); ok {
498 // If an on-disk checkpoint snapshot can be found, use that
499 if height%checkpointInterval == 0 {
500 if s, err := loadSnapshot(d.config, d.signatures, d.store, h); err == nil {
501 log.WithFields(log.Fields{"func": "IsSealer", "number": height, "hash": h}).Warn("Loaded voting snapshot from disk")
505 log.Warn("loadSnapshot:", err)
510 genesis, err := c.GetHeaderByHeight(0)
514 var genesisVotes []*Vote
515 alreadyVote := make(map[string]struct{})
516 for _, voter := range d.config.SelfVoteSigners {
517 if _, ok := alreadyVote[voter]; !ok {
518 genesisVotes = append(genesisVotes, &Vote{
522 //Stake: state.GetBalance(voter),
524 alreadyVote[voter] = struct{}{}
527 snap = newSnapshot(d.config, d.signatures, genesis.Hash(), genesisVotes, defaultLoopCntRecalculateSigners)
528 if err := snap.store(d.store); err != nil {
531 log.Info("Stored genesis voting snapshot to disk")
535 header, err := c.GetHeaderByHeight(height)
536 if header == nil || err != nil {
537 return false, errors.New("unknown ancestor")
540 height, h = height-1, header.PreviousBlockHash
543 snap, err := snap.apply(headers)
548 d.recents.Add(snap.Hash, snap)
551 loopIndex := int((headerTime-snap.LoopStartTime)/snap.config.Period) % len(snap.Signers)
552 if loopIndex >= len(snap.Signers) {
554 } else if *snap.Signers[loopIndex] != d.signer {
564 // snapshot retrieves the authorization snapshot at a given point in time.
565 func (d *Dpos) snapshot(c chain.Chain, number uint64, hash bc.Hash, parents []*types.BlockHeader, genesisVotes []*Vote, lcrs uint64) (*Snapshot, error) {
568 headers []*types.BlockHeader
574 // If an in-memory snapshot was found, use that
575 if s, ok := d.recents.Get(h); ok {
579 // If an on-disk checkpoint snapshot can be found, use that
580 if number%checkpointInterval == 0 {
581 if s, err := loadSnapshot(d.config, d.signatures, d.store, h); err == nil {
582 log.WithFields(log.Fields{"number": number, "hash": h}).Warn("Loaded voting snapshot from disk")
588 genesis, err := c.GetHeaderByHeight(0)
592 if err := d.VerifyHeader(c, genesis, false); err != nil {
596 snap = newSnapshot(d.config, d.signatures, genesis.Hash(), genesisVotes, lcrs)
597 if err := snap.store(d.store); err != nil {
600 log.Info("Stored genesis voting snapshot to disk")
603 var header *types.BlockHeader
604 if len(parents) > 0 {
605 header = parents[len(parents)-1]
606 if header.Hash() != h || header.Height != number {
607 return nil, errors.New("unknown ancestor")
609 parents = parents[:len(parents)-1]
612 header, err = c.GetHeaderByHeight(number)
613 if header == nil || err != nil {
614 return nil, errors.New("unknown ancestor")
617 headers = append(headers, header)
618 number, h = number-1, header.PreviousBlockHash
621 // Previous snapshot found, apply any pending headers on top of it
622 for i := 0; i < len(headers)/2; i++ {
623 headers[i], headers[len(headers)-1-i] = headers[len(headers)-1-i], headers[i]
625 snap, err := snap.apply(headers)
629 d.recents.Add(snap.Hash, snap)
631 // If we've generated a new checkpoint snapshot, save to disk
632 if snap.Number%checkpointInterval == 0 && len(headers) > 0 {
633 if err = snap.store(d.store); err != nil {
636 log.Info("Stored voting snapshot to disk", "number", snap.Number, "hash", snap.Hash)
641 // Get the signer missing from last signer till header.Coinbase
642 func getSignerMissing(lastSigner string, currentSigner string, extra HeaderExtra) []string {
644 var signerMissing []string
645 recordMissing := false
646 for _, signer := range extra.SignerQueue {
647 if signer == lastSigner {
651 if signer == currentSigner {
655 signerMissing = append(signerMissing, signer)