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 lock sync.RWMutex // Protects the signer fields
107 lcsc uint64 // Last confirmed side chain
111 func ecrecover(header *types.BlockHeader, sigcache *lru.ARCCache, c chain.Chain) (string, error) {
112 xpub := &chainkd.XPub{}
113 xpub.UnmarshalText(header.Coinbase)
114 derivedPK := xpub.PublicKey()
115 pubHash := crypto.Ripemd160(derivedPK)
116 address, err := common.NewAddressWitnessPubKeyHash(pubHash, &consensus.ActiveNetParams)
121 return address.EncodeAddress(), nil
125 func New(config *config.DposConfig, store protocol.Store) *Dpos {
128 conf.Epoch = defaultEpochLength
130 if conf.Period == 0 {
131 conf.Period = defaultBlockPeriod
133 if conf.MaxSignerCount == 0 {
134 conf.MaxSignerCount = defaultMaxSignerCount
136 if conf.MinVoterBalance == 0 {
137 conf.MinVoterBalance = defaultMinVoterBalance
139 // Allocate the snapshot caches and create the engine
140 recents, _ := lru.NewARC(inMemorySnapshots)
141 signatures, _ := lru.NewARC(inMemorySignatures)
146 signatures: signatures,
150 // Authorize injects a private key into the consensus engine to mint new blocks with.
151 func (d *Dpos) Authorize(signer string) {
153 defer d.lock.Unlock()
157 func (d *Dpos) VerifySeal(c chain.Chain, header *types.BlockHeader) error {
158 return d.verifyCascadingFields(c, header, nil)
161 func (d *Dpos) verifyCascadingFields(c chain.Chain, header *types.BlockHeader, parents []*types.BlockHeader) error {
162 // The genesis block is the always valid dead-end
163 height := header.Height
169 parent *types.BlockHeader
173 if len(parents) > 0 {
174 parent = parents[len(parents)-1]
176 parent, err = c.GetHeaderByHeight(height - 1)
183 return errors.New("unknown ancestor")
186 if _, err = d.snapshot(c, height-1, header.PreviousBlockHash, parents, nil, defaultLoopCntRecalculateSigners); err != nil {
190 return d.verifySeal(c, header, parents)
193 // verifySeal checks whether the signature contained in the header satisfies the
194 // consensus protocol requirements. The method accepts an optional list of parent
195 // headers that aren't yet part of the local blockchain to generate the snapshots
197 func (d *Dpos) verifySeal(c chain.Chain, header *types.BlockHeader, parents []*types.BlockHeader) error {
198 height := header.Height
200 return errUnknownBlock
202 // Retrieve the snapshot needed to verify this header and cache it
203 snap, err := d.snapshot(c, height-1, header.PreviousBlockHash, parents, nil, defaultLoopCntRecalculateSigners)
210 if height > d.config.MaxSignerCount {
212 parent *types.BlockHeader
215 if len(parents) > 0 {
216 parent = parents[len(parents)-1]
218 if parent, err = c.GetHeaderByHeight(height - 1); err != nil {
224 xpub := &chainkd.XPub{}
225 xpub.UnmarshalText(parent.Coinbase)
226 derivedPK := xpub.PublicKey()
227 pubHash := crypto.Ripemd160(derivedPK)
228 parentCoinbase, err := common.NewAddressWitnessPubKeyHash(pubHash, &consensus.ActiveNetParams)
234 xpub.UnmarshalText(header.Coinbase)
235 derivedPK = xpub.PublicKey()
236 pubHash = crypto.Ripemd160(derivedPK)
237 currentCoinbase, err := common.NewAddressWitnessPubKeyHash(pubHash, &consensus.ActiveNetParams)
241 signer = currentCoinbase.EncodeAddress()
243 parentHeaderExtra := HeaderExtra{}
244 if err = json.Unmarshal(parent.Extra[extraVanity:len(parent.Extra)-extraSeal], &parentHeaderExtra); err != nil {
248 currentHeaderExtra := HeaderExtra{}
249 if err = json.Unmarshal(header.Extra[extraVanity:len(header.Extra)-extraSeal], ¤tHeaderExtra); err != nil {
253 // verify signerqueue
254 if height%d.config.MaxSignerCount == 0 {
255 err := snap.verifySignerQueue(currentHeaderExtra.SignerQueue)
261 for i := 0; i < int(d.config.MaxSignerCount); i++ {
262 if parentHeaderExtra.SignerQueue[i] != currentHeaderExtra.SignerQueue[i] {
263 return errInvalidSignerQueue
267 // verify missing signer for punish
268 parentSignerMissing := getSignerMissing(parentCoinbase.EncodeAddress(), currentCoinbase.EncodeAddress(), parentHeaderExtra)
269 if len(parentSignerMissing) != len(currentHeaderExtra.SignerMissing) {
270 return errPunishedMissing
272 for i, signerMissing := range currentHeaderExtra.SignerMissing {
273 if parentSignerMissing[i] != signerMissing {
274 return errPunishedMissing
280 if !snap.inturn(signer, header.Timestamp) {
281 return errUnauthorized
287 // Prepare implements consensus.Engine, preparing all the consensus fields of the header for running the transactions on top.
288 func (d *Dpos) Prepare(c chain.Chain, header *types.BlockHeader) error {
289 if d.config.GenesisTimestamp < uint64(time.Now().Unix()) {
293 if header.Height == 1 {
295 delay := time.Unix(int64(d.config.GenesisTimestamp-2), 0).Sub(time.Now())
296 if delay <= time.Duration(0) {
297 log.WithFields(log.Fields{"module": module, "time": time.Now()}).Info("Ready for seal block")
299 } else if delay > time.Duration(d.config.Period)*time.Second {
300 delay = time.Duration(d.config.Period) * time.Second
302 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")
304 case <-time.After(delay):
312 func (d *Dpos) Finalize(c chain.Chain, header *types.BlockHeader, txs []*bc.Tx) error {
313 height := header.Height
314 parent, err := c.GetHeaderByHeight(height - 1)
319 var xpub chainkd.XPub
320 xpub.UnmarshalText(parent.Coinbase)
321 pubHash := crypto.Ripemd160(xpub.PublicKey())
322 parentCoinbase, err := common.NewAddressWitnessPubKeyHash(pubHash, &consensus.ActiveNetParams)
328 xpub.UnmarshalText(header.Coinbase)
329 pubHash = crypto.Ripemd160(xpub.PublicKey())
330 currentCoinbase, err := common.NewAddressWitnessPubKeyHash(pubHash, &consensus.ActiveNetParams)
336 t := new(big.Int).Add(new(big.Int).SetUint64(parent.Timestamp), new(big.Int).SetUint64(d.config.Period))
337 header.Timestamp = t.Uint64()
339 if header.Timestamp < uint64(time.Now().Unix()) {
340 header.Timestamp = uint64(time.Now().Unix())
343 if len(header.Extra) < extraVanity {
344 header.Extra = append(header.Extra, bytes.Repeat([]byte{0x00}, extraVanity-len(header.Extra))...)
347 header.Extra = header.Extra[:extraVanity]
348 // genesisVotes write direct into snapshot, which number is 1
349 var genesisVotes []*Vote
350 parentHeaderExtra := HeaderExtra{}
351 currentHeaderExtra := HeaderExtra{}
353 alreadyVote := make(map[string]struct{})
354 for _, voter := range d.config.SelfVoteSigners {
355 if _, ok := alreadyVote[voter]; !ok {
356 genesisVotes = append(genesisVotes, &Vote{
360 //Stake: state.GetBalance(voter),
362 alreadyVote[voter] = struct{}{}
366 parentHeaderExtraByte := parent.Extra[extraVanity : len(parent.Extra)-extraSeal]
367 if err := json.Unmarshal(parentHeaderExtraByte, &parentHeaderExtra); err != nil {
370 currentHeaderExtra.ConfirmedBlockNumber = parentHeaderExtra.ConfirmedBlockNumber
371 currentHeaderExtra.SignerQueue = parentHeaderExtra.SignerQueue
372 currentHeaderExtra.LoopStartTime = parentHeaderExtra.LoopStartTime
373 currentHeaderExtra.SignerMissing = getSignerMissing(parentCoinbase.EncodeAddress(), currentCoinbase.EncodeAddress(), parentHeaderExtra)
376 // calculate votes write into header.extra
377 currentHeaderExtra, err = d.processCustomTx(currentHeaderExtra, c, header, txs)
381 // Assemble the voting snapshot to check which votes make sense
382 snap, err := d.snapshot(c, height-1, header.PreviousBlockHash, nil, genesisVotes, defaultLoopCntRecalculateSigners)
387 currentHeaderExtra.ConfirmedBlockNumber = snap.getLastConfirmedBlockNumber(currentHeaderExtra.CurrentBlockConfirmations).Uint64()
388 // write signerQueue in first header, from self vote signers in genesis block
390 currentHeaderExtra.LoopStartTime = d.config.GenesisTimestamp
391 for i := 0; i < int(d.config.MaxSignerCount); i++ {
392 currentHeaderExtra.SignerQueue = append(currentHeaderExtra.SignerQueue, d.config.SelfVoteSigners[i%len(d.config.SelfVoteSigners)])
395 if height%d.config.MaxSignerCount == 0 {
396 currentHeaderExtra.LoopStartTime = currentHeaderExtra.LoopStartTime + d.config.Period*d.config.MaxSignerCount
397 // create random signersQueue in currentHeaderExtra by snapshot.Tally
398 currentHeaderExtra.SignerQueue = []string{}
399 newSignerQueue, err := snap.createSignerQueue()
404 currentHeaderExtra.SignerQueue = newSignerQueue
407 // encode header.extra
408 currentHeaderExtraEnc, err := json.Marshal(currentHeaderExtra)
412 header.Extra = append(header.Extra, currentHeaderExtraEnc...)
413 header.Extra = append(header.Extra, make([]byte, extraSeal)...)
417 func (d *Dpos) Seal(c chain.Chain, block *types.Block) (*types.Block, error) {
418 header := block.BlockHeader
419 height := header.Height
421 return nil, errUnknownBlock
424 if d.config.Period == 0 && len(block.Transactions) == 0 {
425 return nil, errWaitTransactions
427 // Bail out if we're unauthorized to sign a block
428 snap, err := d.snapshot(c, height-1, header.PreviousBlockHash, nil, nil, defaultLoopCntRecalculateSigners)
432 if !snap.inturn(d.signer, header.Timestamp) {
433 return nil, errUnauthorized
436 var xPrv chainkd.XPrv
437 if config.CommonConfig.Consensus.Dpos.XPrv == "" {
438 return nil, errors.New("Signer is empty")
440 xPrv.UnmarshalText([]byte(config.CommonConfig.Consensus.Dpos.XPrv))
441 sign := xPrv.Sign(block.BlockCommitment.TransactionsMerkleRoot.Bytes())
442 pubHash := crypto.Ripemd160(xPrv.XPub().PublicKey())
444 control, err := vmutil.P2WPKHProgram([]byte(pubHash))
449 block.Proof = types.Proof{Sign: sign, ControlProgram: control}
453 func (d *Dpos) IsSealer(c chain.Chain, hash bc.Hash, header *types.BlockHeader, headerTime uint64) (bool, error) {
456 headers []*types.BlockHeader
459 height := header.Height
461 // If an in-memory snapshot was found, use that
462 if s, ok := d.recents.Get(h); ok {
466 // If an on-disk checkpoint snapshot can be found, use that
467 if height%checkpointInterval == 0 {
468 if s, err := loadSnapshot(d.config, d.signatures, d.store, h); err == nil {
469 log.WithFields(log.Fields{"func": "IsSealer", "number": height, "hash": h}).Warn("Loaded voting snapshot from disk")
473 log.Warn("loadSnapshot:", err)
478 genesis, err := c.GetHeaderByHeight(0)
482 var genesisVotes []*Vote
483 alreadyVote := make(map[string]struct{})
484 for _, voter := range d.config.SelfVoteSigners {
485 if _, ok := alreadyVote[voter]; !ok {
486 genesisVotes = append(genesisVotes, &Vote{
490 //Stake: state.GetBalance(voter),
492 alreadyVote[voter] = struct{}{}
495 snap = newSnapshot(d.config, d.signatures, genesis.Hash(), genesisVotes, defaultLoopCntRecalculateSigners)
496 if err := snap.store(d.store); err != nil {
499 log.Info("Stored genesis voting snapshot to disk")
503 header, err := c.GetHeaderByHeight(height)
504 if header == nil || err != nil {
505 return false, errors.New("unknown ancestor")
508 height, h = height-1, header.PreviousBlockHash
511 snap, err := snap.apply(headers)
516 d.recents.Add(snap.Hash, snap)
519 loopIndex := int((headerTime-snap.LoopStartTime)/snap.config.Period) % len(snap.Signers)
520 if loopIndex >= len(snap.Signers) {
522 } else if *snap.Signers[loopIndex] != d.signer {
532 // snapshot retrieves the authorization snapshot at a given point in time.
533 func (d *Dpos) snapshot(c chain.Chain, number uint64, hash bc.Hash, parents []*types.BlockHeader, genesisVotes []*Vote, lcrs uint64) (*Snapshot, error) {
536 headers []*types.BlockHeader
542 // If an in-memory snapshot was found, use that
543 if s, ok := d.recents.Get(h); ok {
547 // If an on-disk checkpoint snapshot can be found, use that
548 if number%checkpointInterval == 0 {
549 if s, err := loadSnapshot(d.config, d.signatures, d.store, h); err == nil {
550 log.WithFields(log.Fields{"number": number, "hash": h}).Warn("Loaded voting snapshot from disk")
556 genesis, err := c.GetHeaderByHeight(0)
560 if err := d.VerifySeal(c, genesis); err != nil {
564 snap = newSnapshot(d.config, d.signatures, genesis.Hash(), genesisVotes, lcrs)
565 if err := snap.store(d.store); err != nil {
568 log.Info("Stored genesis voting snapshot to disk")
571 var header *types.BlockHeader
572 if len(parents) > 0 {
573 header = parents[len(parents)-1]
574 if header.Hash() != h || header.Height != number {
575 return nil, errors.New("unknown ancestor")
577 parents = parents[:len(parents)-1]
580 header, err = c.GetHeaderByHeight(number)
581 if header == nil || err != nil {
582 return nil, errors.New("unknown ancestor")
585 headers = append(headers, header)
586 number, h = number-1, header.PreviousBlockHash
589 // Previous snapshot found, apply any pending headers on top of it
590 for i := 0; i < len(headers)/2; i++ {
591 headers[i], headers[len(headers)-1-i] = headers[len(headers)-1-i], headers[i]
593 snap, err := snap.apply(headers)
597 d.recents.Add(snap.Hash, snap)
599 // If we've generated a new checkpoint snapshot, save to disk
600 if snap.Number%checkpointInterval == 0 && len(headers) > 0 {
601 if err = snap.store(d.store); err != nil {
604 log.Info("Stored voting snapshot to disk", "number", snap.Number, "hash", snap.Hash)
609 // Get the signer missing from last signer till header.Coinbase
610 func getSignerMissing(lastSigner string, currentSigner string, extra HeaderExtra) []string {
612 var signerMissing []string
613 recordMissing := false
614 for _, signer := range extra.SignerQueue {
615 if signer == lastSigner {
619 if signer == currentSigner {
623 signerMissing = append(signerMissing, signer)