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 }