github.com/core-coin/go-core/v2@v2.1.9/consensus/cryptore/cryptore.go (about)

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