github.com/ebakus/go-ebakus@v1.0.5-0.20200520105415-dbccef9ec421/consensus/dpos/dpos.go (about) 1 // Copyright 2019 The ebakus/go-ebakus Authors 2 // This file is part of the ebakus/go-ebakus library. 3 // 4 // The ebakus/go-ebakus library is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU Lesser General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // The ebakus/go-ebakus library is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU Lesser General Public License for more details. 13 // 14 // You should have received a copy of the GNU Lesser General Public License 15 // along with the ebakus/go-ebakus library. If not, see <http://www.gnu.org/licenses/>. 16 17 package dpos 18 19 import ( 20 "bytes" 21 "encoding/binary" 22 "errors" 23 "fmt" 24 "io" 25 "math/big" 26 "math/bits" 27 "sync" 28 "time" 29 30 "github.com/ebakus/ebakusdb" 31 32 "github.com/ebakus/go-ebakus/accounts" 33 "github.com/ebakus/go-ebakus/common" 34 "github.com/ebakus/go-ebakus/core" 35 "github.com/ebakus/go-ebakus/core/rawdb" 36 "github.com/ebakus/go-ebakus/core/state" 37 "github.com/ebakus/go-ebakus/core/types" 38 "github.com/ebakus/go-ebakus/core/vm" 39 "github.com/ebakus/go-ebakus/crypto" 40 "github.com/ebakus/go-ebakus/metrics" 41 42 "github.com/ebakus/go-ebakus/log" 43 "github.com/ebakus/go-ebakus/rlp" 44 45 "github.com/ebakus/go-ebakus/consensus" 46 "github.com/ebakus/go-ebakus/rpc" 47 48 "github.com/ebakus/go-ebakus/ethdb" 49 "github.com/ebakus/go-ebakus/params" 50 lru "github.com/hashicorp/golang-lru" 51 "golang.org/x/crypto/sha3" 52 ) 53 54 var ( 55 checkpointInterval = uint64(60 * 10) 56 blockPeriod = uint64(2) // Default block issuance period of 5 sec 57 initialDistribution = uint64(1e9) // EBK 58 yearlyInflation = float64(0.01) 59 60 signatureCacheSize = 4096 // Number of recent block signatures to keep in memory 61 ) 62 63 var ( 64 // errUnknownBlock is returned when the list of signers is requested for a block 65 // that is not part of the local blockchain. 66 errUnknownBlock = errors.New("unknown block") 67 68 // errInvalidCheckpointBeneficiary is returned if a checkpoint/epoch transition 69 // block has a beneficiary set to non-zeroes. 70 errInvalidCheckpointBeneficiary = errors.New("beneficiary in checkpoint block non-zero") 71 72 // ErrInvalidTimestamp is returned if the timestamp of a block is lower than 73 // the previous block's timestamp + the minimum block period. 74 ErrInvalidTimestamp = errors.New("invalid timestamp") 75 76 // errUnauthorized is returned if a header is signed by a non-authorized entity. 77 errUnauthorized = errors.New("unauthorized") 78 79 // errInvalidVotingChain is returned when out-of-range or non-contiguous headers are provided. 80 errInvalidHeaderChain = errors.New("invalid header chain") 81 82 errInvalidStateHeaderAlignment = errors.New("invalid state header alignment") 83 84 // errMissingSignature is returned if a block's does not contain a 65 byte secp256k1 signature. 85 errMissingSignature = errors.New("65 byte signature missing") 86 87 ErrInvalidDelegateUpdateBlock = errors.New("Delegates updated at wrong block") 88 89 // ErrProductionAborted is returned when the producer is instructed to prepaturely abort 90 ErrProductionAborted = errors.New("Production aborted") 91 ) 92 93 var blockProduceTimer = metrics.GetOrRegisterTimer("worker/blocks/produce", nil) 94 95 // SignerFn is a signer callback function to request a hash to be signed by a 96 // backing account. 97 type SignerFn func(accounts.Account, string, []byte) ([]byte, error) 98 99 // DPOS is the delegate proof-of-stake consensus engine 100 type DPOS struct { 101 config *params.DPOSConfig 102 db ethdb.Database 103 ebakusDb *ebakusdb.DB 104 blockchain *core.BlockChain 105 genesis *core.Genesis 106 107 signatures *lru.ARCCache // Signatures of recent blocks to speed up address recover 108 109 signer common.Address // Ebakus address of the signing key 110 signFn SignerFn // Signer function to authorize hashes with 111 lock sync.RWMutex 112 } 113 114 // ecrecover extracts the Ebakus account address from a signed header. 115 func ecrecover(header *types.Header, sigcache *lru.ARCCache) (common.Address, error) { 116 // If the signature's already cached, return that 117 hash := header.Hash() 118 if address, known := sigcache.Get(hash); known { 119 return address.(common.Address), nil 120 } 121 122 signature := header.Signature 123 if len(signature) < 65 { 124 return common.Address{}, errMissingSignature 125 } 126 127 // Recover the public key and the Ebakus address 128 pubkey, err := crypto.Ecrecover(sigHash(header).Bytes(), signature) 129 if err != nil { 130 return common.Address{}, err 131 } 132 var signer common.Address 133 copy(signer[:], crypto.Keccak256(pubkey[1:])[12:]) 134 135 sigcache.Add(hash, signer) 136 return signer, nil 137 } 138 139 // New creates a Delegated Proof of Stake consensus engine 140 func New(config *params.DPOSConfig, db ethdb.Database, ebakusDb *ebakusdb.DB, genesis *core.Genesis) *DPOS { 141 conf := *config 142 143 if conf.Period == 0 { 144 conf.Period = blockPeriod 145 } 146 147 if conf.InitialDistribution == 0 { 148 conf.InitialDistribution = initialDistribution 149 } 150 151 if conf.YearlyInflation == 0 { 152 conf.YearlyInflation = yearlyInflation 153 } 154 155 signatures, _ := lru.NewARC(signatureCacheSize) 156 157 return &DPOS{ 158 config: &conf, 159 db: db, 160 ebakusDb: ebakusDb, 161 blockchain: nil, 162 genesis: genesis, 163 164 signatures: signatures, 165 } 166 } 167 168 func (d *DPOS) SetBlockchain(bc *core.BlockChain) { 169 d.blockchain = bc 170 } 171 172 // Author implements consensus.Engine, returning the Ebakus address recovered 173 // from the signature in the header 174 func (d *DPOS) Author(header *types.Header) (common.Address, error) { 175 return ecrecover(header, d.signatures) 176 } 177 178 // VerifyHeader checks whether a header conforms to the consensus rules of a 179 // given engine. Verifying the seal may be done optionally here, or explicitly 180 // via the VerifySeal method. 181 func (d *DPOS) VerifyHeader(chain consensus.ChainReader, header *types.Header, seal bool) error { 182 return d.verifyHeader(chain, header, nil) 183 } 184 185 func (d *DPOS) getParent(chain consensus.ChainReader, header *types.Header, parents []*types.Header) *types.Header { 186 if len(parents) > 0 { 187 return parents[len(parents)-1] 188 } 189 return chain.GetHeader(header.ParentHash, header.Number.Uint64()-1) 190 } 191 192 func (d *DPOS) verifyHeader(chain consensus.ChainReader, header *types.Header, parents []*types.Header) error { 193 if header.Number == nil { 194 return errUnknownBlock 195 } 196 197 blockNum := header.Number.Uint64() 198 199 if header.Time > uint64(time.Now().Unix()) { 200 return consensus.ErrFutureBlock 201 } 202 203 if blockNum == 0 { 204 return nil 205 } 206 207 // Ensure that the block's timestamp isn't too close to it's parent 208 parent := d.getParent(chain, header, parents) 209 if parent == nil || parent.Number.Uint64() != blockNum-1 || parent.Hash() != header.ParentHash { 210 return consensus.ErrUnknownAncestor 211 } 212 if parent.Time+d.config.Period > header.Time { 213 return ErrInvalidTimestamp 214 } 215 216 return nil 217 } 218 219 // VerifyHeaders is similar to VerifyHeader, but verifies a batch of headers 220 // concurrently. The method returns a quit channel to abort the operations and 221 // a results channel to retrieve the async verifications (the order is that of 222 // the input slice). 223 func (d *DPOS) VerifyHeaders(chain consensus.ChainReader, headers []*types.Header, seals []bool) (chan<- struct{}, <-chan error) { 224 abort := make(chan struct{}) 225 results := make(chan error, len(headers)) 226 227 go func() { 228 for i, header := range headers { 229 err := d.verifyHeader(chain, header, headers[:i]) 230 231 select { 232 case <-abort: 233 return 234 case results <- err: 235 } 236 } 237 }() 238 239 return abort, results 240 } 241 242 // VerifyBlock verifies that the given block conform to the consensus 243 // rules of a given engine. 244 func (d *DPOS) VerifyBlock(chain consensus.ChainReader, block *types.Block) error { 245 246 return d.verifySeal(chain, block.Header(), nil) 247 } 248 249 // VerifySeal checks whether the crypto seal on a header is valid according to 250 // the consensus rules of the given engine. 251 func (d *DPOS) VerifySeal(chain consensus.ChainReader, header *types.Header) error { 252 return d.verifySeal(chain, header, nil) 253 } 254 255 func (d *DPOS) verifySeal(chain consensus.ChainReader, header *types.Header, parents []*types.Header) error { 256 blockNumber := header.Number.Uint64() 257 if blockNumber == 0 { 258 return nil 259 } 260 261 slot := float64(header.Time) / float64(d.config.Period) 262 263 parentBlockNumber := blockNumber - 1 264 ebakusState, err := chain.EbakusStateAt(header.ParentHash, parentBlockNumber) 265 if err != nil { 266 return fmt.Errorf("Verify seal failed to get ebakus state: %s", err) 267 } 268 269 parentHeader := d.blockchain.GetHeaderByHash(header.ParentHash) 270 271 signer := d.getSignerAtSlot(chain, parentHeader, ebakusState, slot) 272 ebakusState.Release() 273 274 blockSigner, err := ecrecover(header, d.signatures) 275 if err != nil { 276 return err 277 } 278 279 if blockSigner != signer { 280 return errUnauthorized 281 } 282 283 return nil 284 } 285 286 // Close terminates any background threads maintained by the consensus engine (we don't have any). 287 func (d *DPOS) Close() error { 288 var err error 289 return err 290 } 291 292 // Prepare initializes the consensus fields of a block header according to the 293 // rules of a particular engine. The changes are executed inline. 294 func (d *DPOS) Prepare(chain consensus.ChainReader, stop <-chan struct{}) (*types.Block, *types.Header, error) { 295 d.lock.RLock() 296 signer := d.signer 297 d.lock.RUnlock() 298 299 for { 300 head := chain.CurrentBlock() 301 headSlot := float64(head.Time()) / float64(d.config.Period) 302 303 now := unixNow() 304 slot := float64(now) / float64(d.config.Period) 305 306 headHash := head.Hash() 307 headBlockNumber := head.NumberU64() 308 ebakusState, err := chain.EbakusStateAt(headHash, headBlockNumber) 309 if err != nil { 310 return nil, nil, fmt.Errorf("Prepare new block failed to get ebakus state at block number %d: %s", headBlockNumber, err) 311 } 312 313 inTurnSigner := d.getSignerAtSlot(chain, head.Header(), ebakusState, slot) 314 ebakusState.Release() 315 316 log.Trace("Check turn", "slot", slot, "signer", signer, "turn for", inTurnSigner) 317 318 if slot > headSlot && signer == inTurnSigner { 319 // We are the chosen one. Break. 320 num := head.Number() 321 322 header := &types.Header{ 323 ParentHash: headHash, 324 Number: num.Add(num, common.Big1), 325 GasLimit: 0, 326 GasUsed: 0, 327 Time: uint64(slot * float64(d.config.Period)), 328 } 329 330 // Sealing the genesis block is not supported 331 blockNumber := header.Number.Uint64() 332 if blockNumber == 0 { 333 return nil, nil, errUnknownBlock 334 } 335 336 log.Trace("Will seal block", "header", header, "slot", slot) 337 338 return head, header, nil 339 } 340 341 nextSlotTime := time.Unix(int64((slot+1)*float64(d.config.Period)), 0) 342 343 timeToNextSlot := nextSlotTime.Sub(time.Now()) 344 345 log.Trace("Sleeping", "time", timeToNextSlot) 346 347 select { 348 case <-stop: 349 log.Info("Woke to abort") 350 return nil, nil, ErrProductionAborted 351 case <-time.After(timeToNextSlot): 352 } 353 } 354 } 355 356 // Finalize runs any post-transaction state modifications (e.g. block rewards) 357 // and assembles the final block. 358 // Note: The block header and state database might be updated to reflect any 359 // consensus rules that happen at finalization (e.g. block rewards). 360 func (d *DPOS) Finalize(chain consensus.ChainReader, header *types.Header, state *state.StateDB, ebakusState *ebakusdb.Snapshot, coinbase common.Address, txs []*types.Transaction) { 361 // Accumulate any block and uncle rewards and commit the final state root 362 d.AccumulateRewards(chain.Config().DPOS, state, header, coinbase) 363 header.Root = state.IntermediateRoot(chain.Config().IsEIP158(header.Number)) 364 } 365 366 // FinalizeAndAssemble implements consensus.Engine, accumulating the block and 367 // setting the final state and assembling the block. 368 func (d *DPOS) FinalizeAndAssemble(chain consensus.ChainReader, header *types.Header, state *state.StateDB, ebakusState *ebakusdb.Snapshot, coinbase common.Address, txs []*types.Transaction, 369 receipts []*types.Receipt) (*types.Block, error) { 370 // Accumulate any block and uncle rewards and commit the final state root 371 d.AccumulateRewards(chain.Config().DPOS, state, header, coinbase) 372 header.Root = state.IntermediateRoot(chain.Config().IsEIP158(header.Number)) 373 374 // Calculate delegate changes 375 oldBlockNumber := header.Number.Uint64() - 1 376 oldEbakusSnapshotId := rawdb.ReadSnapshot(d.db, header.ParentHash, oldBlockNumber) 377 if oldEbakusSnapshotId == nil { 378 return nil, errUnknownBlock 379 } 380 oldEbakusState := d.ebakusDb.Snapshot(*oldEbakusSnapshotId) 381 defer oldEbakusState.Release() 382 383 delegateCount := d.config.DelegateCount 384 bonusDelegateCount := d.config.BonusDelegateCount 385 turnBlockCount := d.config.TurnBlockCount 386 oldDelegates := GetDelegates(d.blockchain.GetHeaderByHash(header.ParentHash), oldEbakusState, delegateCount, bonusDelegateCount, turnBlockCount) 387 newDelegates := GetDelegates(header, ebakusState, delegateCount, bonusDelegateCount, turnBlockCount) 388 delegateDiff := oldDelegates.Diff(newDelegates) 389 390 log.Trace("Delegates", "diff", delegateDiff) 391 392 block := types.NewBlock(header, txs, receipts, &delegateDiff) 393 394 return block, nil 395 } 396 397 // Authorize injects a private key into the consensus engine to mint new blocks 398 // with. 399 func (d *DPOS) Authorize(signer common.Address, signFn SignerFn) { 400 d.lock.Lock() 401 defer d.lock.Unlock() 402 403 d.signer = signer 404 d.signFn = signFn 405 } 406 407 // Seal generates a new block for the given input block with the local miner's 408 // seal place on top. 409 func (d *DPOS) Seal(chain consensus.ChainReader, block *types.Block, results chan<- *types.Block, stop <-chan struct{}) error { 410 header := block.Header() 411 412 // Sealing the genesis block is not supported 413 blockNumber := header.Number.Uint64() 414 if blockNumber == 0 { 415 return errUnknownBlock 416 } 417 418 // Don't hold the signer fields for the entire sealing procedure 419 d.lock.RLock() 420 signer, signFn := d.signer, d.signFn 421 d.lock.RUnlock() 422 423 // Ensure the timestamp has the correct delay 424 parent := chain.GetHeader(header.ParentHash, blockNumber-1) 425 if parent == nil { 426 return consensus.ErrUnknownAncestor 427 } 428 429 // Sign 430 sighash, err := signFn(accounts.Account{Address: signer}, accounts.MimetypeDpos, RLP(header)) 431 if err != nil { 432 return err 433 } 434 435 header.Signature = sighash 436 437 results <- block.WithSeal(header) 438 439 return nil 440 } 441 442 // SealHash returns the hash of a block prior to it being sealed. 443 func (d *DPOS) SealHash(header *types.Header) (hash common.Hash) { 444 hasher := sha3.NewLegacyKeccak256() 445 446 rlp.Encode(hasher, []interface{}{ 447 header.ParentHash, 448 header.Root, 449 header.TxHash, 450 header.ReceiptHash, 451 header.Bloom, 452 header.Number, 453 header.GasLimit, 454 header.GasUsed, 455 header.Time, 456 }) 457 hasher.Sum(hash[:0]) 458 return hash 459 } 460 461 // AccumulateRewards credits the coinbase of the given block with the reward 462 func (d *DPOS) AccumulateRewards(config *params.DPOSConfig, state *state.StateDB, header *types.Header, coinbase common.Address) { 463 reward := big.NewInt(3171 * 1e14) 464 465 state.AddBalance(coinbase, reward) 466 } 467 468 // CalcDifficulty is essentialy dummy in ebakus 469 func (d *DPOS) CalcDifficulty(chain consensus.ChainReader, time uint64, parent *types.Header) *big.Int { 470 return big.NewInt(1) 471 } 472 473 // APIs implements consensus.Engine, returning the user facing RPC API to allow 474 // controlling the delegate voting etc. 475 func (d *DPOS) APIs(chain consensus.ChainReader) []rpc.API { 476 return []rpc.API{{ 477 Namespace: "dpos", 478 Version: "1.0", 479 Service: &API{chain: chain, dpos: d}, 480 }} 481 } 482 483 func unixNow() uint64 { 484 return uint64(time.Now().Unix()) 485 } 486 487 func (d *DPOS) getSignerAtSlot(chain consensus.ChainReader, header *types.Header, state *ebakusdb.Snapshot, slot float64) common.Address { 488 delegates := GetDelegates(header, state, d.config.DelegateCount, d.config.BonusDelegateCount, d.config.TurnBlockCount) 489 490 if d.config.TurnBlockCount == 0 { 491 log.Warn("DPOS.TurnBlockCount is zero. This means that mining won't match a signer.") 492 } 493 494 if d.config.DelegateCount == 0 || d.config.TurnBlockCount == 0 { 495 return common.Address{} 496 } 497 498 slot = slot / float64(d.config.TurnBlockCount) 499 s := int(slot) % int(d.config.DelegateCount) 500 501 if s < len(delegates) { 502 return delegates[s].Id 503 } 504 505 return common.Address{} 506 } 507 508 func (d *DPOS) getBlockDensity(chain consensus.ChainReader, number rpc.BlockNumber, lookbackTime uint64) (map[string]interface{}, error) { 509 var prevBlock *types.Block 510 totalMissedBlocks := 0 511 latestBlockNumber := chain.CurrentBlock().NumberU64() 512 513 if number == rpc.LatestBlockNumber { 514 number = rpc.BlockNumber(latestBlockNumber) 515 } 516 517 if uint64(number) > latestBlockNumber { 518 return nil, consensus.ErrFutureBlock 519 } 520 521 initialBlock := d.blockchain.GetBlockByNumber(uint64(number)) 522 blocksHashes := d.blockchain.GetBlockHashesFromHash(initialBlock.Hash(), lookbackTime) 523 524 // create a map using `timestamp` as key for our algorithm lookup 525 blocksMap := make(map[uint64]*types.Block, len(blocksHashes)+1) 526 blocksMap[uint64(initialBlock.Time())] = initialBlock 527 528 for _, blockHash := range blocksHashes { 529 block := d.blockchain.GetBlockByHash(blockHash) 530 blocksMap[uint64(block.Time())] = block 531 } 532 533 lookbackTimestamp := initialBlock.Time() - lookbackTime 534 535 blocksNumber := uint64(len(blocksHashes) + 1) 536 if lookbackTime > blocksNumber { 537 lookbackTimestamp = initialBlock.Time() - blocksNumber 538 } 539 540 for timestamp := initialBlock.Time(); timestamp >= lookbackTimestamp; timestamp-- { 541 block, blockFound := blocksMap[timestamp] 542 if !blockFound { 543 totalMissedBlocks++ 544 continue 545 } 546 547 if err := d.verifySeal(chain, block.Header(), nil); err != nil { 548 totalMissedBlocks++ 549 } 550 551 if prevBlock != nil && (prevBlock.NumberU64() != block.NumberU64()+1 || block.Hash() != prevBlock.ParentHash()) { 552 totalMissedBlocks++ 553 } 554 555 prevBlock = block 556 } 557 558 result := map[string]interface{}{ 559 "total_missed_blocks": totalMissedBlocks, 560 } 561 562 return result, nil 563 } 564 565 func uniformRandom(max uint64, hash common.Hash) uint64 { 566 bitsRequired := bits.Len64(max - 1) 567 568 startBit := 0 569 var rand uint64 570 for { 571 rand = 0 572 for i := 0; i < bitsRequired; i++ { 573 b := hash[((startBit+i)/8)%common.HashLength] 574 p := byte((startBit + i) % 8) 575 rand += uint64((b & (1 << p) >> p)) << i 576 } 577 if rand < max { 578 break 579 } 580 if startBit/8 >= common.HashLength { 581 rand = rand % max 582 break 583 } 584 startBit++ 585 } 586 587 return rand 588 } 589 590 func GetDelegates(header *types.Header, snap *ebakusdb.Snapshot, maxWitnesses uint64, maxBonusWitnesses uint64, turnBlockCount uint64) vm.WitnessArray { 591 if maxWitnesses == 0 { 592 log.Warn("DPOS.getDelegates maxWitnesses is zero. This means that mining won't match a signer. Check if DPOS.DelegatesCount is set to zero") 593 } 594 595 maxWitnessesToLoad := maxWitnesses 596 if maxBonusWitnesses > 0 { 597 maxWitnessesToLoad += maxBonusWitnesses 598 } 599 600 delegates := vm.DelegateVotingGetDelegates(snap, maxWitnessesToLoad) 601 602 // get bonus delegate 603 if uint64(len(delegates)) > maxWitnesses { 604 bonusCandidateDelegates := delegates[maxWitnesses-1:] 605 delegates = delegates[:maxWitnesses-1] // excluding the last bonus position from maxWitnesses 606 607 slot := (header.Time + 1) / turnBlockCount 608 slotData := make([]byte, 8) 609 binary.BigEndian.PutUint64(slotData, slot) 610 rand := uniformRandom(uint64(len(bonusCandidateDelegates)), crypto.Keccak256Hash(slotData)) 611 delegates = append(delegates, bonusCandidateDelegates[rand]) 612 } 613 614 return delegates 615 } 616 617 // sigHash returns the hash which is used as input for the delegate proof-of-stake 618 // signing. It is the hash of the entire header apart from the 65 byte signature 619 // contained at the end of the extra data. 620 func sigHash(header *types.Header) (hash common.Hash) { 621 hasher := sha3.NewLegacyKeccak256() 622 encodeSigHeader(hasher, header) 623 hasher.Sum(hash[:0]) 624 return hash 625 } 626 627 // RLP returns the rlp bytes which needs to be signed for the proof-of-authority 628 // sealing. 629 func RLP(header *types.Header) []byte { 630 b := new(bytes.Buffer) 631 encodeSigHeader(b, header) 632 return b.Bytes() 633 } 634 635 func encodeSigHeader(w io.Writer, header *types.Header) { 636 err := rlp.Encode(w, []interface{}{ 637 header.ParentHash, 638 header.Root, 639 header.TxHash, 640 header.ReceiptHash, 641 header.Bloom, 642 header.Number, 643 header.GasLimit, 644 header.GasUsed, 645 header.Time, 646 header.DelegateDiff, 647 }) 648 if err != nil { 649 panic("can't encode: " + err.Error()) 650 } 651 }