github.com/isti4github/eth-ecc@v0.0.0-20201227085832-c337f2d99319/consensus/eccpow/algorithm.go (about) 1 package eccpow 2 3 import ( 4 "encoding/binary" 5 "math/big" 6 "math/rand" 7 8 //"reflect" 9 10 "sync" 11 "time" 12 13 "github.com/Onther-Tech/go-ethereum/common" 14 "github.com/Onther-Tech/go-ethereum/consensus" 15 "github.com/Onther-Tech/go-ethereum/core/types" 16 "github.com/Onther-Tech/go-ethereum/crypto" 17 "github.com/Onther-Tech/go-ethereum/metrics" 18 "github.com/Onther-Tech/go-ethereum/rpc" 19 ) 20 21 type ECC struct { 22 config Config 23 24 // Mining related fields 25 rand *rand.Rand // Properly seeded random source for nonces 26 threads int // Number of threads to mine on if mining 27 update chan struct{} // Notification channel to update mining parameters 28 hashrate metrics.Meter // Meter tracking the average hashrate 29 30 // Remote sealer related fields 31 workCh chan *sealTask // Notification channel to push new work and relative result channel to remote sealer 32 fetchWorkCh chan *sealWork // Channel used for remote sealer to fetch mining work 33 submitWorkCh chan *mineResult // Channel used for remote sealer to submit their mining result 34 fetchRateCh chan chan uint64 // Channel used to gather submitted hash rate for local or remote sealer. 35 submitRateCh chan *hashrate // Channel used for remote sealer to submit their mining hashrate 36 37 shared *ECC // Shared PoW verifier to avoid cache regeneration 38 fakeFail uint64 // Block number which fails PoW check even in fake mode 39 fakeDelay time.Duration // Time delay to sleep for before returning from verify 40 41 lock sync.Mutex // Ensures thread safety for the in-memory caches and mining fields 42 closeOnce sync.Once // Ensures exit channel will not be closed twice. 43 exitCh chan chan error // Notification channel to exiting backend threads 44 45 } 46 47 type Mode uint 48 49 const ( 50 ModeNormal Mode = iota 51 //ModeShared 52 ModeTest 53 ModeFake 54 ModeFullFake 55 ) 56 57 // Config are the configuration parameters of the ethash. 58 type Config struct { 59 PowMode Mode 60 } 61 62 // sealTask wraps a seal block with relative result channel for remote sealer thread. 63 type sealTask struct { 64 block *types.Block 65 results chan<- *types.Block 66 } 67 68 // mineResult wraps the pow solution parameters for the specified block. 69 type mineResult struct { 70 nonce types.BlockNonce 71 mixDigest common.Hash 72 hash common.Hash 73 74 errc chan error 75 } 76 77 // hashrate wraps the hash rate submitted by the remote sealer. 78 type hashrate struct { 79 id common.Hash 80 ping time.Time 81 rate uint64 82 83 done chan struct{} 84 } 85 86 // sealWork wraps a seal work package for remote sealer. 87 type sealWork struct { 88 errc chan error 89 res chan [4]string 90 } 91 92 // hasher is a repetitive hasher allowing the same hash data structures to be 93 // reused between hash runs instead of requiring new ones to be created. 94 //var hasher func(dest []byte, data []byte) 95 96 var ( 97 two256 = new(big.Int).Exp(big.NewInt(2), big.NewInt(256), big.NewInt(0)) 98 99 sharedECC = New(Config{ModeNormal}, nil, false) 100 ) 101 102 type verifyParameters struct { 103 n uint64 104 m uint64 105 wc uint64 106 wr uint64 107 seed uint64 108 outputWord []uint64 109 } 110 111 //const cross_err = 0.01 112 113 //type ( 114 // intMatrix [][]int 115 // floatMatrix [][]float64 116 //) 117 118 //RunOptimizedConcurrencyLDPC use goroutine for mining block 119 func RunOptimizedConcurrencyLDPC(header *types.Header, hash []byte) (bool, []int, []int, uint64, []byte) { 120 //Need to set difficulty before running LDPC 121 // Number of goroutines : 500, Number of attempts : 50000 Not bad 122 123 var LDPCNonce uint64 124 var hashVector []int 125 var outputWord []int 126 var digest []byte 127 var flag bool 128 129 //var wg sync.WaitGroup 130 //var outerLoopSignal = make(chan struct{}) 131 //var innerLoopSignal = make(chan struct{}) 132 //var goRoutineSignal = make(chan struct{}) 133 134 parameters, _ := setParameters(header) 135 H := generateH(parameters) 136 colInRow, rowInCol := generateQ(parameters, H) 137 /* 138 outerLoop: 139 for { 140 select { 141 // If outerLoopSignal channel is closed, then break outerLoop 142 case <-outerLoopSignal: 143 break outerLoop 144 145 default: 146 // Defined default to unblock select statement 147 } 148 149 innerLoop: 150 //for i := 0; i < runtime.NumCPU(); i++ { 151 for i := 0; i < 1; i++ { 152 select { 153 // If innerLoop signal is closed, then break innerLoop and close outerLoopSignal 154 case <-innerLoopSignal: 155 close(outerLoopSignal) 156 break innerLoop 157 158 default: 159 // Defined default to unblock select statement 160 } 161 162 wg.Add(1) 163 go func(goRoutineSignal chan struct{}) { 164 defer wg.Done() 165 //goRoutineNonce := generateRandomNonce() 166 //fmt.Printf("Initial goroutine Nonce : %v\n", goRoutineNonce) 167 168 var goRoutineHashVector []int 169 var goRoutineOutputWord []int 170 171 select { 172 case <-goRoutineSignal: 173 break 174 175 default: 176 attemptLoop: 177 for attempt := 0; attempt < 1; attempt++ { 178 goRoutineNonce := generateRandomNonce() 179 seed := make([]byte, 40) 180 copy(seed, hash) 181 binary.LittleEndian.PutUint64(seed[32:], goRoutineNonce) 182 seed = crypto.Keccak512(seed) 183 184 goRoutineHashVector = generateHv(parameters, seed) 185 goRoutineHashVector, goRoutineOutputWord, _ = OptimizedDecoding(parameters, goRoutineHashVector, H, rowInCol, colInRow) 186 flag = MakeDecision(header, colInRow, goRoutineOutputWord) 187 188 select { 189 case <-goRoutineSignal: 190 // fmt.Println("goRoutineSignal channel is already closed") 191 break attemptLoop 192 default: 193 if flag { 194 close(goRoutineSignal) 195 close(innerLoopSignal) 196 hashVector = goRoutineHashVector 197 outputWord = goRoutineOutputWord 198 LDPCNonce = goRoutineNonce 199 digest = seed 200 break attemptLoop 201 } 202 } 203 //goRoutineNonce++ 204 } 205 } 206 }(goRoutineSignal) 207 } 208 // Need to wait to prevent memory leak 209 wg.Wait() 210 } 211 */ 212 213 for i := 0; i < 64; i++ { 214 var goRoutineHashVector []int 215 var goRoutineOutputWord []int 216 goRoutineNonce := generateRandomNonce() 217 seed := make([]byte, 40) 218 copy(seed, hash) 219 binary.LittleEndian.PutUint64(seed[32:], goRoutineNonce) 220 seed = crypto.Keccak512(seed) 221 222 goRoutineHashVector = generateHv(parameters, seed) 223 goRoutineHashVector, goRoutineOutputWord, _ = OptimizedDecoding(parameters, goRoutineHashVector, H, rowInCol, colInRow) 224 flag = MakeDecision(header, colInRow, goRoutineOutputWord) 225 226 if flag { 227 hashVector = goRoutineHashVector 228 outputWord = goRoutineOutputWord 229 LDPCNonce = goRoutineNonce 230 digest = seed 231 break 232 } 233 } 234 return flag, hashVector, outputWord, LDPCNonce, digest 235 } 236 237 //MakeDecision check outputWord is valid or not using colInRow 238 func MakeDecision(header *types.Header, colInRow [][]int, outputWord []int) bool { 239 parameters, difficultyLevel := setParameters(header) 240 for i := 0; i < parameters.m; i++ { 241 sum := 0 242 for j := 0; j < parameters.wr; j++ { 243 // fmt.Printf("i : %d, j : %d, m : %d, wr : %d \n", i, j, m, wr) 244 sum = sum + outputWord[colInRow[j][i]] 245 } 246 if sum%2 == 1 { 247 return false 248 } 249 } 250 251 var numOfOnes int 252 for _, val := range outputWord { 253 numOfOnes += val 254 } 255 256 if numOfOnes >= Table[difficultyLevel].decisionFrom && 257 numOfOnes <= Table[difficultyLevel].decisionTo && 258 numOfOnes%Table[difficultyLevel].decisionStep == 0 { 259 return true 260 } 261 262 return false 263 } 264 265 //func isRegular(nSize, wCol, wRow int) bool { 266 // res := float64(nSize*wCol) / float64(wRow) 267 // m := math.Round(res) 268 // 269 // if int(m)*wRow == nSize*wCol { 270 // return true 271 // } 272 // 273 // return false 274 //} 275 276 //func SetDifficulty(nSize, wCol, wRow int) bool { 277 // if isRegular(nSize, wCol, wRow) { 278 // n = nSize 279 // wc = wCol 280 // wr = wRow 281 // m = int(n * wc / wr) 282 // return true 283 // } 284 // return false 285 //} 286 287 //func newIntMatrix(rows, cols int) intMatrix { 288 // m := intMatrix(make([][]int, rows)) 289 // for i := range m { 290 // m[i] = make([]int, cols) 291 // } 292 // return m 293 //} 294 // 295 //func newFloatMatrix(rows, cols int) floatMatrix { 296 // m := floatMatrix(make([][]float64, rows)) 297 // for i := range m { 298 // m[i] = make([]float64, cols) 299 // } 300 // return m 301 //} 302 303 // New creates a full sized ethash PoW scheme and starts a background thread for 304 // remote mining, also optionally notifying a batch of remote services of new work 305 // packages. 306 func New(config Config, notify []string, noverify bool) *ECC { 307 ecc := &ECC{ 308 config: config, 309 update: make(chan struct{}), 310 hashrate: metrics.NewMeterForced(), 311 workCh: make(chan *sealTask), 312 fetchWorkCh: make(chan *sealWork), 313 submitWorkCh: make(chan *mineResult), 314 fetchRateCh: make(chan chan uint64), 315 submitRateCh: make(chan *hashrate), 316 exitCh: make(chan chan error), 317 } 318 go ecc.remote(notify, noverify) 319 return ecc 320 } 321 322 func NewTester(notify []string, noverify bool) *ECC { 323 ecc := &ECC{ 324 config: Config{PowMode: ModeTest}, 325 update: make(chan struct{}), 326 hashrate: metrics.NewMeterForced(), 327 workCh: make(chan *sealTask), 328 fetchWorkCh: make(chan *sealWork), 329 submitWorkCh: make(chan *mineResult), 330 fetchRateCh: make(chan chan uint64), 331 submitRateCh: make(chan *hashrate), 332 exitCh: make(chan chan error), 333 } 334 go ecc.remote(notify, noverify) 335 return ecc 336 } 337 338 // NewFaker creates a ethash consensus engine with a fake PoW scheme that accepts 339 // all blocks' seal as valid, though they still have to conform to the Ethereum 340 // consensus rules. 341 func NewFaker() *ECC { 342 return &ECC{ 343 config: Config{ 344 PowMode: ModeFake, 345 }, 346 } 347 } 348 349 // NewFakeFailer creates a ethash consensus engine with a fake PoW scheme that 350 // accepts all blocks as valid apart from the single one specified, though they 351 // still have to conform to the Ethereum consensus rules. 352 func NewFakeFailer(fail uint64) *ECC { 353 return &ECC{ 354 config: Config{ 355 PowMode: ModeFake, 356 }, 357 fakeFail: fail, 358 } 359 } 360 361 // NewFakeDelayer creates a ethash consensus engine with a fake PoW scheme that 362 // accepts all blocks as valid, but delays verifications by some time, though 363 // they still have to conform to the Ethereum consensus rules. 364 func NewFakeDelayer(delay time.Duration) *ECC { 365 return &ECC{ 366 config: Config{ 367 PowMode: ModeFake, 368 }, 369 fakeDelay: delay, 370 } 371 } 372 373 // NewFullFaker creates an ethash consensus engine with a full fake scheme that 374 // accepts all blocks as valid, without checking any consensus rules whatsoever. 375 func NewFullFaker() *ECC { 376 return &ECC{ 377 config: Config{ 378 PowMode: ModeFullFake, 379 }, 380 } 381 } 382 383 // NewShared creates a full sized ethash PoW shared between all requesters running 384 // in the same process. 385 //func NewShared() *ECC { 386 // return &ECC{shared: sharedECC} 387 //} 388 389 // Close closes the exit channel to notify all backend threads exiting. 390 func (ecc *ECC) Close() error { 391 var err error 392 ecc.closeOnce.Do(func() { 393 // Short circuit if the exit channel is not allocated. 394 if ecc.exitCh == nil { 395 return 396 } 397 errc := make(chan error) 398 ecc.exitCh <- errc 399 err = <-errc 400 close(ecc.exitCh) 401 }) 402 return err 403 } 404 405 // Threads returns the number of mining threads currently enabled. This doesn't 406 // necessarily mean that mining is running! 407 func (ecc *ECC) Threads() int { 408 ecc.lock.Lock() 409 defer ecc.lock.Unlock() 410 411 return ecc.threads 412 } 413 414 // SetThreads updates the number of mining threads currently enabled. Calling 415 // this method does not start mining, only sets the thread count. If zero is 416 // specified, the miner will use all cores of the machine. Setting a thread 417 // count below zero is allowed and will cause the miner to idle, without any 418 // work being done. 419 func (ecc *ECC) SetThreads(threads int) { 420 ecc.lock.Lock() 421 defer ecc.lock.Unlock() 422 423 // If we're running a shared PoW, set the thread count on that instead 424 if ecc.shared != nil { 425 ecc.shared.SetThreads(threads) 426 return 427 } 428 // Update the threads and ping any running seal to pull in any changes 429 ecc.threads = threads 430 select { 431 case ecc.update <- struct{}{}: 432 default: 433 } 434 } 435 436 // Hashrate implements PoW, returning the measured rate of the search invocations 437 // per second over the last minute. 438 // Note the returned hashrate includes local hashrate, but also includes the total 439 // hashrate of all remote miner. 440 func (ecc *ECC) Hashrate() float64 { 441 // Short circuit if we are run the ecc in normal/test mode. 442 443 var res = make(chan uint64, 1) 444 445 select { 446 case ecc.fetchRateCh <- res: 447 case <-ecc.exitCh: 448 // Return local hashrate only if ecc is stopped. 449 return ecc.hashrate.Rate1() 450 } 451 452 // Gather total submitted hash rate of remote sealers. 453 return ecc.hashrate.Rate1() + float64(<-res) 454 } 455 456 // APIs implements consensus.Engine, returning the user facing RPC APIs. 457 func (ecc *ECC) APIs(chain consensus.ChainReader) []rpc.API { 458 // In order to ensure backward compatibility, we exposes ecc RPC APIs 459 // to both eth and ecc namespaces. 460 return []rpc.API{ 461 { 462 Namespace: "eth", 463 Version: "1.0", 464 Service: &API{ecc}, 465 Public: true, 466 }, 467 { 468 Namespace: "ecc", 469 Version: "1.0", 470 Service: &API{ecc}, 471 Public: true, 472 }, 473 } 474 } 475 476 //// SeedHash is the seed to use for generating a verification cache and the mining 477 //// dataset. 478 func SeedHash(block *types.Block) []byte { 479 return block.ParentHash().Bytes() 480 }