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 }