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  }