github.com/dominant-strategies/go-quai@v0.28.2/consensus/blake3pow/blake3pow.go (about)

     1  package blake3pow
     2  
     3  import (
     4  	"math/big"
     5  	"math/rand"
     6  	"sync"
     7  	"time"
     8  
     9  	"github.com/dominant-strategies/go-quai/common"
    10  	"github.com/dominant-strategies/go-quai/common/hexutil"
    11  	"github.com/dominant-strategies/go-quai/consensus"
    12  	"github.com/dominant-strategies/go-quai/log"
    13  	"github.com/dominant-strategies/go-quai/metrics"
    14  	"github.com/dominant-strategies/go-quai/rpc"
    15  )
    16  
    17  var (
    18  	// sharedBlake3pow is a full instance that can be shared between multiple users.
    19  	sharedBlake3pow *Blake3pow
    20  )
    21  
    22  func init() {
    23  	sharedConfig := Config{
    24  		PowMode: ModeNormal,
    25  	}
    26  	sharedBlake3pow = New(sharedConfig, nil, false)
    27  }
    28  
    29  // Mode defines the type and amount of PoW verification a blake3pow engine makes.
    30  type Mode uint
    31  
    32  const (
    33  	ModeNormal Mode = iota
    34  	ModeShared
    35  	ModeTest
    36  	ModeFake
    37  	ModeFullFake
    38  )
    39  
    40  // Config are the configuration parameters of the blake3pow.
    41  type Config struct {
    42  	PowMode Mode
    43  
    44  	DurationLimit *big.Int
    45  
    46  	GasCeil uint64
    47  
    48  	MinDifficulty *big.Int
    49  
    50  	// When set, notifications sent by the remote sealer will
    51  	// be block header JSON objects instead of work package arrays.
    52  	NotifyFull bool
    53  
    54  	Log *log.Logger `toml:"-"`
    55  }
    56  
    57  // Blake3pow is a proof-of-work consensus engine using the blake3 hash algorithm
    58  type Blake3pow struct {
    59  	config Config
    60  
    61  	// Mining related fields
    62  	rand     *rand.Rand    // Properly seeded random source for nonces
    63  	threads  int           // Number of threads to mine on if mining
    64  	update   chan struct{} // Notification channel to update mining parameters
    65  	hashrate metrics.Meter // Meter tracking the average hashrate
    66  	remote   *remoteSealer
    67  
    68  	// The fields below are hooks for testing
    69  	shared    *Blake3pow    // Shared PoW verifier to avoid cache regeneration
    70  	fakeFail  uint64        // Block number which fails PoW check even in fake mode
    71  	fakeDelay time.Duration // Time delay to sleep for before returning from verify
    72  
    73  	lock      sync.Mutex // Ensures thread safety for the in-memory caches and mining fields
    74  	closeOnce sync.Once  // Ensures exit channel will not be closed twice.
    75  }
    76  
    77  // New creates a full sized blake3pow PoW scheme and starts a background thread for
    78  // remote mining, also optionally notifying a batch of remote services of new work
    79  // packages.
    80  func New(config Config, notify []string, noverify bool) *Blake3pow {
    81  	if config.Log == nil {
    82  		config.Log = &log.Log
    83  	}
    84  	blake3pow := &Blake3pow{
    85  		config:   config,
    86  		update:   make(chan struct{}),
    87  		hashrate: metrics.NewMeterForced(),
    88  	}
    89  	if config.PowMode == ModeShared {
    90  		blake3pow.shared = sharedBlake3pow
    91  	}
    92  	blake3pow.remote = startRemoteSealer(blake3pow, notify, noverify)
    93  	return blake3pow
    94  }
    95  
    96  // NewTester creates a small sized blake3pow PoW scheme useful only for testing
    97  // purposes.
    98  func NewTester(notify []string, noverify bool) *Blake3pow {
    99  	return New(Config{PowMode: ModeTest}, notify, noverify)
   100  }
   101  
   102  // NewFaker creates a blake3pow consensus engine with a fake PoW scheme that accepts
   103  // all blocks' seal as valid, though they still have to conform to the Quai
   104  // consensus rules.
   105  func NewFaker() *Blake3pow {
   106  	return &Blake3pow{
   107  		config: Config{
   108  			PowMode: ModeFake,
   109  			Log:     &log.Log,
   110  		},
   111  	}
   112  }
   113  
   114  // NewFakeFailer creates a blake3pow consensus engine with a fake PoW scheme that
   115  // accepts all blocks as valid apart from the single one specified, though they
   116  // still have to conform to the Quai consensus rules.
   117  func NewFakeFailer(fail uint64) *Blake3pow {
   118  	return &Blake3pow{
   119  		config: Config{
   120  			PowMode: ModeFake,
   121  			Log:     &log.Log,
   122  		},
   123  		fakeFail: fail,
   124  	}
   125  }
   126  
   127  // NewFakeDelayer creates a blake3pow consensus engine with a fake PoW scheme that
   128  // accepts all blocks as valid, but delays verifications by some time, though
   129  // they still have to conform to the Quai consensus rules.
   130  func NewFakeDelayer(delay time.Duration) *Blake3pow {
   131  	return &Blake3pow{
   132  		config: Config{
   133  			PowMode: ModeFake,
   134  			Log:     &log.Log,
   135  		},
   136  		fakeDelay: delay,
   137  	}
   138  }
   139  
   140  // NewFullFaker creates an blake3pow consensus engine with a full fake scheme that
   141  // accepts all blocks as valid, without checking any consensus rules whatsoever.
   142  func NewFullFaker() *Blake3pow {
   143  	return &Blake3pow{
   144  		config: Config{
   145  			PowMode: ModeFullFake,
   146  			Log:     &log.Log,
   147  		},
   148  	}
   149  }
   150  
   151  // NewShared creates a full sized blake3pow PoW shared between all requesters running
   152  // in the same process.
   153  func NewShared() *Blake3pow {
   154  	return &Blake3pow{shared: sharedBlake3pow}
   155  }
   156  
   157  // Close closes the exit channel to notify all backend threads exiting.
   158  func (blake3pow *Blake3pow) Close() error {
   159  	blake3pow.closeOnce.Do(func() {
   160  		// Short circuit if the exit channel is not allocated.
   161  		if blake3pow.remote == nil {
   162  			return
   163  		}
   164  		close(blake3pow.remote.requestExit)
   165  		<-blake3pow.remote.exitCh
   166  	})
   167  	return nil
   168  }
   169  
   170  // Threads returns the number of mining threads currently enabled. This doesn't
   171  // necessarily mean that mining is running!
   172  func (blake3pow *Blake3pow) Threads() int {
   173  	blake3pow.lock.Lock()
   174  	defer blake3pow.lock.Unlock()
   175  
   176  	return blake3pow.threads
   177  }
   178  
   179  // SetThreads updates the number of mining threads currently enabled. Calling
   180  // this method does not start mining, only sets the thread count. If zero is
   181  // specified, the miner will use all cores of the machine. Setting a thread
   182  // count below zero is allowed and will cause the miner to idle, without any
   183  // work being done.
   184  func (blake3pow *Blake3pow) SetThreads(threads int) {
   185  	blake3pow.lock.Lock()
   186  	defer blake3pow.lock.Unlock()
   187  
   188  	if blake3pow.shared != nil {
   189  		// If we're running a shared PoW, set the thread count on that instead
   190  		blake3pow.shared.SetThreads(threads)
   191  	} else {
   192  		// Update the threads and ping any running seal to pull in any changes
   193  		blake3pow.threads = threads
   194  		select {
   195  		case blake3pow.update <- struct{}{}:
   196  		default:
   197  		}
   198  	}
   199  }
   200  
   201  // Hashrate implements PoW, returning the measured rate of the search invocations
   202  // per second over the last minute.
   203  // Note the returned hashrate includes local hashrate, but also includes the total
   204  // hashrate of all remote miner.
   205  func (blake3pow *Blake3pow) Hashrate() float64 {
   206  	// Short circuit if we are run the blake3pow in normal/test mode.
   207  	if blake3pow.config.PowMode != ModeNormal && blake3pow.config.PowMode != ModeTest {
   208  		return blake3pow.hashrate.Rate1()
   209  	}
   210  	var res = make(chan uint64, 1)
   211  
   212  	select {
   213  	case blake3pow.remote.fetchRateCh <- res:
   214  	case <-blake3pow.remote.exitCh:
   215  		// Return local hashrate only if blake3pow is stopped.
   216  		return blake3pow.hashrate.Rate1()
   217  	}
   218  
   219  	// Gather total submitted hash rate of remote sealers.
   220  	return blake3pow.hashrate.Rate1() + float64(<-res)
   221  }
   222  
   223  // SubmitHashrate can be used for remote miners to submit their hash rate.
   224  // This enables the node to report the combined hash rate of all miners
   225  // which submit work through this node.
   226  //
   227  // It accepts the miner hash rate and an identifier which must be unique
   228  // between nodes.
   229  func (blake3pow *Blake3pow) SubmitHashrate(rate hexutil.Uint64, id common.Hash) bool {
   230  	if blake3pow.remote == nil {
   231  		return false
   232  	}
   233  
   234  	var done = make(chan struct{}, 1)
   235  	select {
   236  	case blake3pow.remote.submitRateCh <- &hashrate{done: done, rate: uint64(rate), id: id}:
   237  	case <-blake3pow.remote.exitCh:
   238  		return false
   239  	}
   240  
   241  	// Block until hash rate submitted successfully.
   242  	<-done
   243  	return true
   244  }
   245  
   246  // APIs implements consensus.Engine, returning the user facing RPC APIs.
   247  func (blake3pow *Blake3pow) APIs(chain consensus.ChainHeaderReader) []rpc.API {
   248  	// In order to ensure backward compatibility, we exposes blake3pow RPC APIs
   249  	// to both eth and blake3pow namespaces.
   250  	return []rpc.API{
   251  		{
   252  			Namespace: "eth",
   253  			Version:   "1.0",
   254  			Service:   &API{blake3pow},
   255  			Public:    true,
   256  		},
   257  		{
   258  			Namespace: "blake3pow",
   259  			Version:   "1.0",
   260  			Service:   &API{blake3pow},
   261  			Public:    true,
   262  		},
   263  	}
   264  }