github.com/sberex/go-sberex@v1.8.2-0.20181113200658-ed96ac38f7d7/miner/unconfirmed.go (about) 1 // This file is part of the go-sberex library. The go-sberex library is 2 // free software: you can redistribute it and/or modify it under the terms 3 // of the GNU Lesser General Public License as published by the Free 4 // Software Foundation, either version 3 of the License, or (at your option) 5 // any later version. 6 // 7 // The go-sberex library is distributed in the hope that it will be useful, 8 // but WITHOUT ANY WARRANTY; without even the implied warranty of 9 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser 10 // General Public License <http://www.gnu.org/licenses/> for more details. 11 12 package miner 13 14 import ( 15 "container/ring" 16 "sync" 17 18 "github.com/Sberex/go-sberex/common" 19 "github.com/Sberex/go-sberex/core/types" 20 "github.com/Sberex/go-sberex/log" 21 ) 22 23 // headerRetriever is used by the unconfirmed block set to verify whether a previously 24 // mined block is part of the canonical chain or not. 25 type headerRetriever interface { 26 // GetHeaderByNumber retrieves the canonical header associated with a block number. 27 GetHeaderByNumber(number uint64) *types.Header 28 } 29 30 // unconfirmedBlock is a small collection of metadata about a locally mined block 31 // that is placed into a unconfirmed set for canonical chain inclusion tracking. 32 type unconfirmedBlock struct { 33 index uint64 34 hash common.Hash 35 } 36 37 // unconfirmedBlocks implements a data structure to maintain locally mined blocks 38 // have have not yet reached enough maturity to guarantee chain inclusion. It is 39 // used by the miner to provide logs to the user when a previously mined block 40 // has a high enough guarantee to not be reorged out of the canonical chain. 41 type unconfirmedBlocks struct { 42 chain headerRetriever // Blockchain to verify canonical status through 43 depth uint // Depth after which to discard previous blocks 44 blocks *ring.Ring // Block infos to allow canonical chain cross checks 45 lock sync.RWMutex // Protects the fields from concurrent access 46 } 47 48 // newUnconfirmedBlocks returns new data structure to track currently unconfirmed blocks. 49 func newUnconfirmedBlocks(chain headerRetriever, depth uint) *unconfirmedBlocks { 50 return &unconfirmedBlocks{ 51 chain: chain, 52 depth: depth, 53 } 54 } 55 56 // Insert adds a new block to the set of unconfirmed ones. 57 func (set *unconfirmedBlocks) Insert(index uint64, hash common.Hash) { 58 // If a new block was mined locally, shift out any old enough blocks 59 set.Shift(index) 60 61 // Create the new item as its own ring 62 item := ring.New(1) 63 item.Value = &unconfirmedBlock{ 64 index: index, 65 hash: hash, 66 } 67 // Set as the initial ring or append to the end 68 set.lock.Lock() 69 defer set.lock.Unlock() 70 71 if set.blocks == nil { 72 set.blocks = item 73 } else { 74 set.blocks.Move(-1).Link(item) 75 } 76 // Display a log for the user to notify of a new mined block unconfirmed 77 log.Info("🔨 mined potential block", "number", index, "hash", hash) 78 } 79 80 // Shift drops all unconfirmed blocks from the set which exceed the unconfirmed sets depth 81 // allowance, checking them against the canonical chain for inclusion or staleness 82 // report. 83 func (set *unconfirmedBlocks) Shift(height uint64) { 84 set.lock.Lock() 85 defer set.lock.Unlock() 86 87 for set.blocks != nil { 88 // Retrieve the next unconfirmed block and abort if too fresh 89 next := set.blocks.Value.(*unconfirmedBlock) 90 if next.index+uint64(set.depth) > height { 91 break 92 } 93 // Block seems to exceed depth allowance, check for canonical status 94 header := set.chain.GetHeaderByNumber(next.index) 95 switch { 96 case header == nil: 97 log.Warn("Failed to retrieve header of mined block", "number", next.index, "hash", next.hash) 98 case header.Hash() == next.hash: 99 log.Info("🔗 block reached canonical chain", "number", next.index, "hash", next.hash) 100 default: 101 log.Info("⑂ block became a side fork", "number", next.index, "hash", next.hash) 102 } 103 // Drop the block out of the ring 104 if set.blocks.Value == set.blocks.Next().Value { 105 set.blocks = nil 106 } else { 107 set.blocks = set.blocks.Move(-1) 108 set.blocks.Unlink(1) 109 set.blocks = set.blocks.Move(1) 110 } 111 } 112 }