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