github.com/FusionFoundation/efsn/v4@v4.2.0/miner/unconfirmed.go (about) 1 // Copyright 2016 The go-ethereum Authors 2 // This file is part of the go-ethereum library. 3 // 4 // The go-ethereum 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 go-ethereum 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 go-ethereum library. If not, see <http://www.gnu.org/licenses/>. 16 17 package miner 18 19 import ( 20 "container/ring" 21 "sync" 22 23 "github.com/FusionFoundation/efsn/v4/common" 24 "github.com/FusionFoundation/efsn/v4/core/types" 25 "github.com/FusionFoundation/efsn/v4/log" 26 ) 27 28 // chainRetriever is used by the unconfirmed block set to verify whether a previously 29 // mined block is part of the canonical chain or not. 30 type chainRetriever interface { 31 // GetHeaderByNumber retrieves the canonical header associated with a block number. 32 GetHeaderByNumber(number uint64) *types.Header 33 34 // GetBlockByNumber retrieves the canonical block associated with a block number. 35 GetBlockByNumber(number uint64) *types.Block 36 } 37 38 // unconfirmedBlock is a small collection of metadata about a locally mined block 39 // that is placed into a unconfirmed set for canonical chain inclusion tracking. 40 type unconfirmedBlock struct { 41 index uint64 42 hash common.Hash 43 phash common.Hash 44 } 45 46 // unconfirmedBlocks implements a data structure to maintain locally mined blocks 47 // have have not yet reached enough maturity to guarantee chain inclusion. It is 48 // used by the miner to provide logs to the user when a previously mined block 49 // has a high enough guarantee to not be reorged out of the canonical chain. 50 type unconfirmedBlocks struct { 51 chain chainRetriever // Blockchain to verify canonical status through 52 depth uint // Depth after which to discard previous blocks 53 blocks *ring.Ring // Block infos to allow canonical chain cross checks 54 lock sync.RWMutex // Protects the fields from concurrent access 55 } 56 57 // newUnconfirmedBlocks returns new data structure to track currently unconfirmed blocks. 58 func newUnconfirmedBlocks(chain chainRetriever, depth uint) *unconfirmedBlocks { 59 return &unconfirmedBlocks{ 60 chain: chain, 61 depth: depth, 62 } 63 } 64 65 func (set *unconfirmedBlocks) Has(parentHash common.Hash) bool { 66 set.lock.RLock() 67 defer set.lock.RUnlock() 68 69 if set.blocks == nil { 70 return false 71 } 72 73 r := set.blocks 74 for i := 0; i < r.Len(); i++ { 75 item := r.Value.(*unconfirmedBlock) 76 if item.phash == parentHash { 77 return true 78 } 79 r = r.Prev() 80 } 81 82 return false 83 } 84 85 // Insert adds a new block to the set of unconfirmed ones. 86 func (set *unconfirmedBlocks) Insert(index uint64, hash common.Hash, parentHash common.Hash) { 87 // If a new block was mined locally, shift out any old enough blocks 88 set.Shift(index) 89 90 // Create the new item as its own ring 91 item := ring.New(1) 92 item.Value = &unconfirmedBlock{ 93 index: index, 94 hash: hash, 95 phash: parentHash, 96 } 97 // Set as the initial ring or append to the end 98 set.lock.Lock() 99 defer set.lock.Unlock() 100 101 if set.blocks == nil { 102 set.blocks = item 103 } else { 104 set.blocks.Move(-1).Link(item) 105 } 106 // Display a log for the user to notify of a new mined block unconfirmed 107 log.Info("🔨 mined potential block", "number", index, "hash", hash) 108 } 109 110 // Shift drops all unconfirmed blocks from the set which exceed the unconfirmed sets depth 111 // allowance, checking them against the canonical chain for inclusion or staleness 112 // report. 113 func (set *unconfirmedBlocks) Shift(height uint64) { 114 set.lock.Lock() 115 defer set.lock.Unlock() 116 117 for set.blocks != nil { 118 // Retrieve the next unconfirmed block and abort if too fresh 119 next := set.blocks.Value.(*unconfirmedBlock) 120 if next.index+uint64(set.depth) > height { 121 break 122 } 123 // Block seems to exceed depth allowance, check for canonical status 124 header := set.chain.GetHeaderByNumber(next.index) 125 switch { 126 case header == nil: 127 log.Warn("Failed to retrieve header of mined block", "number", next.index, "hash", next.hash) 128 case header.Hash() == next.hash: 129 log.Info("🔗 block reached canonical chain", "number", next.index, "hash", next.hash) 130 default: 131 log.Info("😱 block lost", "number", next.index, "hash", next.hash, "order", header.Nonce.Uint64()) 132 } 133 // Drop the block out of the ring 134 if set.blocks.Value == set.blocks.Next().Value { 135 set.blocks = nil 136 } else { 137 set.blocks = set.blocks.Move(-1) 138 set.blocks.Unlink(1) 139 set.blocks = set.blocks.Move(1) 140 } 141 } 142 }