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  }