github.com/aquanetwork/aquachain@v1.7.8/cmd/aquaminer/main.go (about)

     1  // aquaminer command is a aquachain reference miner
     2  package main
     3  
     4  import (
     5  	"context"
     6  	crand "crypto/rand"
     7  	"encoding/binary"
     8  	"flag"
     9  	"fmt"
    10  	"log"
    11  	"math"
    12  	"math/big"
    13  	mrand "math/rand"
    14  	"os"
    15  	"runtime"
    16  	"strings"
    17  	"time"
    18  
    19  	"github.com/aerth/tgun"
    20  	"gitlab.com/aquachain/aquachain/cmd/utils"
    21  	"gitlab.com/aquachain/aquachain/common"
    22  	"gitlab.com/aquachain/aquachain/core/types"
    23  	"gitlab.com/aquachain/aquachain/crypto"
    24  	"gitlab.com/aquachain/aquachain/opt/aquaclient"
    25  	"gitlab.com/aquachain/aquachain/rpc"
    26  )
    27  
    28  const version = "aquaminer version 0.9.1x (https://gitlab.com/aquachain/aquachain)"
    29  
    30  // EmptyMixDigest is sent when submitting work since HF5
    31  var EmptyMixDigest = common.BytesToHash(make([]byte, common.HashLength))
    32  
    33  var (
    34  	maxproc        = flag.Int("t", runtime.NumCPU(), "number of miners to spawn")
    35  	farm           = flag.String("F", "http://localhost:8543", "rpc server to mine to")
    36  	showVersion    = flag.Bool("version", false, "show version and exit")
    37  	autoworkername = flag.Bool("autoname", false, "adds random worker name to pool url")
    38  	benching       = flag.Bool("B", false, "offline benchmark mode")
    39  	debug          = flag.Bool("d", false, "debug mode")
    40  	benchversion   = flag.Uint64("v", 2, "hash version (benchmarking only)")
    41  	nonceseed      = flag.Int64("seed", 1, "nonce seed multiplier")
    42  	refresh        = flag.Duration("r", time.Second*3, "seconds to wait between asking for more work")
    43  	proxypath      = flag.String("prx", "", "example: socks5://192.168.1.3:1080 or 'tor' for localhost:9050")
    44  )
    45  
    46  // big numbers
    47  var bigOne = big.NewInt(1)
    48  var oneLsh256 = new(big.Int).Lsh(bigOne, 256)
    49  
    50  // bench work taken from a testnet work load
    51  var benchdiff = new(big.Int).SetBytes(common.HexToHash("0x08637bd05af6c69b5a63f9a49c2c1b10fd7e45803cd141a6937d1fe64f54").Bytes())
    52  var benchwork = common.HexToHash("0xd3b5f1b47f52fdc72b1dab0b02ab352442487a1d3a43211bc4f0eb5f092403fc")
    53  
    54  type workload struct {
    55  	job     common.Hash
    56  	target  *big.Int
    57  	version uint64
    58  	err     error
    59  }
    60  
    61  type doneworkload struct {
    62  	job   common.Hash
    63  	nonce uint64
    64  }
    65  
    66  type worker struct {
    67  	newwork chan workload
    68  }
    69  
    70  func main() {
    71  	fmt.Println(version)
    72  	flag.Parse()
    73  	if *showVersion {
    74  		os.Exit(0)
    75  	}
    76  	if !*debug {
    77  		fmt.Println("not showing h/s, use -d flag to print")
    78  	}
    79  	runtime.GOMAXPROCS(*maxproc)
    80  	runtime.GOMAXPROCS(*maxproc)
    81  
    82  	// get a random nonceseed
    83  	if *nonceseed == 1 {
    84  		seed, err := crand.Int(crand.Reader, big.NewInt(math.MaxInt64))
    85  		if err != nil {
    86  			if err != nil {
    87  				utils.Fatalf("rand err: %v", err)
    88  			}
    89  		}
    90  		*nonceseed = seed.Int64()
    91  	}
    92  	fmt.Println("rand seed:", *nonceseed)
    93  
    94  	// multiply nonceseed by 'now' so machines can share the same nonceseed
    95  	mrand.Seed(time.Now().UTC().Unix() * *nonceseed)
    96  
    97  	var (
    98  		workers    = []*worker{}
    99  		getnewwork = time.Tick(*refresh)
   100  		maxProc    = *maxproc
   101  		client     = &aquaclient.Client{}
   102  	)
   103  
   104  	if *autoworkername {
   105  		if !strings.HasSuffix(*farm, "/") {
   106  			*farm = *farm + "/"
   107  		}
   108  		*farm = *farm + fmt.Sprintf("%x", *nonceseed)[:6]
   109  		fmt.Println("auto:", *farm)
   110  	}
   111  
   112  	if !*benching {
   113  		// make http client
   114  		tgunner := &tgun.Client{
   115  			Proxy: *proxypath,
   116  		}
   117  		httpClient, err := tgunner.HTTPClient()
   118  		if err != nil {
   119  			utils.Fatalf("dial err: %v", err)
   120  		}
   121  
   122  		// make rpc client
   123  		rpcclient, err := rpc.DialHTTPWithClient(*farm, httpClient)
   124  		if err != nil {
   125  			utils.Fatalf("dial err: %v", err)
   126  		}
   127  
   128  		// wrap with aquaclient
   129  		client = aquaclient.NewClient(rpcclient)
   130  	} else {
   131  		fmt.Println("OFFLINE MODE")
   132  		<-time.After(time.Second)
   133  	}
   134  
   135  	var (
   136  		donework     = make(chan doneworkload)
   137  		forcenewwork = make(chan struct{}, 100)
   138  		ctx          = context.Background()
   139  		cachework    = common.Hash{}
   140  	)
   141  
   142  	// spawn miners
   143  	for i := 0; i < maxProc*2; i++ {
   144  		w := new(worker)
   145  		w.newwork = make(chan workload, 4) // new work incoming channel
   146  
   147  		workers = append(workers, w)
   148  		var workername string
   149  		if maxProc == 1 {
   150  			workername = fmt.Sprintf("%x", *nonceseed)[:6]
   151  		} else {
   152  			workername = fmt.Sprintf("%x", i)
   153  		}
   154  		go miner(workername, donework, *benching, w.newwork)
   155  	}
   156  
   157  	runtime.LockOSThread()
   158  	for {
   159  		select {
   160  		case <-getnewwork:
   161  			forcenewwork <- struct{}{}
   162  		case <-forcenewwork:
   163  			if *debug {
   164  				log.Println("fetching new work")
   165  			}
   166  			work, target, algo, err := refreshWork(ctx, client, *benching)
   167  			if err != nil {
   168  				log.Println("Error fetching new work from pool:", err)
   169  			}
   170  			if work == cachework {
   171  				continue // dont send already known work
   172  			}
   173  			cachework = work
   174  			log.Printf("Begin new work: %s (difficulty: %v) algo %v\n", work.Hex(), big2diff(target), algo)
   175  			for i := range workers {
   176  				workers[i].newwork <- workload{work, target, algo, err}
   177  			}
   178  		case gotdone := <-donework:
   179  			log.Println("submitting nonce:", gotdone)
   180  			blknonce := types.EncodeNonce(gotdone.nonce)
   181  			if client.SubmitWork(ctx, blknonce, gotdone.job, EmptyMixDigest) {
   182  				log.Println("good nonce:", gotdone)
   183  			} else {
   184  				// there was an error when we send the work. lets get a totally
   185  				log.Println("nonce not accepted", gotdone)
   186  				forcenewwork <- struct{}{}
   187  			}
   188  		}
   189  	}
   190  
   191  }
   192  
   193  // courtesy function to display difficulty for humans
   194  func big2diff(large *big.Int) uint64 {
   195  	if large == nil {
   196  		return 0
   197  	}
   198  	denominator := new(big.Int).Add(large, bigOne)
   199  	return new(big.Int).Div(oneLsh256, denominator).Uint64()
   200  
   201  }
   202  
   203  // fetch work from a rpc client
   204  func refreshWork(ctx context.Context, client *aquaclient.Client, benchmarking bool) (common.Hash, *big.Int, uint64, error) {
   205  	if benchmarking {
   206  		return benchwork, benchdiff, *benchversion, nil
   207  	}
   208  	work, err := client.GetWork(ctx)
   209  	if err != nil {
   210  		return common.Hash{}, benchdiff, 0, fmt.Errorf("getwork err: %v\ncheck address, pool url, and/or local rpc", err)
   211  	}
   212  	target := new(big.Int).SetBytes(common.HexToHash(work[2]).Bytes())
   213  	headerVersion := new(big.Int).SetBytes(common.HexToHash(work[1]).Bytes()).Uint64()
   214  
   215  	// set header version manually for before hf8
   216  	if headerVersion == 0 || headerVersion > 4 {
   217  		headerVersion = 2
   218  	}
   219  	return common.HexToHash(work[0]), target, headerVersion, nil
   220  }
   221  
   222  // single miner loop
   223  func miner(label string, doneworkchan chan doneworkload, offline bool, getworkchan <-chan workload) {
   224  
   225  	var (
   226  		second   = time.Tick(*refresh)
   227  		fps      = 0.00
   228  		workHash common.Hash
   229  		target   *big.Int
   230  		err      error
   231  		algo     uint64
   232  	)
   233  
   234  	// remember original nonce
   235  	ononce := mrand.Uint64()
   236  	nonce := ononce
   237  
   238  	for {
   239  
   240  		// accept new work if available
   241  		select {
   242  		case newwork := <-getworkchan:
   243  			workHash = newwork.job
   244  			target = newwork.target
   245  			algo = newwork.version
   246  			err = newwork.err
   247  		default:
   248  		}
   249  
   250  		// error fetching work, wait one second and see if theres more work
   251  		if err != nil {
   252  			log.Println("error getting work:", err)
   253  			<-time.After(time.Second)
   254  			continue
   255  		}
   256  
   257  		// difficulty isnt set. wait one second for more work.
   258  		if target == nil {
   259  			log.Println(label, "waiting for work...")
   260  			<-time.After(time.Second)
   261  			continue
   262  		}
   263  
   264  		// count h/s
   265  		if *debug {
   266  			fps++
   267  			select {
   268  			case <-second:
   269  				log.Printf("( %s %2.0fH/s (algo #%v)", label, fps/(*refresh).Seconds(), algo)
   270  				fps = 0
   271  			default:
   272  			}
   273  		}
   274  
   275  		// increment nonce
   276  		nonce++
   277  
   278  		// real actual hashing!
   279  		seed := make([]byte, 40)
   280  		copy(seed, workHash.Bytes())
   281  		binary.LittleEndian.PutUint64(seed[32:], nonce)
   282  		result := common.BytesToHash(crypto.VersionHash(byte(algo), seed))
   283  		// check difficulty of result
   284  		if diff := new(big.Int).SetBytes(result.Bytes()); diff.Cmp(target) <= 0 {
   285  			if offline {
   286  				continue
   287  			}
   288  			// submit the nonce, with the original job
   289  			doneworkchan <- doneworkload{workHash, nonce}
   290  		}
   291  	}
   292  }