github.com/FUSIONFoundation/efsn@v3.6.2-0.20200916075423-dbb5dd5d2cc7+incompatible/consensus/datong/consensus.go (about) 1 package datong 2 3 import ( 4 "bytes" 5 "encoding/base64" 6 "encoding/json" 7 "errors" 8 "fmt" 9 "math/big" 10 "sort" 11 "sync" 12 "time" 13 14 "github.com/FusionFoundation/efsn/accounts" 15 "github.com/FusionFoundation/efsn/common" 16 "github.com/FusionFoundation/efsn/common/hexutil" 17 "github.com/FusionFoundation/efsn/consensus" 18 "github.com/FusionFoundation/efsn/core/rawdb" 19 "github.com/FusionFoundation/efsn/core/state" 20 "github.com/FusionFoundation/efsn/core/types" 21 "github.com/FusionFoundation/efsn/crypto" 22 "github.com/FusionFoundation/efsn/crypto/sha3" 23 "github.com/FusionFoundation/efsn/ethdb" 24 "github.com/FusionFoundation/efsn/log" 25 "github.com/FusionFoundation/efsn/params" 26 "github.com/FusionFoundation/efsn/rlp" 27 "github.com/FusionFoundation/efsn/rpc" 28 29 cmath "github.com/FusionFoundation/efsn/common/math" 30 ) 31 32 const ( 33 delayTimeModifier = 15 // adjust factor 34 adjustIntervalBlocks = 10 // adjust delay time by blocks 35 36 maxNumberOfDeletedTickets = 7 // maximum number of tickets to be deleted because not mining block in time 37 ) 38 39 var ( 40 errUnknownBlock = errors.New("unknown block") 41 42 errCoinbase = errors.New("error coinbase") 43 44 errMissingVanity = errors.New("extra-data 32 byte vanity prefix missing") 45 46 errMissingSignature = errors.New("extra-data 65 byte suffix signature missing") 47 48 errUnauthorized = errors.New("unauthorized") 49 50 ErrNoTicket = errors.New("Miner doesn't have ticket") 51 ) 52 53 // SignerFn is a signer callback function to request a hash to be signed by a 54 // backing account. 55 type SignerFn func(accounts.Account, []byte) ([]byte, error) 56 57 var ( 58 extraVanity = 32 59 extraSeal = 65 60 MinBlockTime int64 = 7 // 7 seconds 61 maxBlockTime uint64 = 600 // 10 minutes 62 ) 63 64 // DaTong wacom 65 type DaTong struct { 66 config *params.DaTongConfig 67 db ethdb.Database 68 stateCache state.Database 69 70 signer common.Address 71 signFn SignerFn 72 lock sync.RWMutex 73 } 74 75 // New wacom 76 func New(config *params.DaTongConfig, db ethdb.Database) *DaTong { 77 return &DaTong{ 78 config: config, 79 db: db, 80 stateCache: nil, 81 } 82 } 83 84 // Authorize wacom 85 func (dt *DaTong) Authorize(signer common.Address, signFn SignerFn) { 86 dt.lock.Lock() 87 defer dt.lock.Unlock() 88 dt.signer = signer 89 dt.signFn = signFn 90 } 91 92 // Author retrieves the Ethereum address of the account that minted the given 93 // block, which may be different from the header's coinbase if a consensus 94 // engine is based on signatures. 95 func (dt *DaTong) Author(header *types.Header) (common.Address, error) { 96 return header.Coinbase, nil 97 } 98 99 func VerifySignature(header *types.Header) error { 100 signature := header.Extra[len(header.Extra)-extraSeal:] 101 pubkey, err := crypto.Ecrecover(sigHash(header).Bytes(), signature) 102 if err != nil { 103 return err 104 } 105 var signer common.Address 106 copy(signer[:], crypto.Keccak256(pubkey[1:])[12:]) 107 if header.Coinbase != signer { 108 return errors.New("Coinbase is not the signer") 109 } 110 return nil 111 } 112 113 // VerifyHeader checks whether a header conforms to the consensus rules of the 114 // stock Ethereum ethash engine. 115 func (dt *DaTong) verifyHeader(chain consensus.ChainReader, header *types.Header, seal bool, parents []*types.Header) error { 116 if header.Number == nil || header.Number.Sign() == 0 { 117 return errUnknownBlock 118 } 119 // Checkpoint blocks need to enforce zero beneficiary 120 if header.Coinbase == (common.Address{}) { 121 return errCoinbase 122 } 123 if len(header.Extra) < extraVanity { 124 return errMissingVanity 125 } 126 if len(header.Extra) < extraVanity+extraSeal { 127 return errMissingSignature 128 } 129 if header.Time.Cmp(big.NewInt(time.Now().Unix())) > 0 { 130 return consensus.ErrFutureBlock 131 } 132 // verify Ancestor 133 parent, err := getParent(chain, header, parents) 134 if err != nil { 135 return err 136 } 137 // verify pos hash 138 if common.GetPoSHashVersion(header.Number) < common.PosV2 { 139 if header.UncleHash != types.EmptyUncleHash { 140 return fmt.Errorf("non empty uncle hash") 141 } 142 } else if header.UncleHash != posHash(parent) { 143 return fmt.Errorf("PoS hash mismatch: have %x, want %x", header.UncleHash, posHash(parent)) 144 } 145 // verify header time 146 if header.Time.Int64()-parent.Time.Int64() < MinBlockTime { 147 return fmt.Errorf("block %v header.Time:%v < parent.Time:%v + %v Second", 148 header.Number, header.Time.Int64(), parent.Time.Int64(), MinBlockTime) 149 150 } 151 // verify signature 152 if err := VerifySignature(header); err != nil { 153 return err 154 } 155 // check block time 156 if err = dt.checkBlockTime(chain, header, parent); err != nil { 157 return err 158 } 159 if isInRange, err := CheckPoint(chain.Config().ChainID, header.Number.Uint64(), header.Hash()); isInRange { 160 if err == nil { 161 selected, retreat, err := dt.getSelectedAndRetreatedTickets(chain, header, parent) 162 if err != nil { 163 return err 164 } 165 // assign selected and retreated tickets (used in Finalize) 166 header.SetSelectedTicket(selected) 167 header.SetRetreatTickets(retreat) 168 } 169 return err 170 } 171 return dt.verifySeal(chain, header, parent) 172 } 173 174 // VerifyHeader checks whether a header conforms to the consensus rules of the 175 // stock Ethereum ethash engine. 176 func (dt *DaTong) VerifyHeader(chain consensus.ChainReader, header *types.Header, seal bool) error { 177 return dt.verifyHeader(chain, header, seal, glb_parents) 178 } 179 180 // VerifyHeaders is similar to VerifyHeader, but verifies a batch of headers 181 // concurrently. The method returns a quit channel to abort the operations and 182 // a results channel to retrieve the async verifications. 183 func (dt *DaTong) VerifyHeaders(chain consensus.ChainReader, headers []*types.Header, seals []bool) (chan<- struct{}, <-chan error) { 184 abort := make(chan struct{}) 185 results := make(chan error, len(headers)) 186 go func() { 187 for i, header := range headers { 188 err := dt.verifyHeader(chain, header, seals[i], headers[:i]) 189 select { 190 case <-abort: 191 return 192 case results <- err: 193 } 194 } 195 }() 196 197 return abort, results 198 } 199 200 // VerifyUncles verifies that the given block's uncles conform to the consensus 201 // rules of the stock Ethereum ethash engine. 202 func (dt *DaTong) VerifyUncles(chain consensus.ChainReader, block *types.Block) error { 203 return nil 204 } 205 206 // VerifySeal implements consensus.Engine, checking whether the signature contained 207 // in the header satisfies the consensus protocol requirements. 208 func (c *DaTong) VerifySeal(chain consensus.ChainReader, header *types.Header) error { 209 return c.verifySeal(chain, header, nil) 210 } 211 212 var glb_parents []*types.Header 213 214 func SetHeaders(parents []*types.Header) { 215 glb_parents = parents 216 } 217 218 func getParent(chain consensus.ChainReader, header *types.Header, parents []*types.Header) (*types.Header, error) { 219 number := header.Number.Uint64() 220 var parent *types.Header 221 if parents != nil && len(parents) > 0 { 222 parent = parents[len(parents)-1] 223 } else { 224 parent = chain.GetHeader(header.ParentHash, number-1) 225 } 226 if parent == nil || parent.Number.Uint64() != number-1 || parent.Hash() != header.ParentHash { 227 return nil, consensus.ErrUnknownAncestor 228 } 229 return parent, nil 230 } 231 232 // VerifySeal checks whether the crypto seal on a header is valid according to 233 // the consensus rules of the given engine. 234 func (dt *DaTong) verifySeal(chain consensus.ChainReader, header *types.Header, parent *types.Header) error { 235 // verify ticket 236 snap, err := NewSnapshotFromHeader(header) 237 if err != nil { 238 return err 239 } 240 // verify ticket: list squence, ID , ticket Info, difficulty 241 diff, tk, listSq, retreat, errv := dt.calcBlockDifficulty(chain, header, parent) 242 if errv != nil { 243 return errv 244 } 245 // verify ticket with signer 246 if tk.Owner != header.Coinbase { 247 return errors.New("Coinbase is not the voted ticket owner") 248 } 249 // check ticket ID 250 if tk.ID != snap.Selected { 251 return fmt.Errorf("verifySeal ticketID mismatch, have %v, want %v", snap.Selected.String(), tk.ID.String()) 252 } 253 if common.IsHeaderSnapCheckingEnabled(header.Number) { 254 // check retreat tickets 255 if len(retreat) != len(snap.Retreat) { 256 return fmt.Errorf("verifySeal retreat tickets count mismatch") 257 } 258 for i := 0; i < len(retreat); i++ { 259 if retreat[i].ID != snap.Retreat[i] { 260 return fmt.Errorf("verifySeal retreat tickets mismatch") 261 } 262 } 263 } 264 // check ticket info 265 errt := dt.checkTicketInfo(header, tk) 266 if errt != nil { 267 return errt 268 } 269 // check ticket order 270 if header.Nonce != types.EncodeNonce(listSq) { 271 return fmt.Errorf("verifySeal ticket order mismatch, have %v, want %v", header.Nonce.Uint64(), listSq) 272 } 273 274 // check difficulty 275 if diff.Cmp(header.Difficulty) != 0 { 276 return fmt.Errorf("verifySeal difficulty mismatch, have %v, want %v", header.Difficulty, diff) 277 } 278 279 return nil 280 } 281 282 // Prepare initializes the consensus fields of a block header according to the 283 // rules of a particular engine. The changes are executed inline. 284 func (dt *DaTong) Prepare(chain consensus.ChainReader, header *types.Header) error { 285 number := header.Number.Uint64() 286 parent := chain.GetHeader(header.ParentHash, number-1) 287 if parent == nil { 288 return consensus.ErrUnknownAncestor 289 } 290 if len(header.Extra) < extraVanity { 291 header.Extra = append(header.Extra, bytes.Repeat([]byte{0x00}, extraVanity-len(header.Extra))...) 292 } 293 header.Extra = header.Extra[:extraVanity] 294 header.Extra = append(header.Extra, make([]byte, extraSeal)...) 295 if common.GetPoSHashVersion(header.Number) < common.PosV2 { 296 header.UncleHash = types.EmptyUncleHash 297 } else { 298 header.UncleHash = posHash(parent) 299 } 300 difficulty, _, order, _, err := dt.calcBlockDifficulty(chain, header, parent) 301 if err != nil { 302 return err 303 } 304 header.Nonce = types.EncodeNonce(order) 305 header.Difficulty = difficulty 306 // adjust block time if illegal 307 if order > 0 { 308 recvTime := header.Time.Int64() - parent.Time.Int64() 309 maxDelaySeconds := int64(maxBlockTime + dt.config.Period) 310 if recvTime < maxDelaySeconds { 311 expectTime := int64(dt.config.Period + order*delayTimeModifier) 312 if recvTime < expectTime { 313 if expectTime > maxDelaySeconds { 314 expectTime = maxDelaySeconds 315 } 316 header.Time = big.NewInt(parent.Time.Int64() + expectTime) 317 } 318 } 319 } 320 return nil 321 } 322 323 type DisInfo struct { 324 tk *common.Ticket 325 res *big.Int 326 } 327 type DistanceSlice []*DisInfo 328 329 func (s DistanceSlice) Len() int { 330 return len(s) 331 } 332 333 func (s DistanceSlice) Less(i, j int) bool { 334 return s[i].res.Cmp(s[j].res) < 0 335 } 336 337 func (s DistanceSlice) Swap(i, j int) { 338 s[i], s[j] = s[j], s[i] 339 } 340 341 // Finalize runs any post-transaction state modifications (e.g. block rewards) 342 // and assembles the final block. 343 // Note: The block header and state database might be updated to reflect any 344 // consensus rules that happen at finalization (e.g. block rewards). 345 func (dt *DaTong) Finalize(chain consensus.ChainReader, header *types.Header, statedb *state.StateDB, txs []*types.Transaction, 346 uncles []*types.Header, receipts []*types.Receipt) (*types.Block, error) { 347 parent, err := getParent(chain, header, glb_parents) 348 if err != nil { 349 return nil, err 350 } 351 selected := header.GetSelectedTicket() 352 retreat := header.GetRetreatTickets() 353 if selected == nil { 354 log.Warn("Finalize shouldn't calc difficulty, as it's done in VerifyHeader or Prepare") 355 common.DebugCall(func() { 356 panic("Finalize shouldn't calc difficulty, as it's done in VerifyHeader or Prepare") 357 }) 358 _, selected, _, retreat, err = dt.calcBlockDifficulty(chain, header, parent) 359 if err != nil { 360 return nil, err 361 } 362 } 363 364 snap := newSnapshot() 365 isInMining := header.MixDigest == (common.Hash{}) 366 367 //update tickets 368 headerState := statedb 369 tickets, err := headerState.AllTickets() 370 if err != nil { 371 return nil, err 372 } 373 numTickets := tickets.NumberOfTickets() 374 if numTickets <= 1 { 375 log.Warn("Next block doesn't have ticket, wait buy ticket") 376 return nil, errors.New("Next block doesn't have ticket, wait buy ticket") 377 } 378 379 returnTicket := func(ticket *common.Ticket) { 380 if ticket.ExpireTime <= header.Time.Uint64() { 381 return 382 } 383 value := common.NewTimeLock(&common.TimeLockItem{ 384 StartTime: ticket.StartTime, 385 EndTime: ticket.ExpireTime, 386 Value: ticket.Value(), 387 }) 388 headerState.AddTimeLockBalance(ticket.Owner, common.SystemAssetID, value, header.Number, header.Time.Uint64()) 389 } 390 391 deleteTicket := func(ticket *common.Ticket, logType ticketLogType, returnBack bool) { 392 id := ticket.ID 393 headerState.RemoveTicket(id) 394 snap.AddLog(&ticketLog{ 395 TicketID: id, 396 Type: logType, 397 }) 398 if returnBack { 399 returnTicket(ticket) 400 } 401 } 402 403 deleteTicket(selected, ticketSelect, !selected.IsInGenesis()) 404 405 //delete tickets before coinbase if selected miner did not Seal 406 for i, t := range retreat { 407 if !isInMining && i == 0 { 408 common.DebugInfo("retreat ticket", "nonce", header.Nonce.Uint64(), "id", retreat[0].ID.String(), "owner", retreat[0].Owner, "blockHeight", header.Number, "ticketHeight", retreat[0].Height) 409 } 410 deleteTicket(t, ticketRetreat, !(t.IsInGenesis() || i == 0)) 411 } 412 413 if common.IsVote1ForkBlock(header.Number) { 414 ApplyVote1HardFork(headerState, header.Number, parent.Time.Uint64()) 415 } 416 417 hash, err := headerState.UpdateTickets(header.Number, parent.Time.Uint64()) 418 if err != nil { 419 return nil, errors.New("UpdateTickets failed: " + err.Error()) 420 } 421 422 snap.SetTicketNumber(int(headerState.TotalNumberOfTickets())) 423 snapBytes := snap.Bytes() 424 425 if isInMining { 426 header.MixDigest = hash 427 header.Extra = header.Extra[:extraVanity] 428 header.Extra = append(header.Extra, snapBytes...) 429 header.Extra = append(header.Extra, make([]byte, extraSeal)...) 430 } else { 431 if header.MixDigest != hash { 432 return nil, fmt.Errorf("MixDigest mismatch, have:%v, want:%v", header.MixDigest, hash) 433 } 434 if common.IsHeaderSnapCheckingEnabled(header.Number) { 435 if !bytes.Equal(getSnapData(header.Extra), snapBytes) { 436 return nil, fmt.Errorf("snapBytes in Extra mismatch") 437 } 438 } 439 } 440 441 headerState.AddBalance(header.Coinbase, common.SystemAssetID, CalcRewards(header.Number)) 442 header.Root = headerState.IntermediateRoot(chain.Config().IsEIP158(header.Number)) 443 return types.NewBlock(header, txs, nil, receipts), nil 444 } 445 446 // Seal generates a new sealing request for the given input block and pushes 447 // the result into the given channel. 448 // 449 // Note, the method returns immediately and will send the result async. More 450 // than one result may also be returned depending on the consensus algorothm. 451 func (dt *DaTong) Seal(chain consensus.ChainReader, block *types.Block, results chan<- *types.Block, stop <-chan struct{}) error { 452 header := block.Header() 453 number := header.Number.Uint64() 454 if number == 0 { 455 return errUnknownBlock 456 } 457 dt.lock.RLock() 458 signer, signFn := dt.signer, dt.signFn 459 dt.lock.RUnlock() 460 461 if signer != header.Coinbase { 462 return errors.New("Mismatched Signer and Coinbase") 463 } 464 465 // delay time decide block time 466 delay, errc := dt.calcDelayTime(chain, header) 467 if errc != nil { 468 return errc 469 } 470 471 sighash, err := signFn(accounts.Account{Address: header.Coinbase}, sigHash(header).Bytes()) 472 if err != nil { 473 return err 474 } 475 copy(header.Extra[len(header.Extra)-extraSeal:], sighash) 476 477 go func() { 478 select { 479 case <-stop: 480 return 481 case <-time.After(delay): 482 } 483 484 select { 485 case results <- block.WithSeal(header): 486 default: 487 log.Warn("Sealing result is not read by miner", "sealhash", dt.SealHash(header)) 488 } 489 }() 490 491 return nil 492 } 493 494 // SealHash returns the hash of a block prior to it being sealed. 495 func (dt *DaTong) SealHash(header *types.Header) (hash common.Hash) { 496 hasher := sha3.NewKeccak256() 497 rlp.Encode(hasher, []interface{}{ 498 header.ParentHash, 499 header.UncleHash, 500 header.Coinbase, 501 header.Root, 502 header.TxHash, 503 header.ReceiptHash, 504 header.Bloom, 505 header.Difficulty, 506 header.Number, 507 header.GasLimit, 508 header.GasUsed, 509 header.Extra[:extraVanity], 510 header.MixDigest, 511 header.Nonce, 512 }) 513 hasher.Sum(hash[:0]) 514 return hash 515 } 516 517 // CalcDifficulty is the difficulty adjustment algorithm. It returns the difficulty 518 // that a new block should have. 519 func (dt *DaTong) CalcDifficulty(chain consensus.ChainReader, time uint64, parent *types.Header) *big.Int { 520 return nil 521 } 522 523 // APIs returns the RPC APIs this consensus engine provides. 524 func (dt *DaTong) APIs(chain consensus.ChainReader) []rpc.API { 525 return []rpc.API{{ 526 Namespace: "fsn", 527 Version: "1.0", 528 Service: &API{chain: chain}, 529 Public: false, 530 }} 531 } 532 533 // Close terminates any background threads maintained by the consensus engine. 534 func (dt *DaTong) Close() error { 535 return nil 536 } 537 538 func (dt *DaTong) getAllTickets(chain consensus.ChainReader, header *types.Header) (common.TicketsDataSlice, error) { 539 if ts := state.GetCachedTickets(header.MixDigest); ts != nil { 540 return ts, nil 541 } 542 statedb, err := state.New(header.Root, header.MixDigest, dt.stateCache) 543 if err == nil { 544 return statedb.AllTickets() 545 } else if header.Number.Uint64() == 0 { 546 return nil, err 547 } 548 549 // get tickets from past state 550 var tickets common.TicketsDataSlice 551 parent := header 552 parents := []*types.Header{parent} 553 for { 554 if parent = chain.GetHeader(parent.ParentHash, parent.Number.Uint64()-1); parent == nil { 555 return nil, fmt.Errorf("Can not find parent, number=%v, hash=%v", parent.Number.Uint64()-1, parent.ParentHash.String()) 556 } 557 statedb, err = state.New(parent.Root, parent.MixDigest, dt.stateCache) 558 if err == nil { 559 if tickets, err = statedb.AllTickets(); err != nil { 560 return nil, err 561 } 562 break 563 } else if parent.Number.Uint64() == 0 { 564 return nil, err 565 } 566 parents = append(parents, parent) 567 } 568 log.Info("getAllTickets find tickets from past state", "current", header.Number, "past", parent.Number) 569 defer func(bstart time.Time) { 570 common.DebugInfo("getAllTickets from past state spend time", "duration", common.PrettyDuration(time.Since(bstart))) 571 }(time.Now()) 572 573 // deduct the current tickets 574 getFuncType := func(l *types.Log) uint8 { 575 switch l.Address { 576 case common.FSNCallAddress: 577 if len(l.Topics) > 0 { 578 topic := l.Topics[0] 579 return topic[common.HashLength-1] 580 } 581 } 582 return 0xff 583 } 584 processBuyTicketLog := func(l *types.Log) error { 585 maps := make(map[string]interface{}) 586 err := json.Unmarshal(l.Data, &maps) 587 if err != nil { 588 return err 589 } 590 591 if _, hasError := maps["Error"]; hasError { 592 return nil 593 } 594 595 idstr, idok := maps["TicketID"].(string) 596 ownerstr, ownerok := maps["TicketOwner"].(string) 597 datastr, dataok := maps["Base"].(string) 598 if !idok || !ownerok || !dataok { 599 return errors.New("buy ticket log has wrong data") 600 } 601 602 data, err := base64.StdEncoding.DecodeString(datastr) 603 if err != nil { 604 return err 605 } 606 607 buyTicketParam := common.BuyTicketParam{} 608 rlp.DecodeBytes(data, &buyTicketParam) 609 610 ticket := &common.Ticket{ 611 Owner: common.HexToAddress(ownerstr), 612 TicketBody: common.TicketBody{ 613 ID: common.HexToHash(idstr), 614 Height: l.BlockNumber, 615 StartTime: buyTicketParam.Start, 616 ExpireTime: buyTicketParam.End, 617 }, 618 } 619 tickets, err = tickets.AddTicket(ticket) 620 return err 621 } 622 processReportLog := func(l *types.Log) error { 623 maps := make(map[string]interface{}) 624 err := json.Unmarshal(l.Data, &maps) 625 if err != nil { 626 return err 627 } 628 629 if _, hasError := maps["Error"]; hasError { 630 return nil 631 } 632 633 ids, idsok := maps["DeleteTickets"].(string) 634 if !idsok { 635 return fmt.Errorf("report log has wrong data") 636 } 637 638 bs, err := hexutil.Decode(ids) 639 if err != nil { 640 return fmt.Errorf("decode hex data error: %v", err) 641 } 642 delTickets := []common.Hash{} 643 if err := rlp.DecodeBytes(bs, &delTickets); err != nil { 644 return fmt.Errorf("decode report log error: %v", err) 645 } 646 647 for _, id := range delTickets { 648 tickets, err = tickets.RemoveTicket(id) 649 if err != nil { 650 return err 651 } 652 } 653 654 return nil 655 } 656 processLog := func(l *types.Log) error { 657 funcType := getFuncType(l) 658 switch funcType { 659 case common.BuyTicketFunc: 660 if err := processBuyTicketLog(l); err != nil { 661 return err 662 } 663 case common.ReportIllegalFunc: 664 if err := processReportLog(l); err != nil { 665 return err 666 } 667 } 668 return nil 669 } 670 processSnap := func(h *types.Header) error { 671 snap, err := NewSnapshotFromHeader(h) 672 if err != nil { 673 return err 674 } 675 tickets, err = tickets.RemoveTicket(snap.Selected) 676 if err != nil { 677 return err 678 } 679 for _, id := range snap.Retreat { 680 tickets, err = tickets.RemoveTicket(id) 681 if err != nil { 682 return err 683 } 684 } 685 return nil 686 } 687 688 for i := len(parents) - 1; i >= 0; i-- { 689 hash := parents[i].Hash() 690 if number := rawdb.ReadHeaderNumber(dt.db, hash); number != nil { 691 receipts := rawdb.ReadReceipts(dt.db, hash, *number) 692 for _, receipt := range receipts { 693 for _, log := range receipt.Logs { 694 if err := processLog(log); err != nil { 695 return nil, err 696 } 697 } 698 } 699 } 700 if err := processSnap(parents[i]); err != nil { 701 return nil, err 702 } 703 } 704 705 tickets, err = tickets.ClearExpiredTickets(header.Time.Uint64()) 706 if err != nil { 707 return nil, err 708 } 709 if err := state.AddCachedTickets(header.MixDigest, tickets); err != nil { 710 return nil, err 711 } 712 return tickets, nil 713 } 714 715 func sigHash(header *types.Header) (hash common.Hash) { 716 hasher := sha3.NewKeccak256() 717 rlp.Encode(hasher, []interface{}{ 718 header.ParentHash, 719 header.UncleHash, 720 header.Coinbase, 721 header.Root, 722 header.TxHash, 723 header.ReceiptHash, 724 header.Bloom, 725 header.Difficulty, 726 header.Number, 727 header.GasLimit, 728 header.GasUsed, 729 header.Time, 730 header.Extra[:len(header.Extra)-extraSeal], 731 header.MixDigest, 732 header.Nonce, 733 }) 734 hasher.Sum(hash[:0]) 735 return hash 736 } 737 738 func getSnapDataByHeader(header *types.Header) []byte { 739 return getSnapData(header.Extra) 740 } 741 742 func getSnapData(data []byte) []byte { 743 extraSuffix := len(data) - extraSeal 744 if extraSuffix < extraVanity { 745 return []byte{} 746 } 747 return data[extraVanity:extraSuffix] 748 } 749 750 func CalcRewards(height *big.Int) *big.Int { 751 var i int64 752 div2 := big.NewInt(2) 753 // initial reward 2.5 754 var reward = new(big.Int).Mul(big.NewInt(25), big.NewInt(100000000000000000)) 755 // every 4915200 blocks divide reward by 2 756 segment := new(big.Int).Div(height, new(big.Int).SetUint64(4915200)) 757 for i = 0; i < segment.Int64(); i++ { 758 reward = new(big.Int).Div(reward, div2) 759 } 760 return reward 761 } 762 763 // get rid of header.Extra[0:extraVanity] of user custom data 764 func posHash(header *types.Header) (hash common.Hash) { 765 hasher := sha3.NewKeccak256() 766 switch common.GetPoSHashVersion(header.Number) { 767 case common.PosV1: 768 rlp.Encode(hasher, []interface{}{ 769 header.ParentHash, 770 header.UncleHash, 771 header.Coinbase, 772 header.Root, 773 header.TxHash, 774 header.ReceiptHash, 775 header.Bloom, 776 header.Difficulty, 777 header.Number, 778 header.GasLimit, 779 header.GasUsed, 780 header.Time, 781 header.Extra[extraVanity : len(header.Extra)-extraSeal], 782 header.MixDigest, 783 header.Nonce, 784 }) 785 case common.PosV2: 786 rlp.Encode(hasher, []interface{}{ 787 header.UncleHash, 788 header.Coinbase, 789 header.Difficulty, 790 header.Number, 791 (header.Time.Uint64() >> 5) << 5, 792 header.Extra[extraVanity : len(header.Extra)-extraSeal], 793 header.MixDigest, 794 header.Nonce, 795 }) 796 case common.PosV3: 797 rlp.Encode(hasher, []interface{}{ 798 header.UncleHash, 799 header.Coinbase, 800 header.Difficulty, 801 header.Number, 802 (header.Time.Uint64() >> 5) << 5, 803 header.Extra[extraVanity : len(header.Extra)-extraSeal], 804 header.Nonce, 805 }) 806 } 807 hasher.Sum(hash[:0]) 808 return hash 809 } 810 811 type DisInfoWithIndex struct { 812 index int 813 info *DisInfo 814 } 815 816 func calcDisInfo(ind int, tickets common.TicketsData, parent *types.Header, ch chan *DisInfoWithIndex) { 817 posHash := posHash(parent) 818 owner := tickets.Owner 819 820 var minTicket common.TicketBody 821 var minDist *big.Int 822 for _, t := range tickets.Tickets { 823 w := new(big.Int).SetUint64(parent.Number.Uint64() - t.Height + 1) 824 w2 := new(big.Int).Mul(w, w) 825 826 id := new(big.Int).SetBytes(crypto.Keccak256(posHash[:], t.ID[:], []byte(owner.Hex()))) 827 id2 := new(big.Int).Mul(id, id) 828 s := new(big.Int).Add(w2, id2) 829 830 if minDist == nil || s.Cmp(minDist) < 0 { 831 minTicket = t 832 minDist = s 833 } 834 } 835 ticket := &common.Ticket{ 836 Owner: owner, 837 TicketBody: minTicket, 838 } 839 result := &DisInfoWithIndex{index: ind, info: &DisInfo{tk: ticket, res: minDist}} 840 ch <- result 841 } 842 843 func (dt *DaTong) calcBlockDifficulty(chain consensus.ChainReader, header *types.Header, parent *types.Header) (*big.Int, *common.Ticket, uint64, common.TicketPtrSlice, error) { 844 if header.GetSelectedTicket() != nil { 845 return header.Difficulty, header.GetSelectedTicket(), header.Nonce.Uint64(), header.GetRetreatTickets(), nil 846 } 847 parentTickets, err := dt.getAllTickets(chain, parent) 848 if err != nil { 849 return nil, nil, 0, nil, err 850 } 851 haveTicket := false 852 for _, v := range parentTickets { 853 if v.Owner == header.Coinbase { 854 haveTicket = true 855 break 856 } 857 } 858 if !haveTicket { 859 return nil, nil, 0, nil, ErrNoTicket 860 } 861 ticketsTotalAmount, numberOfticketOwners := parentTickets.NumberOfTicketsAndOwners() 862 863 // calc balance before selected ticket from stored tickets list 864 var ( 865 selected *common.Ticket 866 retreat common.TicketPtrSlice 867 ) 868 869 // make consensus by tickets sequence(selectedTime) with: parentHash, weigth, ticketID, coinbase 870 ch := make(chan *DisInfoWithIndex, numberOfticketOwners) 871 list := make(DistanceSlice, numberOfticketOwners) 872 for k, v := range parentTickets { 873 go calcDisInfo(k, v, parent, ch) 874 } 875 for i := 0; i < int(numberOfticketOwners); i++ { 876 v := <-ch 877 list[v.index] = v.info 878 } 879 close(ch) 880 sort.Sort(list) 881 selectedTime := uint64(0) 882 for i, t := range list { 883 owner := t.tk.Owner 884 if owner == header.Coinbase { 885 selected = t.tk 886 break 887 } else { 888 selectedTime++ 889 if i < maxNumberOfDeletedTickets { 890 retreat = append(retreat, t.tk) // one miner one selected ticket 891 } 892 } 893 } 894 if selected == nil { 895 return nil, nil, 0, nil, errors.New("myself tickets not selected in maxBlockTime") 896 } 897 898 // cacl difficulty 899 difficulty := new(big.Int).SetUint64(ticketsTotalAmount - selectedTime) 900 if selectedTime > 0 { 901 // base10 = base * 10 (base > 1) 902 base10 := int64(16) 903 // exponent = max(selectedTime, 50) 904 exponent := int64(selectedTime) 905 if exponent > 50 { 906 exponent = 50 907 } 908 // difficulty = ticketsTotal * pow(10, exponent) / pow(base10, exponent) 909 difficulty = new(big.Int).Div( 910 new(big.Int).Mul(difficulty, cmath.BigPow(10, exponent)), 911 cmath.BigPow(base10, exponent)) 912 if difficulty.Cmp(common.Big1) < 0 { 913 difficulty = common.Big1 914 } 915 } 916 adjust := new(big.Int).SetUint64(numberOfticketOwners - selectedTime) 917 difficulty = new(big.Int).Add(difficulty, adjust) 918 919 header.SetSelectedTicket(selected) 920 header.SetRetreatTickets(retreat) 921 922 return difficulty, selected, selectedTime, retreat, nil 923 } 924 925 // PreProcess update state if needed from various block info 926 // used with some PoS Systems 927 func (c *DaTong) PreProcess(chain consensus.ChainReader, header *types.Header, statedb *state.StateDB) error { 928 return nil 929 } 930 931 func (dt *DaTong) calcDelayTime(chain consensus.ChainReader, header *types.Header) (time.Duration, error) { 932 list := header.Nonce.Uint64() 933 if list > 0 { 934 return time.Unix(header.Time.Int64(), 0).Sub(time.Now()), nil 935 } 936 937 // delayTime = ParentTime + (15 - 2) - time.Now 938 parent := chain.GetHeaderByNumber(header.Number.Uint64() - 1) 939 endTime := new(big.Int).Add(header.Time, new(big.Int).SetUint64(list*uint64(delayTimeModifier)+dt.config.Period-2)) 940 delayTime := time.Unix(endTime.Int64(), 0).Sub(time.Now()) 941 942 // delay maximum 943 if (new(big.Int).Sub(endTime, header.Time)).Uint64() > maxBlockTime { 944 endTime = new(big.Int).Add(header.Time, new(big.Int).SetUint64(maxBlockTime+dt.config.Period-2+list)) 945 delayTime = time.Unix(endTime.Int64(), 0).Sub(time.Now()) 946 } 947 if header.Number.Uint64() > (adjustIntervalBlocks + 1) { 948 // adjust = ( ( parent - gparent ) / 2 - (dt.config.Period) ) / dt.config.Period 949 gparent := chain.GetHeaderByNumber(header.Number.Uint64() - 1 - adjustIntervalBlocks) 950 adjust := ((time.Unix(parent.Time.Int64(), 0).Sub(time.Unix(gparent.Time.Int64(), 0)) / adjustIntervalBlocks) - 951 time.Duration(int64(dt.config.Period))*time.Second) / 952 time.Duration(int64(adjustIntervalBlocks)) 953 954 stampSecond := time.Duration(2) * time.Second 955 if adjust > stampSecond { 956 adjust = stampSecond 957 } else if adjust < -stampSecond { 958 adjust = -stampSecond 959 } 960 delayTime -= adjust 961 } 962 return delayTime, nil 963 } 964 965 // check ticket info 966 func (dt *DaTong) checkTicketInfo(header *types.Header, ticket *common.Ticket) error { 967 // check height 968 if ticket.BlockHeight().Cmp(header.Number) >= 0 { 969 return errors.New("checkTicketInfo ticket height mismatch") 970 } 971 // check start and expire time 972 if ticket.ExpireTime <= ticket.StartTime || 973 ticket.ExpireTime < (ticket.StartTime+30*24*3600) || 974 ticket.ExpireTime < header.Time.Uint64() { 975 return errors.New("checkTicketInfo ticket ExpireTime mismatch") 976 } 977 return nil 978 } 979 980 // check block time 981 func (dt *DaTong) checkBlockTime(chain consensus.ChainReader, header *types.Header, parent *types.Header) error { 982 list := header.Nonce.Uint64() 983 if list <= 0 { // No.1 pass, check others 984 return nil 985 } 986 recvTime := header.Time.Int64() - parent.Time.Int64() 987 maxDelaySeconds := int64(maxBlockTime + dt.config.Period) 988 if recvTime < maxDelaySeconds { 989 expectTime := int64(dt.config.Period + list*delayTimeModifier) 990 if recvTime < expectTime { 991 return fmt.Errorf("block time mismatch: order: %v, receive: %v, expect: %v.", list, recvTime, expectTime) 992 } 993 } 994 return nil 995 } 996 997 func (dt *DaTong) SetStateCache(stateCache state.Database) { 998 dt.stateCache = stateCache 999 } 1000 1001 func (dt *DaTong) getSelectedAndRetreatedTickets(chain consensus.ChainReader, header *types.Header, parent *types.Header) (*common.Ticket, common.TicketPtrSlice, error) { 1002 parentTickets, err := dt.getAllTickets(chain, parent) 1003 if err != nil { 1004 return nil, nil, err 1005 } 1006 snap, err := NewSnapshotFromHeader(header) 1007 if err != nil { 1008 return nil, nil, err 1009 } 1010 selectedTicket, err := parentTickets.Get(snap.Selected) 1011 if err != nil { 1012 return nil, nil, err 1013 } 1014 retreat := make(common.TicketPtrSlice, len(snap.Retreat)) 1015 for i, tid := range snap.Retreat { 1016 ticket, err := parentTickets.Get(tid) 1017 if err != nil { 1018 return nil, nil, err 1019 } 1020 retreat[i] = ticket 1021 } 1022 return selectedTicket, retreat, nil 1023 }