github.com/yinchengtsinghua/golang-Eos-dpos-Ethereum@v0.0.0-20190121132951-92cc4225ed8e/consensus/dpos/dpos.go (about) 1 2 //此源码被清华学神尹成大魔王专业翻译分析并修改 3 //尹成QQ77025077 4 //尹成微信18510341407 5 //尹成所在QQ群721929980 6 //尹成邮箱 yinc13@mails.tsinghua.edu.cn 7 //尹成毕业于清华大学,微软区块链领域全球最有价值专家 8 //https://mvp.microsoft.com/zh-cn/PublicProfile/4033620 9 package dpos 10 11 import ( 12 "bytes" 13 "encoding/binary" 14 "errors" 15 "fmt" 16 "math/big" 17 "sync" 18 "time" 19 20 "github.com/ethereum/go-ethereum/accounts" 21 "github.com/ethereum/go-ethereum/common" 22 "github.com/ethereum/go-ethereum/consensus" 23 "github.com/ethereum/go-ethereum/consensus/misc" 24 "github.com/ethereum/go-ethereum/core/state" 25 "github.com/ethereum/go-ethereum/core/types" 26 "github.com/ethereum/go-ethereum/crypto" 27 "github.com/ethereum/go-ethereum/crypto/sha3" 28 "github.com/ethereum/go-ethereum/ethdb" 29 "github.com/ethereum/go-ethereum/log" 30 "github.com/ethereum/go-ethereum/params" 31 "github.com/ethereum/go-ethereum/rlp" 32 "github.com/ethereum/go-ethereum/rpc" 33 "github.com/ethereum/go-ethereum/trie" 34 lru "github.com/hashicorp/golang-lru" 35 ) 36 37 38 const ( 39 extraVanity = 32 //固定为签名者虚荣保留的额外数据前缀字节数 40 extraSeal = 65 //固定为签名者密封保留的额外数据后缀字节数 41 inmemorySignatures = 4096 //要保存在内存中的最近块签名数 42 43 //blockinterval=int64(10)//出块间隔 44 //epochinterval=int64(86400)//选举周期间隔24*60*60 s 45 //最大验证大小=21 46 // 47 //conensusize=15//maxvalidatorsize*2/3+1 48 blockInterval = int64(10) //附带条件 49 epochInterval = int64(60) //选举周间隔24*60*60 s 50 maxValidatorSize = 3 51 safeSize = 2 //maxvalidator大小*2/3+1 52 consensusSize = 2 //maxvalidator大小*2/3+1 53 ) 54 55 56 57 var ( 58 big0 = big.NewInt(0) 59 big8 = big.NewInt(8) 60 big32 = big.NewInt(32) 61 62 frontierBlockReward *big.Int = big.NewInt(5e+18) // 63 byzantiumBlockReward *big.Int = big.NewInt(3e+18) //从拜占庭向上成功开采一个区块,在魏城获得区块奖励 64 65 timeOfFirstBlock = int64(0) 66 67 confirmedBlockHead = []byte("confirmed-block-head") 68 ) 69 70 var ( 71 //当请求块的签名者列表时,返回errunknownblock。 72 //这不是本地区块链的一部分。 73 errUnknownBlock = errors.New("unknown block") 74 //如果块的额外数据节短于 75 //32字节,这是存储签名者虚荣所必需的。 76 errMissingVanity = errors.New("extra-data 32 byte vanity prefix missing") 77 //如果块的额外数据节似乎不存在,则返回errmissingsignature 78 //包含65字节的secp256k1签名。 79 errMissingSignature = errors.New("extra-data 65 byte suffix signature missing") 80 //如果块的mix digest为非零,则返回errInvalidMixDigest。 81 errInvalidMixDigest = errors.New("non-zero mix digest") 82 //如果块包含非空的叔叔列表,则返回errInvalidUncleHash。 83 errInvalidUncleHash = errors.New("non empty uncle hash") 84 errInvalidDifficulty = errors.New("invalid difficulty") 85 86 //如果块的时间戳低于,则返回errInvalidTimestamp 87 //上一个块的时间戳+最小块周期。 88 ErrInvalidTimestamp = errors.New("invalid timestamp") 89 ErrWaitForPrevBlock = errors.New("wait for last block arrived") 90 ErrMintFutureBlock = errors.New("mint the future block") 91 ErrMismatchSignerAndValidator = errors.New("mismatch block signer and validator") 92 ErrInvalidBlockValidator = errors.New("invalid block validator") 93 ErrInvalidMintBlockTime = errors.New("invalid time to mint the block") 94 ErrNilBlockHeader = errors.New("nil block header returned") 95 ) 96 var ( 97 uncleHash = types.CalcUncleHash(nil) //作为叔叔,Keccak256(rlp([])在POW之外总是毫无意义的。 98 ) 99 100 type Dpos struct { 101 config *params.DposConfig //共识引擎配置参数 102 db ethdb.Database //存储和检索快照检查点的数据库 103 104 signer common.Address 105 signFn SignerFn 106 signatures *lru.ARCCache //加快开采速度的近期区块特征 107 confirmedBlockHeader *types.Header 108 109 mu sync.RWMutex 110 stop chan bool 111 } 112 113 type SignerFn func(accounts.Account, []byte) ([]byte, error) 114 115 //注:Sighash是从集团复制的 116 //sighash返回用作权限证明输入的哈希 117 //签署。它是除65字节签名之外的整个头的哈希 118 //包含在额外数据的末尾。 119 // 120 //注意,该方法要求额外数据至少为65字节,否则 121 //恐慌。这样做是为了避免意外使用这两个表单(存在签名 122 //或者不是),这可能会被滥用,从而为同一个头产生不同的散列。 123 func sigHash(header *types.Header) (hash common.Hash) { 124 hasher := sha3.NewKeccak256() 125 126 rlp.Encode(hasher, []interface{}{ 127 header.ParentHash, 128 header.UncleHash, 129 header.Validator, 130 header.Coinbase, 131 header.Root, 132 header.TxHash, 133 header.ReceiptHash, 134 header.Bloom, 135 header.Difficulty, 136 header.Number, 137 header.GasLimit, 138 header.GasUsed, 139 header.Time, 140 header.Extra[:len(header.Extra)-65], //是的,如果多余的太短,这会很恐慌的 141 header.MixDigest, 142 header.Nonce, 143 header.DposContext.Root(), 144 header.MaxValidatorSize, // 145 }) 146 hasher.Sum(hash[:0]) 147 148 return hash 149 } 150 151 func New(config *params.DposConfig, db ethdb.Database) *Dpos { 152 signatures, _ := lru.NewARC(inmemorySignatures) 153 154 return &Dpos{ 155 config: config, 156 db: db, 157 signatures: signatures, 158 } 159 } 160 161 func (d *Dpos) Author(header *types.Header) (common.Address, error) { 162 return header.Validator, nil 163 } 164 165 //验证批量是否符合共识算法规则 166 func (d *Dpos) VerifyHeader(chain consensus.ChainReader, header *types.Header, seal bool, blockInterval uint64) error { 167 return d.verifyHeader(chain, header, nil, blockInterval) 168 } 169 170 func (d *Dpos) verifyHeader(chain consensus.ChainReader, header *types.Header, parents []*types.Header,blockInterval uint64 ) error { 171 if header.Number == nil { 172 return errUnknownBlock 173 } 174 number := header.Number.Uint64() 175 //不需要验证功能块 176 if header.Time.Cmp(big.NewInt(time.Now().Unix())) > 0 { 177 return consensus.ErrFutureBlock 178 } 179 //检查额外数据是否包含虚荣和签名 180 if len(header.Extra) < extraVanity { 181 return errMissingVanity 182 } 183 if len(header.Extra) < extraVanity+extraSeal { 184 return errMissingSignature 185 } 186 //确保混合摘要为零,因为我们当前没有分叉保护 187 if header.MixDigest != (common.Hash{}) { 188 return errInvalidMixDigest 189 } 190 //困难总是1 191 //度设定为1 192 // 193 if header.Difficulty.Uint64() != 1 { 194 return errInvalidDifficulty 195 } 196 197 //确保块中不包含任何在DPO中无意义的叔叔。 198 if header.UncleHash != uncleHash { 199 return errInvalidUncleHash 200 } 201 //如果所有检查都通过,则验证硬分叉的任何特殊字段 202 if err := misc.VerifyForkHashes(chain.Config(), header, false); err != nil { 203 return err 204 } 205 206 var parent *types.Header 207 if len(parents) > 0 { 208 parent = parents[len(parents)-1] 209 } else { 210 parent = chain.GetHeader(header.ParentHash, number-1) 211 } 212 if parent == nil || parent.Number.Uint64() != number-1 || parent.Hash() != header.ParentHash { 213 return consensus.ErrUnknownAncestor 214 } 215 if parent.Time.Uint64()+blockInterval> header.Time.Uint64() { 216 return ErrInvalidTimestamp 217 } 218 return nil 219 } 220 221 //批量验证区块头是否符合公共计算法规 222 func (d *Dpos) VerifyHeaders(chain consensus.ChainReader, headers []*types.Header, seals []bool,) (chan<- struct{}, <-chan error) { 223 abort := make(chan struct{}) 224 results := make(chan error, len(headers)) 225 blockInterval := chain.GetHeaderByNumber(0).BlockInterval 226 227 go func() { 228 for i, header := range headers { 229 //header.extra=make([]字节,ExtraVanity+ExtraSeal) 230 err := d.verifyHeader(chain, header, headers[:i],blockInterval) 231 select { 232 case <-abort: 233 return 234 case results <- err: 235 } 236 } 237 }() 238 return abort, results 239 } 240 241 //verifyuncles实现converse.engine,始终返回任何 242 //因为这个共识机制不允许叔叔。 243 func (d *Dpos) VerifyUncles(chain consensus.ChainReader, block *types.Block) error { 244 if len(block.Uncles()) > 0 { 245 return errors.New("uncles not allowed") 246 } 247 return nil 248 } 249 250 //验证seal是否执行consension.engine,检查签名是否包含 251 //头部满足共识协议要求。 252 func (d *Dpos) VerifySeal(chain consensus.ChainReader, currentheader, genesisheader *types.Header) error { 253 return d.verifySeal(chain, currentheader, genesisheader,nil) 254 } 255 256 func (d *Dpos) verifySeal(chain consensus.ChainReader, currentheader, genesisheader *types.Header, parents []*types.Header) error { 257 //验证不支持Genesis块 258 number := currentheader.Number.Uint64() 259 if number == 0 { 260 return errUnknownBlock 261 } 262 var parent *types.Header 263 if len(parents) > 0 { 264 parent = parents[len(parents)-1] 265 } else { 266 parent = chain.GetHeader(currentheader.ParentHash, number-1) 267 } 268 269 trieDB := trie.NewDatabase(d.db) 270 dposContext, err := types.NewDposContextFromProto(trieDB, parent.DposContext) //零位 271 272 if err != nil { 273 return err 274 } 275 epochContext := &EpochContext{DposContext: dposContext} 276 blockInterVal := genesisheader.BlockInterval 277 validator, err := epochContext.lookupValidator(currentheader.Time.Int64(),blockInterVal) 278 if err != nil { 279 return err 280 } 281 // 282 if err := d.verifyBlockSigner(validator, currentheader); err != nil { 283 return err 284 } 285 return d.updateConfirmedBlockHeader(chain) 286 } 287 288 func (d *Dpos) verifyBlockSigner(validator common.Address, header *types.Header) error { 289 signer, err := ecrecover(header, d.signatures) 290 if err != nil { 291 return err 292 } 293 if bytes.Compare(signer.Bytes(), validator.Bytes()) != 0 { 294 return ErrInvalidBlockValidator 295 } 296 if bytes.Compare(signer.Bytes(), header.Validator.Bytes()) != 0 { 297 return ErrMismatchSignerAndValidator 298 } 299 return nil 300 } 301 302 func (d *Dpos) updateConfirmedBlockHeader(chain consensus.ChainReader) error { 303 if d.confirmedBlockHeader == nil { 304 header, err := d.loadConfirmedBlockHeader(chain) 305 if err != nil { 306 header = chain.GetHeaderByNumber(0) 307 if header == nil { 308 return err 309 } 310 } 311 d.confirmedBlockHeader = header 312 } 313 314 curHeader := chain.CurrentHeader() 315 316 fmt.Println("+++++++++++++++++++555555++++++++++++++++++++++\n") 317 genesisHeader := chain.GetHeaderByNumber(0) 318 fmt.Println("+++++++++++++++++++from genesisBlock to get Maxvalidatorsize++++++++++++++++++++++\n") 319 epoch := int64(-1) 320 validatorMap := make(map[common.Address]bool) 321 for d.confirmedBlockHeader.Hash() != curHeader.Hash() && 322 d.confirmedBlockHeader.Number.Uint64() < curHeader.Number.Uint64() { 323 curEpoch := curHeader.Time.Int64() / epochInterval 324 if curEpoch != epoch { 325 epoch = curEpoch 326 validatorMap = make(map[common.Address]bool) 327 } 328 //快速返回 329 //如果块数差小于一致同意的见证数 330 //无需检查是否确认阻塞 331 consensusSize :=int(genesisHeader.MaxValidatorSize*2/3+1) 332 if curHeader.Number.Int64()-d.confirmedBlockHeader.Number.Int64() < int64(consensusSize-len(validatorMap)) { 333 log.Debug("Dpos fast return", "current", curHeader.Number.String(), "confirmed", d.confirmedBlockHeader.Number.String(), "witnessCount", len(validatorMap)) 334 return nil 335 } 336 validatorMap[curHeader.Validator] = true 337 if len(validatorMap) >= consensusSize { 338 d.confirmedBlockHeader = curHeader 339 if err := d.storeConfirmedBlockHeader(d.db); err != nil { 340 return err 341 } 342 log.Debug("dpos set confirmed block header success", "currentHeader", curHeader.Number.String()) 343 return nil 344 } 345 curHeader = chain.GetHeaderByHash(curHeader.ParentHash) 346 if curHeader == nil { 347 return ErrNilBlockHeader 348 } 349 } 350 return nil 351 } 352 353 func (s *Dpos) loadConfirmedBlockHeader(chain consensus.ChainReader) (*types.Header, error) { 354 key, err := s.db.Get(confirmedBlockHead) 355 if err != nil { 356 return nil, err 357 } 358 header := chain.GetHeaderByHash(common.BytesToHash(key)) 359 if header == nil { 360 return nil, ErrNilBlockHeader 361 } 362 return header, nil 363 } 364 365 //存储将快照插入数据库。 366 func (s *Dpos) storeConfirmedBlockHeader(db ethdb.Database) error { 367 return db.Put(confirmedBlockHead, s.confirmedBlockHeader.Hash().Bytes()) 368 } 369 370 func (d *Dpos) Prepare(chain consensus.ChainReader, header *types.Header) error { 371 header.Nonce = types.BlockNonce{} 372 number := header.Number.Uint64() 373 if len(header.Extra) < extraVanity { 374 header.Extra = append(header.Extra, bytes.Repeat([]byte{0x00}, extraVanity-len(header.Extra))...) 375 } 376 header.Extra = header.Extra[:extraVanity] 377 header.Extra = append(header.Extra, make([]byte, extraSeal)...) 378 parent := chain.GetHeader(header.ParentHash, number-1) 379 if parent == nil { 380 return consensus.ErrUnknownAncestor 381 } 382 header.Difficulty = d.CalcDifficulty(chain, header.Time.Uint64(), parent) 383 header.Validator = d.signer 384 return nil 385 } 386 387 func AccumulateRewards(config *params.ChainConfig, state *state.StateDB, header *types.Header, uncles []*types.Header) { 388 //根据链进程选择正确的区块奖励 389 blockReward := frontierBlockReward 390 if config.IsByzantium(header.Number) { 391 blockReward = byzantiumBlockReward 392 } 393 //为矿工和任何包括叔叔的人累积奖励 394 reward := new(big.Int).Set(blockReward) 395 state.AddBalance(header.Coinbase, reward) 396 } 397 398 //将出块周期内的交易打包进新的区域块中 399 func (d *Dpos) Finalize(chain consensus.ChainReader, header *types.Header, state *state.StateDB, txs []*types.Transaction, 400 uncles []*types.Header, receipts []*types.Receipt, dposContext *types.DposContext) (*types.Block, error) { 401 //累积积木奖励并提交最终状态根 402 AccumulateRewards(chain.Config(), state, header, uncles) 403 header.Root = state.IntermediateRoot(chain.Config().IsEIP158(header.Number)) 404 405 parent := chain.GetHeaderByHash(header.ParentHash) 406 epochContext := &EpochContext{ 407 statedb: state, 408 DposContext: dposContext, 409 TimeStamp: header.Time.Int64(), 410 } 411 if timeOfFirstBlock == 0 { 412 if firstBlockHeader := chain.GetHeaderByNumber(1); firstBlockHeader != nil { 413 timeOfFirstBlock = firstBlockHeader.Time.Int64() 414 } 415 } 416 fmt.Println("++++++++++++++77777++++++++++++++++++\n") 417 fmt.Println("**************get genesis header********\n") 418 genesis := chain.GetHeaderByNumber(0) 419 420 err := epochContext.tryElect(genesis, parent) 421 if err != nil { 422 return nil, fmt.Errorf("got error when elect next epoch, err: %s", err) 423 } 424 425 //更新薄荷计数trie 426 updateMintCnt(parent.Time.Int64(), header.Time.Int64(), header.Validator, dposContext) 427 header.DposContext = dposContext.ToProto() 428 return types.NewBlock(header, txs, uncles, receipts), nil 429 } 430 431 func (d *Dpos) checkDeadline(lastBlock *types.Block, now int64, blockInterval uint64) error { 432 prevSlot := PrevSlot(now, blockInterval) 433 nextSlot := NextSlot(now, blockInterval) 434 if lastBlock.Time().Int64() >= nextSlot { 435 return ErrMintFutureBlock 436 } 437 //最后一个街区到了,或者时间到了 438 if lastBlock.Time().Int64() == prevSlot || nextSlot-now <= 1 { 439 return nil 440 } 441 return ErrWaitForPrevBlock 442 } 443 444 //检查当前的验证人员是否在当前的节点上 445 func (d *Dpos) CheckValidator(lastBlock *types.Block, now int64,blockInterval uint64) error { 446 if err := d.checkDeadline(lastBlock, now, blockInterval); err != nil { 447 return err 448 } 449 // 450 dposContext, err := types.NewDposContextFromProto(trie.NewDatabase(d.db), lastBlock.Header().DposContext) 451 if err != nil { 452 return err 453 } 454 epochContext := &EpochContext{DposContext: dposContext} 455 validator, err := epochContext.lookupValidator(now,blockInterval) 456 if err != nil { 457 return err 458 } 459 if (validator == common.Address{}) || bytes.Compare(validator.Bytes(), d.signer.Bytes()) != 0 { 460 return ErrInvalidBlockValidator 461 } 462 return nil 463 } 464 465 //Seal使用本地矿工的 466 //密封顶部。 467 //验证模块内容是否符合DPOSS计算法规(验证新模块是否应由该验证人员提出模块) 468 func (d *Dpos) Seal(chain consensus.ChainReader, block *types.Block, stop <-chan struct{}) (*types.Block, error) { 469 header := block.Header() 470 number := header.Number.Uint64() 471 //不支持密封Genesis块 472 if number == 0 { 473 return nil, errUnknownBlock 474 } 475 now := time.Now().Unix() 476 delay := NextSlot(now,chain.GetHeaderByNumber(0).BlockInterval) - now 477 if delay > 0 { 478 select { 479 case <-stop: 480 return nil, nil 481 case <-time.After(time.Duration(delay) * time.Second): 482 } 483 } 484 block.Header().Time.SetInt64(time.Now().Unix()) 485 486 //时间到了,在街区签名 487 //对新块进行签名 488 sighash, err := d.signFn(accounts.Account{Address: d.signer}, sigHash(header).Bytes()) 489 if err != nil { 490 return nil, err 491 } 492 copy(header.Extra[len(header.Extra)-extraSeal:], sighash) 493 return block.WithSeal(header), nil 494 } 495 496 func (d *Dpos) CalcDifficulty(chain consensus.ChainReader, time uint64, parent *types.Header) *big.Int { 497 return big.NewInt(1) 498 } 499 500 func (d *Dpos) APIs(chain consensus.ChainReader) []rpc.API { 501 return []rpc.API{{ 502 Namespace: "dpos", 503 Version: "1.0", 504 Service: &API{chain: chain, dpos: d}, 505 Public: true, 506 }} 507 } 508 509 func (d *Dpos) Authorize(signer common.Address, signFn SignerFn) { 510 d.mu.Lock() 511 d.signer = signer 512 d.signFn = signFn 513 d.mu.Unlock() 514 } 515 516 func (d *Dpos) Close() error { 517 return nil 518 } 519 520 //ecrecover从签名的头中提取以太坊帐户地址。 521 func ecrecover(header *types.Header, sigcache *lru.ARCCache) (common.Address, error) { 522 //如果签名已经缓存,则返回 523 hash := header.Hash() 524 if address, known := sigcache.Get(hash); known { 525 return address.(common.Address), nil 526 } 527 //从头中检索签名额外数据 528 if len(header.Extra) < extraSeal { 529 return common.Address{}, errMissingSignature 530 } 531 signature := header.Extra[len(header.Extra)-extraSeal:] 532 //恢复公钥和以太坊地址 533 pubkey, err := crypto.Ecrecover(sigHash(header).Bytes(), signature) 534 if err != nil { 535 return common.Address{}, err 536 } 537 var signer common.Address 538 copy(signer[:], crypto.Keccak256(pubkey[1:])[12:]) 539 sigcache.Add(hash, signer) 540 return signer, nil 541 } 542 543 func PrevSlot(now int64, blockInterval uint64) int64 { 544 return int64((now-1)/int64(blockInterval)) * int64(blockInterval) 545 } 546 547 func NextSlot(now int64, blockInterval uint64) int64 { 548 return int64((now+int64(blockInterval)-1)/int64(blockInterval)) * int64(blockInterval) 549 } 550 551 //更新Newblock矿工的mintcntrie计数 552 //更新周期内验证人员出块数目的 553 func updateMintCnt(parentBlockTime, currentBlockTime int64, validator common.Address, dposContext *types.DposContext) { 554 currentMintCntTrie := dposContext.MintCntTrie() 555 currentEpoch := parentBlockTime / epochInterval 556 currentEpochBytes := make([]byte, 8) 557 binary.BigEndian.PutUint64(currentEpochBytes, uint64(currentEpoch)) 558 559 cnt := int64(1) 560 newEpoch := currentBlockTime / epochInterval 561 //仍在当前报告期间 562 if currentEpoch == newEpoch { 563 iter := trie.NewIterator(currentMintCntTrie.NodeIterator(currentEpochBytes)) 564 565 //当电流不是起源时,从mintcntrie读取最后一个计数。 566 if iter.Next() { 567 cntBytes := currentMintCntTrie.Get(append(currentEpochBytes, validator.Bytes()...)) 568 569 //不是第一次造币 570 if cntBytes != nil { 571 cnt = int64(binary.BigEndian.Uint64(cntBytes)) + 1 572 } 573 } 574 } 575 576 newCntBytes := make([]byte, 8) 577 newEpochBytes := make([]byte, 8) 578 binary.BigEndian.PutUint64(newEpochBytes, uint64(newEpoch)) 579 binary.BigEndian.PutUint64(newCntBytes, uint64(cnt)) 580 dposContext.MintCntTrie().TryUpdate(append(newEpochBytes, validator.Bytes()...), newCntBytes) 581 }