OpenCores
URL https://opencores.org/ocsvn/btcminer/btcminer/trunk

Subversion Repositories btcminer

[/] [btcminer/] [trunk/] [BTCMiner.java] - Blame information for rev 2

Details | Compare with Previous | View Log

Line No. Rev Author Line
1 2 ZTEX
/*!
2
   BTCMiner -- BTCMiner for ZTEX USB-FPGA Modules
3
   Copyright (C) 2011-2012 ZTEX GmbH
4
   http://www.ztex.de
5
 
6
   This program is free software; you can redistribute it and/or modify
7
   it under the terms of the GNU General Public License version 3 as
8
   published by the Free Software Foundation.
9
 
10
   This program is distributed in the hope that it will be useful, but
11
   WITHOUT ANY WARRANTY; without even the implied warranty of
12
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13
   General Public License for more details.
14
 
15
   You should have received a copy of the GNU General Public License
16
   along with this program; if not, see http://www.gnu.org/licenses/.
17
!*/
18
 
19
/* TODO:
20
 * HUP signal
21
 * rollntime / expire
22
 */
23
 
24
 
25
import java.io.*;
26
import java.util.*;
27
import java.net.*;
28
import java.security.*;
29
import java.text.*;
30
import java.util.zip.*;
31
 
32
import ch.ntb.usb.*;
33
 
34
import ztex.*;
35
 
36
// *****************************************************************************
37
// ******* ParameterException **************************************************
38
// *****************************************************************************
39
// Exception the prints a help message
40
class ParameterException extends Exception {
41
    public final static String helpMsg = new String (
42
                "Parameters:\n"+
43
                "    -host <string>    Host URL (default: http://127.0.0.1:8332)\n" +
44
                "    -u <string>       RPC User name\n" +
45
                "    -p <string>       RPC Password\n" +
46
                "    -b <url> <user name> <password> \n" +
47
                "                      URL, user name and password of a backup server. Can be specified multiple times. \n"+
48
                "    -lp <url> <user name> <password> \n" +
49
                "                      URL, user name and password of a long polling server (determined automatically by default) \n"+
50
                "    -l <log file>     Log file (default: BTCMiner.log) \n" +
51
                "    -l2 <log file>    Secondary log file, logs everything but statistics \n" +
52
                "    -bl <log file>    Log of submitted blocks file \n" +
53
                "    -c <file name>    Secondary command input file, can be a named pipe \n" +
54
                "    -m s|t|p|c        Set single mode, test mode, programming mode or cluster mode\n"+
55
                "                      Single mode: runs BTCMiner on a single board (default mode)\n" +
56
                "                      Test mode: tests a board using some test data\n" +
57
                "                      Programming mode: programs device with the given firmware\n" +
58
                "                      Cluster mode: runs BTCMiner on all programmed boards\n" +
59
                "    -ep0              Always use slow EP0 for Bitstream transfer\n" +
60
                "    -oh <number>      Overheat threshold: if the hash rate drops by that factor (but at least two frequency steps)\n"+
61
                "                      the overheat shutdown is triggered (default: 0.04, recommended: 0 to 0.08)\n"+
62
                "    -t <number>       Temperature limit (in °C, default 70°C)\n" +
63
                "    -ps <string>      Select devices with the given serial number,\n" +
64
                "                      in cluster mode: select devices which serial number starts with the given string\n" +
65
                "    -e <number>       Maximum error rate\n"+
66
                "    -tc               Enable target check (disabled by default)\n" +
67
                "    -v                Be verbose\n" +
68
                "    -h                This help\n" +
69
                "Parameters in single mode, test mode and programming mode\n"+
70
                "    -d <number>       Device Number, see -i\n" +
71
                "    -f <ihx file>     Firmware file (required in programming mode)\n" +
72
                "    -i                Print bus info\n" +
73
                "Parameters in cluster mode\n"+
74
                "    -n <number>       Maximum amount of devices per thread (default: 10)\n"+
75
                "Parameters in programming mode\n"+
76
                "    -pt <string>      Program devices of the given type\n" +
77
                "                      If neither -ps nor -ps is given, only unconfigured devices are programmed\n" +
78
                "    -s                Set serial number\n" +
79
                "    -rf               Erase firmware in EEPROM (overwrites -f, requires -pt or -ps)\n"
80
        );
81
 
82
 
83
    public ParameterException (String msg) {
84
        super( msg + "\n" + helpMsg );
85
    }
86
}
87
 
88
/* *****************************************************************************
89
   ******* ParserException *****************************************************
90
   ***************************************************************************** */
91
class ParserException extends Exception {
92
    public ParserException(String msg ) {
93
        super( msg );
94
    }
95
}
96
 
97
/* *****************************************************************************
98
   ******* FirmwareException ***************************************************
99
   ***************************************************************************** */
100
class FirmwareException extends Exception {
101
    public FirmwareException(String msg ) {
102
        super( msg );
103
    }
104
}
105
 
106
 
107
// *****************************************************************************
108
// ******* MsgObj *************************************************************
109
// *****************************************************************************
110
interface MsgObj {
111
    public void msg(String s);
112
}
113
 
114
 
115
// *****************************************************************************
116
// ******* NewBlockMonitor *****************************************************
117
// *****************************************************************************
118
class NewBlockMonitor extends Thread implements MsgObj {
119
    public int newCount = -1;
120
 
121
    public boolean running;
122
 
123
    private static final int minLongPollInterval = 250; // in ms
124
 
125
    private byte[] prevBlock = new byte[32];
126
    private byte[] dataBuf = new byte[128];
127
 
128
    private Vector<LogString> logBuf = new Vector<LogString>();
129
 
130
    public static boolean submitOld;
131
 
132
// ******* constructor *********************************************************
133
    public NewBlockMonitor( ) {
134
        start();
135
    }
136
 
137
// ******* checkNew ************************************************************
138
    synchronized public boolean checkNew ( byte[] data ) throws NumberFormatException {
139
        if ( data.length < 36 )
140
            throw new NumberFormatException("Invalid length of data");
141
 
142
        boolean n = false;
143
 
144
        for ( int i=0; i<32; i++ ) {
145
            n = n | ( data[i+4] != prevBlock[i] );
146
            prevBlock[i] = data[i+4];
147
        }
148
        if ( n ) {
149
            newCount += 1;
150
            submitOld = true;
151
            if ( newCount > 0 )
152
                msg("New block detected by block monitor");
153
        }
154
 
155
        return n;
156
    }
157
 
158
// ******* run *****************************************************************
159
    public void run () {
160
        running = true;
161
 
162
        boolean enableLP = true;
163
        boolean warnings = true;
164
        long enableLPTime = 0;
165
 
166
        submitOld = true;
167
 
168
        while ( running ) {
169
            long t = new Date().getTime();
170
 
171
            if ( BTCMiner.longPollURL!=null && enableLP && t>enableLPTime) {
172
                try {
173
//                  msg("info: LP");
174
                    String req = BTCMiner.bitcoinRequest(this, BTCMiner.longPollURL, BTCMiner.longPollUser, BTCMiner.longPollPassw, "getwork", "");
175
                    BTCMiner.hexStrToData(BTCMiner.jsonParse(req, "data"), dataBuf);
176
                    submitOld = true;
177
                    String so = null;
178
                    try {
179
                        so = BTCMiner.jsonParse(req, "submitold");
180
                        if ( so.equalsIgnoreCase("false") )
181
                            submitOld = false;
182
                    }
183
                    catch ( Exception e ) {
184
                    }
185
 
186
                    for ( int i=0; i<32; i++ ) {
187
                        prevBlock[i] = dataBuf[i+4];
188
                    }
189
                    newCount += 1;
190
                    msg( "New block detected by long polling" + ( so == null ? "" : " (submitold = " + so + ")" ) );
191
                }
192
                catch ( MalformedURLException e ) {
193
                    msg("Warning: " + e.getLocalizedMessage() + ": disabling long polling");
194
                    enableLP = false;
195
                }
196
                catch ( IOException e ) {
197
                    if ( new Date().getTime() < t+500 ) {
198
                        msg("Warning: " + e.getLocalizedMessage() + ": disabling long polling fo 60s");
199
                        enableLPTime = new Date().getTime() + 60000;
200
                    }
201
                }
202
                catch ( Exception e ) {
203
                    if ( warnings )
204
                        msg("Warning: " + e.getLocalizedMessage());
205
                    warnings = false;
206
                }
207
            }
208
 
209
            if ( BTCMiner.longPollURL==null )
210
                enableLPTime = new Date().getTime() + 2000;
211
 
212
            t += minLongPollInterval - new Date().getTime();
213
            if ( t > 5 ) {
214
                try {
215
                    Thread.sleep( t );
216
                }
217
                catch ( InterruptedException e) {
218
                }
219
            }
220
        }
221
 
222
//      System.out.println("Stopping block monitor"); 
223
    }
224
 
225
// ******* msg *****************************************************************
226
    public void msg(String s) {
227
        synchronized ( logBuf ) {
228
            logBuf.add( new LogString( s ) );
229
        }
230
    }
231
 
232
// ******* print ***************************************************************
233
    public void print () {
234
        synchronized ( logBuf ) {
235
            for ( int j=0; j<logBuf.size(); j++ ) {
236
                LogString ls = logBuf.elementAt(j);
237
                System.out.println( ls.msg );
238
                if ( BTCMiner.logFile != null ) {
239
                    BTCMiner.logFile.println( BTCMiner.dateFormat.format(ls.time) + ": " + ls.msg );
240
                }
241
                if ( BTCMiner.logFile2 != null && !ls.msg.substring(0,18).equals("New block detected") ) {
242
                    BTCMiner.logFile2.println( BTCMiner.dateFormat.format(ls.time) + ": " + ls.msg );
243
                }
244
            }
245
            logBuf.clear();
246
        }
247
    }
248
}
249
 
250
// *****************************************************************************
251
// ******* BTCMinerThread ******************************************************
252
// *****************************************************************************
253
class BTCMinerThread extends Thread {
254
    private Vector<BTCMiner> miners = new Vector<BTCMiner>();
255
    private String busName;
256
    private PollLoop pollLoop = null;
257
 
258
// ******* constructor *********************************************************
259
    public BTCMinerThread( String bn ) {
260
        busName = bn;
261
    }
262
 
263
// ******* add *****************************************************************
264
    public void add ( BTCMiner m ) {
265
        synchronized ( miners ) {
266
            miners.add ( m );
267
            m.name = busName + ": " + m.name;
268
        }
269
 
270
        if ( pollLoop==null ) {
271
            BTCMiner.printMsg2("Starting mining thread for bus " + busName);
272
            start();
273
        }
274
    }
275
 
276
// ******* size ****************************************************************
277
    public int size () {
278
        return miners.size();
279
    }
280
 
281
// ******* elementAt ***********************************************************
282
    public BTCMiner elementAt ( int i ) {
283
        return miners.elementAt(i);
284
    }
285
 
286
// ******* find ****************************************************************
287
    public BTCMiner find ( int dn ) {
288
        for (int i=0; i<miners.size(); i++ ) {
289
            if ( (miners.elementAt(i).ztex().dev().dev().getDevnum() == dn) )
290
                return miners.elementAt(i);
291
        }
292
        return null;
293
    }
294
 
295
// ******* busName *************************************************************
296
    public String busName () {
297
        return busName;
298
    }
299
 
300
// ******* running *************************************************************
301
    public boolean running () {
302
        return pollLoop != null;
303
    }
304
 
305
// ******* run *****************************************************************
306
    public void run () {
307
        pollLoop = new PollLoop(miners);
308
        pollLoop.run();
309
        pollLoop = null;
310
    }
311
 
312
// ******* printInfo ************************************************************
313
    public void printInfo ( ) {
314
        if ( pollLoop != null )
315
            pollLoop.printInfo( busName );
316
    }
317
 
318
// ******* disconnect ***********************************************************
319
    public int disconnect ( String ss, Vector<BTCMiner> allMiners ) {
320
        int i=0;
321
        synchronized ( miners ) {
322
            for (int j=miners.size()-1; j>=0; j-- ) {
323
                BTCMiner m = miners.elementAt(j);
324
                if ( ss.equals(m.ztex().dev().snString()) ) {
325
                    BTCMiner.printMsg("Disconnecting "+m.name);
326
                    if ( allMiners != null )
327
                        allMiners.removeElement(m);
328
                    m.suspend();
329
                    miners.removeElementAt(j);
330
                    i+=1;
331
                }
332
            }
333
        }
334
        return i;
335
    }
336
 
337
}
338
 
339
 
340
// *****************************************************************************
341
// ******* BTCMinerCluster *****************************************************
342
// *****************************************************************************
343
class BTCMinerCluster {
344
    public static int maxDevicesPerThread = 10;
345
 
346
    private Vector<BTCMinerThread> threads = new Vector<BTCMinerThread>();
347
    private Vector<BTCMiner> allMiners = new Vector<BTCMiner>();
348
 
349
// ******* constructor **************************************************************
350
    public BTCMinerCluster( boolean verbose ) {
351
        final long infoInterval = 300000;
352
 
353
        scan( verbose );
354
 
355
        long nextInfoTime = new Date().getTime() + 60000;
356
 
357
        boolean quit = false;
358
        while ( threads.size()>0 && !quit) {
359
 
360
            try {
361
                Thread.sleep( 300 );
362
            }
363
            catch ( InterruptedException e) {
364
            }
365
 
366
            BTCMiner.newBlockMonitor.print();
367
            for (int i=0; i<allMiners.size(); i++)
368
                allMiners.elementAt(i).print();
369
 
370
            if ( new Date().getTime() > nextInfoTime ) {
371
                double d = 0.0;
372
                double e = 0.0;
373
                for ( int i=0; i<allMiners.size(); i++ ) {
374
                    BTCMiner m = allMiners.elementAt(i);
375
                    m.printInfo( true );
376
                    d+=m.submittedHashRate();
377
                    e+=m.totalHashRate();
378
                }
379
                for ( int i=0; i<threads.size(); i++ )
380
                    threads.elementAt(i).printInfo();
381
 
382
                BTCMiner.printMsg2("Total hash rate: " + String.format("%.1f",  e ) + " MH/s");
383
                BTCMiner.printMsg2("Total submitted hash rate: " + String.format("%.1f",  d ) + " MH/s");
384
                BTCMiner.printMsg2(" -------- ");
385
                nextInfoTime = new Date().getTime() + infoInterval;
386
            }
387
 
388
            for (int i=threads.size()-1; i>=0; i--) {
389
                BTCMinerThread t = threads.elementAt(i);
390
                if ( !t.running() ) {
391
                    BTCMiner.printMsg2( "Stopped thread for bus " + t.busName() );
392
                    threads.removeElementAt(i);
393
                }
394
            }
395
 
396
            try {
397
                StringBuffer sb = new StringBuffer();
398
                while ( System.in.available() > 0 ) {
399
                    int j = System.in.read();
400
                    if (j>32)
401
                        sb.append((char) j);
402
                }
403
 
404
                if ( sb.length() == 0 && BTCMiner.in2 != null ) {
405
                    while ( BTCMiner.in2.available() > 0 ) {
406
                        int j = BTCMiner.in2.read();
407
                        if (j>32)
408
                            sb.append((char) j);
409
                    }
410
                }
411
 
412
                String cmd = sb.toString();
413
 
414
                if (cmd.length()<1) {}
415
                else if (cmd.equalsIgnoreCase("q") || cmd.equalsIgnoreCase("quit") ) {
416
                    for (int i=allMiners.size()-1; i>=0; i--) {
417
                        allMiners.elementAt(i).suspend();
418
                        try {
419
                            Thread.sleep( 10 );
420
                        }
421
                        catch ( InterruptedException e) {
422
                        }
423
                    }
424
                    quit=true;
425
                }
426
                else if (cmd.equalsIgnoreCase("r") || cmd.equalsIgnoreCase("rescan") ) {
427
                    scan( verbose );
428
                }
429
                else if (cmd.equalsIgnoreCase("s") || cmd.equalsIgnoreCase("suspend") ) {
430
                    long t = new Date().getTime();
431
                    int j=0;
432
                    for (int i=allMiners.size()-1; i>=0; i--) {
433
                        if ( allMiners.elementAt(i).suspend() ) j++;
434
                        allMiners.elementAt(i).startTimeAdjust = t;
435
                        try {
436
                            Thread.sleep( 10 );
437
                        }
438
                        catch ( InterruptedException e) {
439
                        }
440
                    }
441
                    BTCMiner.printMsg2("Suspended " + j + " of " + allMiners.size() + " miners. Enter `r' to resume.");
442
                }
443
                else if (cmd.equalsIgnoreCase("c") || cmd.equalsIgnoreCase("counter_reset") ) {
444
                    for (int i=allMiners.size()-1; i>=0; i--) {
445
                        allMiners.elementAt(i).resetCounters();
446
                    }
447
                    BTCMiner.printMsg2("Reset all performance end error counters.");
448
                }
449
                else if (cmd.equalsIgnoreCase("i") || cmd.equalsIgnoreCase("info") ) {
450
                    nextInfoTime = 0;
451
                }
452
                else if ( cmd.charAt(0) == 'd' || cmd.charAt(0) == 'D' ) {
453
                    int i = ( cmd.length()>=10 && cmd.substring(0,10).equalsIgnoreCase("disconnect") ) ? 10 : 1;
454
                    while ( i<cmd.length() && cmd.charAt(i)<=' ' ) i++;
455
                    int j = cmd.length()-1;
456
                    while ( j>=i && cmd.charAt(j)<=' ' ) j--;
457
                    if ( i<=j ) {
458
                        String ss=BTCMiner.checkSnString(cmd.substring(i,j+1));
459
                        j=0;
460
                        for ( i = threads.size()-1; i>=0; i-- )  {
461
                            j+=threads.elementAt(i).disconnect(ss, allMiners);
462
                        }
463
                        System.out.println("Disconnected "+j+" miners");
464
                    }
465
                    else {
466
                        System.out.println("No serial number specified");
467
                    }
468
                }
469
                else if (cmd.equalsIgnoreCase("h") || cmd.equalsIgnoreCase("help") ) {
470
                    System.out.println("q(uit)                         Exit BTCMiner");
471
                    System.out.println("r(escan)                       Rescan bus");
472
                    System.out.println("c(ounter_reset)                Reset performance and error counters");
473
                    System.out.println("s(uspend)                      Suspend cluster");
474
                    System.out.println("d(isconnect) <serial nunmber>  Disconnect device");
475
                    System.out.println("i(nfo)                         Print cluster informations");
476
                    System.out.println("h(elp)                         Print this help");
477
                }
478
                else System.out.println("Invalid command: `"+cmd+"', enter `h' for help");
479
 
480
            }
481
            catch ( Exception e ) {
482
            }
483
 
484
        }
485
 
486
//      BTCMiner.newBlockMonitor.running = false;
487
    }
488
 
489
// ******* add *****************************************************************
490
    private void add ( BTCMiner m ) {
491
        int i=0, j=0;
492
        String bn = m.ztex().dev().dev().getBus().getDirname() + "-" + j;
493
        while ( i<threads.size() ) {
494
            BTCMinerThread t = threads.elementAt(i);
495
            if ( bn.equalsIgnoreCase(threads.elementAt(i).busName()) ) {
496
                if ( t.size() < maxDevicesPerThread )
497
                    break;
498
                j++;
499
                i=0;
500
                bn = m.ztex().dev().dev().getBus().getDirname() + "-" + j;
501
            }
502
            else {
503
                i++;
504
            }
505
        }
506
 
507
        if ( i >= threads.size() )
508
            threads.add( new BTCMinerThread(bn) );
509
        threads.elementAt(i).add(m);
510
    }
511
 
512
// ******* find ****************************************************************
513
    private BTCMiner find ( ZtexDevice1 dev ) {
514
        int dn = dev.dev().getDevnum();
515
        String bn = dev.dev().getBus().getDirname();
516
        for ( int i=threads.size()-1; i>=0; i-- )  {
517
            BTCMiner m = threads.elementAt(i).find(dn);
518
            if (  m != null && bn.equals(m.ztex().dev().dev().getBus().getDirname()) )
519
                return m;
520
        }
521
        return null;
522
    }
523
 
524
// ******* insertIntoAllMiners *************************************************
525
    private void insertIntoAllMiners ( BTCMiner m ) {
526
        int j = 0;
527
        while ( j<allMiners.size() && m.name.compareTo(allMiners.elementAt(j).name)>=0 )
528
            j++;
529
        allMiners.insertElementAt(m, j);
530
    }
531
 
532
// ******* scan ****************************************************************
533
    private void scan ( boolean verbose ) {
534
        long t = new Date().getTime();
535
 
536
        allMiners.clear();
537
        for ( int i = threads.size()-1; i>=0; i-- )  {
538
            BTCMinerThread mt = threads.elementAt(i);
539
            for (int j=mt.size()-1; j>=0; j-- ) {
540
                BTCMiner m = mt.elementAt(j);
541
                insertIntoAllMiners(m);
542
                if ( m.suspended ) {
543
                    m.suspended = false;
544
                    m.isRunning = false;
545
                    try {
546
                        Thread.sleep( 20 );
547
                    }
548
                    catch ( InterruptedException e) {
549
                    }
550
                    BTCMiner.printMsg2(m.name + ": resuming");
551
                }
552
                else {
553
                    m.startTimeAdjust = t;
554
                    BTCMiner.printMsg2(m.name + ": already running");
555
                }
556
            }
557
        }
558
 
559
        BTCMiner.printMsg2("\n(Re)Scanning bus ... ");
560
 
561
        PollLoop.scanMode = true;
562
 
563
        ZtexScanBus1 bus = new ZtexScanBus1( ZtexDevice1.ztexVendorId, ZtexDevice1.ztexProductId, false, false, 1,  null, 10, 0, 1, 0 );
564
        int k = 0;
565
        int l = 0;
566
        for (int i=0; i<bus.numberOfDevices(); i++ ) {
567
            try {
568
                ZtexDevice1 dev = bus.device(i);
569
                if ( dev.productId(0)!=10 || dev.productId(2)>1 )
570
                    break;
571
 
572
                if ( BTCMiner.filterSN == null || dev.snString().substring(0,BTCMiner.filterSN.length()).equals(BTCMiner.filterSN) ) {
573
                    k += 1;
574
                    BTCMiner m = find( dev );
575
                    if ( m == null ) {
576
                        l += 1;
577
                        m = new BTCMiner ( dev, null, verbose );
578
                        m.clusterMode = true;
579
                        add( m );
580
                        BTCMiner.printMsg(m.name + ": added");
581
                        insertIntoAllMiners(m);
582
 
583
                        for ( int j=1; j<m.numberOfFpgas(); j++ ) {
584
                            BTCMiner n = new BTCMiner( m.ztex(), m.fpgaNum(j), verbose );
585
                            n.clusterMode = true;
586
                            add( n );
587
                            BTCMiner.printMsg(n.name + ": added");
588
                            insertIntoAllMiners(n);
589
                        }
590
                    }
591
                }
592
            }
593
            catch ( Exception e ) {
594
                BTCMiner.printMsg( "Error: "+e.getLocalizedMessage() );
595
            }
596
        }
597
 
598
        if ( k == 0 ) {
599
            System.err.println("No devices found. At least one device has to be connected.");
600
            System.exit(0);
601
        }
602
        BTCMiner.printMsg2("" + l + " new devices found.");
603
 
604
        t = new Date().getTime();
605
        for (int i=0; i<allMiners.size(); i++ )
606
            allMiners.elementAt(i).startTime+= t-allMiners.elementAt(i).startTimeAdjust;
607
 
608
        PollLoop.scanMode = false;
609
 
610
        BTCMiner.printMsg2("\nSummary: ");
611
        for (int i=0; i<threads.size(); i++ )
612
            BTCMiner.printMsg2("  Bus " + threads.elementAt(i).busName() + "\t: " + threads.elementAt(i).size() + " miners");
613
        BTCMiner.printMsg2("  Total  \t: " + allMiners.size() + " miners\n");
614
        BTCMiner.printMsg2("\nDisconnect all devices or enter `q' for exit. Enter `h' for help.\n");
615
 
616
        BTCMiner.connectionEffort = 1.0 + Math.exp( (1.0 - Math.sqrt(Math.min(allMiners.size(),maxDevicesPerThread)*allMiners.size())) / 13.0 );
617
//      System.out.println( BTCMiner.connectionEffort );
618
 
619
    }
620
}
621
 
622
 
623
// *****************************************************************************
624
// ******* LogString ***********************************************************
625
// *****************************************************************************
626
class LogString {
627
    public Date time;
628
    public String msg;
629
 
630
    public LogString(String s) {
631
        time = new Date();
632
        msg = s;
633
    }
634
}
635
 
636
 
637
// *****************************************************************************
638
// ******* PollLoop ************************************************************
639
// *****************************************************************************
640
class PollLoop {
641
    public static boolean scanMode = false;
642
 
643
    private double usbTime = 0.0;
644
    private double networkTime = 0.0;
645
    private double timeW = 1e-6;
646
    private Vector<BTCMiner> v;
647
    public static final long minQueryInterval = 250;
648
 
649
// ******* constructor *********************************************************
650
    public PollLoop ( Vector<BTCMiner> pv ) {
651
        v = pv;
652
    }
653
 
654
// ******* run *****************************************************************
655
    public void run ( ) {
656
        int maxIoErrorCount = (int) Math.round( (BTCMiner.rpcCount > 1 ? 2 : 4)*BTCMiner.connectionEffort );
657
        int ioDisableTime = BTCMiner.rpcCount > 1 ? 60 : 30;
658
 
659
        while ( v.size()>0 ) {
660
            long t0 = new Date().getTime();
661
            long tu = 0;
662
 
663
            if ( ! scanMode ) {
664
                synchronized ( v ) {
665
                    for ( int i=v.size()-1; i>=0; i-- ) {
666
                        BTCMiner m = v.elementAt(i);
667
 
668
                        m.usbTime = 0;
669
 
670
                        try {
671
                            if ( ! m.suspended ) {
672
                                if ( m.checkUpdate() && m.getWork() ) { // getwork calls getNonces
673
                                    m.dmsg("Got new work");
674
                                    m.sendData();
675
                                }
676
                                else {
677
                                    m.getNonces();
678
                                }
679
                                m.updateFreq();
680
                                m.printInfo(false);
681
                            }
682
                        }
683
                        catch ( IOException e ) {
684
                            m.ioErrorCount[m.rpcNum]++;
685
                            if ( m.ioErrorCount[m.rpcNum] >= maxIoErrorCount ) {
686
                                m.msg("Error: "+e.getLocalizedMessage() +": Disabling URL " + m.rpcurl[m.rpcNum] + " for " + ioDisableTime + "s");
687
                                m.disableTime[m.rpcNum] = new Date().getTime() + ioDisableTime*1000;
688
                                m.ioErrorCount[m.rpcNum] = 0;
689
                            }
690
                        }
691
                        catch ( ParserException e ) {
692
                            m.msg("Error: "+e.getLocalizedMessage() +": Disabling URL " + m.rpcurl[m.rpcNum] + " for 60s");
693
                            m.disableTime[m.rpcNum] = new Date().getTime() + 60000;
694
                        }
695
                        catch ( NumberFormatException e ) {
696
                            m.msg("Error: "+e.getLocalizedMessage() +": Disabling URL " + m.rpcurl[m.rpcNum] + " for 60s");
697
                            m.disableTime[m.rpcNum] = new Date().getTime() + 60000;
698
                        }
699
                        catch ( IndexOutOfBoundsException e ) {
700
                            m.msg("Error: "+e.getLocalizedMessage() +": Disabling URL " + m.rpcurl[m.rpcNum] + " for 60s");
701
                            m.disableTime[m.rpcNum] = new Date().getTime() + 60000;
702
                        }
703
                        catch ( Exception e ) {
704
                            m.msg("Error: "+e.getLocalizedMessage()+": Disabling device");
705
                            m.fatalError = "Error: "+e.getLocalizedMessage()+": Device disabled since " + BTCMiner.dateFormat.format( new Date() );
706
                            v.removeElementAt(i);
707
                        }
708
 
709
                        tu += m.usbTime;
710
 
711
                        if ( ! m.clusterMode ) {
712
                            BTCMiner.newBlockMonitor.print();
713
                        }
714
                    }
715
                }
716
 
717
                t0 = new Date().getTime() - t0;
718
                usbTime = usbTime * 0.9998 + tu;
719
                networkTime = networkTime * 0.9998 + t0 - tu;
720
                timeW = timeW * 0.9998 + 1;
721
            }
722
            else {
723
                t0 = 0;
724
            }
725
 
726
            t0 = minQueryInterval - t0;
727
            if ( t0 > 5 ) {
728
                try {
729
                    Thread.sleep( t0 );
730
                }
731
                catch ( InterruptedException e) {
732
                }
733
            }
734
        }
735
    }
736
 
737
// ******* printInfo ***********************************************************
738
    public void printInfo( String name ) {
739
        int oc = 0;
740
        double gt=0.0, gtw=0.0, st=0.0, stw=0.0;
741
        for ( int i=v.size()-1; i>=0; i-- ) {
742
            BTCMiner m = v.elementAt(i);
743
            oc += m.overflowCount;
744
            m.overflowCount = 0;
745
 
746
            st += m.submitTime;
747
            stw += m.submitTimeW;
748
 
749
            gt += m.getTime;
750
            gtw += m.getTimeW;
751
        }
752
 
753
        BTCMiner.printMsg2(name + ": poll loop time: " + Math.round((usbTime+networkTime)/timeW) + "ms (USB: " + Math.round(usbTime/timeW) + "ms network: " + Math.round(networkTime/timeW) + "ms)   getwork time: "
754
                +  Math.round(gt/gtw) + "ms  submit time: " +  Math.round(st/stw) + "ms" );
755
        if ( oc > 0 )
756
            BTCMiner.printMsg( name + ": Warning: " + oc + " overflows occured. This is usually caused by a slow network connection." );
757
    }
758
}
759
 
760
 
761
// *****************************************************************************
762
// *****************************************************************************
763
// ******* BTCMiner ************************************************************
764
// *****************************************************************************
765
// *****************************************************************************
766
class BTCMiner implements MsgObj  {
767
 
768
// *****************************************************************************
769
// ******* static methods ******************************************************
770
// *****************************************************************************
771
    static final int maxRpcCount = 32;
772
    static String[] rpcurl = new String[maxRpcCount];
773
    static String[] rpcuser = new String[maxRpcCount];
774
    static String[] rpcpassw = new String[maxRpcCount];
775
    static int rpcCount = 1;
776
 
777
    static String longPollURL = null;
778
    static String longPollUser = "";
779
    static String longPollPassw = "";
780
 
781
    static int bcid = -1;
782
 
783
    static String firmwareFile = null;
784
    static boolean printBus = false;
785
 
786
    public final static SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
787
 
788
    static PrintStream logFile = null;
789
    static PrintStream logFile2 = null;
790
    static PrintStream blkLogFile = null;
791
 
792
    static InputStream in2 = null;
793
    static String in2FileName = null;
794
 
795
    static double connectionEffort = 2.0;
796
 
797
    static NewBlockMonitor newBlockMonitor = null;
798
 
799
    static boolean forceEP0Config = false;
800
 
801
    static double overheatThreshold = 0.04;
802
 
803
    static double maxMaxErrorRate = 0.05;
804
 
805
    static double tempLimit = 65;  // in °C
806
 
807
    static boolean targetCheck = false;
808
 
809
    static String filterSN = null;
810
 
811
    public static final String[] dummyFirmwareNames = {
812
        "USB-FPGA Module 1.15d (default)" ,
813
        "USB-FPGA Module 1.15x (default)" ,
814
        "USB-FPGA Module 1.15y (default)"
815
    };
816
 
817
    public static final int[] defaultFirmwarePID1 = {
818
        13 ,
819
        13 ,
820
        15
821
    };
822
 
823
    public static final String[] firmwareFiles = {
824
        "ztex_ufm1_15d4.ihx" ,
825
        "ztex_ufm1_15d4.ihx" ,
826
        "ztex_ufm1_15y1.ihx"
827
    };
828
 
829
    public static final byte[] sha256_init_state = hexStrToData("67e6096a85ae67bb72f36e3c3af54fa57f520e518c68059babd9831f19cde05b");
830
    public static final byte[] sha256_pad1 = hexStrToData("000000800000000000000000000000000000000000000000000000000000000000000000000000000000000080020000");
831
 
832
// ******* printMsg *************************************************************
833
    public static void printMsg ( String msg ) {
834
        System.out.println( msg );
835
        if ( logFile != null )
836
            logFile.println( dateFormat.format( new Date() ) + ": " + msg );
837
        if ( logFile2 != null )
838
            logFile2.println( dateFormat.format( new Date() ) + ": " + msg );
839
    }
840
 
841
// ******* printMsg2 ************************************************************
842
    public static void printMsg2 ( String msg ) {
843
        System.out.println( msg );
844
        if ( logFile != null )
845
            logFile.println( dateFormat.format( new Date() ) + ": " + msg );
846
    }
847
 
848
// ******* encodeBase64 *********************************************************
849
    public static String encodeBase64(String s) {
850
        return encodeBase64(s.getBytes());
851
    }
852
 
853
    public static String encodeBase64(byte[] src) {
854
        return encodeBase64(src, 0, src.length);
855
    }
856
 
857
    public static String encodeBase64(byte[] src, int start, int length) {
858
        final String charSet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
859
        byte[] encodeData = new byte[64];
860
        byte[] dst = new byte[(length+2)/3 * 4 + length/72];
861
        int x = 0;
862
        int dstIndex = 0;
863
        int state = 0;
864
        int old = 0;
865
        int len = 0;
866
        int max = length + start;
867
 
868
        for (int i = 0; i<64; i++) {
869
            byte c = (byte) charSet.charAt(i);
870
            encodeData[i] = c;
871
        }
872
 
873
        for (int srcIndex = start; srcIndex<max; srcIndex++) {
874
            x = src[srcIndex];
875
            switch (++state) {
876
            case 1:
877
                dst[dstIndex++] = encodeData[(x>>2) & 0x3f];
878
                break;
879
            case 2:
880
                dst[dstIndex++] = encodeData[((old<<4)&0x30)
881
                    | ((x>>4)&0xf)];
882
                break;
883
            case 3:
884
                dst[dstIndex++] = encodeData[((old<<2)&0x3C)
885
                    | ((x>>6)&0x3)];
886
                dst[dstIndex++] = encodeData[x&0x3F];
887
                state = 0;
888
                break;
889
            }
890
            old = x;
891
            if (++len >= 72) {
892
                dst[dstIndex++] = (byte) '\n';
893
                len = 0;
894
            }
895
        }
896
 
897
        switch (state) {
898
        case 1: dst[dstIndex++] = encodeData[(old<<4) & 0x30];
899
           dst[dstIndex++] = (byte) '=';
900
           dst[dstIndex++] = (byte) '=';
901
           break;
902
        case 2: dst[dstIndex++] = encodeData[(old<<2) & 0x3c];
903
           dst[dstIndex++] = (byte) '=';
904
           break;
905
        }
906
        return new String(dst);
907
    }
908
 
909
// ******* hexStrToData ********************************************************
910
    public static byte[] hexStrToData( String str ) throws NumberFormatException {
911
        if ( str.length() % 2 != 0 )
912
            throw new NumberFormatException("Invalid length of string");
913
        byte[] buf = new byte[str.length() >> 1];
914
        for ( int i=0; i<buf.length; i++) {
915
            buf[i] = (byte) Integer.parseInt( str.substring(i*2,i*2+2), 16);
916
        }
917
        return buf;
918
    }
919
 
920
    public static void hexStrToData( String str, byte[] buf ) throws NumberFormatException {
921
        if ( str.length()<buf.length*2 )
922
            throw new NumberFormatException("Invalid length of string");
923
        for ( int i=0; i<buf.length; i++) {
924
            buf[i] = (byte) Integer.parseInt( str.substring(i*2,i*2+2), 16);
925
        }
926
    }
927
 
928
// ******* hexStrToData2 ********************************************************
929
    public static void hexStrToData2( String str, byte[] buf ) throws NumberFormatException {
930
        if ( str.length()<buf.length*2 )
931
            throw new NumberFormatException("Invalid length of string");
932
        for ( int i=0; i<buf.length; i++) {
933
            buf[i] = (byte) (Integer.parseInt( str.substring(i*2,i*2+1), 16) + Integer.parseInt( str.substring(i*2+1,i*2+2), 16)*16);
934
        }
935
    }
936
 
937
// ******* dataToHexStr ********************************************************
938
    public static String dataToHexStr (byte[] data)  {
939
        final char hexchars[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
940
        char[] buf = new char[data.length*2];
941
        for ( int i=0; i<data.length; i++) {
942
            buf[i*2+0] = hexchars[(data[i] & 255) >> 4];
943
            buf[i*2+1] = hexchars[(data[i] & 15)];
944
        }
945
        return new String(buf);
946
    }
947
 
948
// ******* dataToInt **********************************************************
949
    public static int dataToInt (byte[] buf, int offs)  {
950
        if ( offs + 4 > buf.length )
951
            throw new NumberFormatException("Invalid length of data");
952
        return (buf[offs+0] & 255) | ((buf[offs+1] & 255)<<8) | ((buf[offs+2] & 255)<<16) | ((buf[offs+3] & 255)<<24);
953
    }
954
 
955
// ******* intToData **********************************************************
956
    public static byte[] intToData (int n)  {
957
        byte[] buf = new byte[4];
958
        buf[0] = (byte) (n & 255);
959
        buf[1] = (byte) ((n >> 8) & 255);
960
        buf[2] = (byte) ((n >> 16) & 255);
961
        buf[3] = (byte) ((n >> 24) & 255);
962
        return buf;
963
    }
964
 
965
    public static void intToData (int n, byte[] buf, int offs) {
966
        buf[offs+0] = (byte) (n & 255);
967
        buf[offs+1] = (byte) ((n >> 8) & 255);
968
        buf[offs+2] = (byte) ((n >> 16) & 255);
969
        buf[offs+3] = (byte) ((n >> 24) & 255);
970
    }
971
 
972
// ******* intToHexStr ********************************************************
973
    public static String intToHexStr (int n)  {
974
        return dataToHexStr( reverse( intToData ( n ) ) );
975
    }
976
 
977
// ******* reverse ************************************************************
978
    public static byte[] reverse (byte[] data)  {
979
        byte[] buf = new byte[data.length];
980
        for ( int i=0; i<data.length; i++)
981
            buf[data.length-i-1] = data[i];
982
        return buf;
983
    }
984
 
985
// ******* jsonParse ***********************************************************
986
// does not work if parameter name is a part of a parameter value
987
    public static String jsonParse (String response, String parameter) throws ParserException {
988
        int lp = parameter.length();
989
        int i = 0;
990
        while ( i+lp<response.length() && !parameter.equalsIgnoreCase(response.substring(i,i+lp)) )
991
            i++;
992
        i+=lp;
993
        if ( i>=response.length() )
994
            throw new ParserException( "jsonParse: Parameter `"+parameter+"' not found" );
995
        while ( i<response.length() && response.charAt(i) != ':' )
996
            i++;
997
        i+=1;
998
        while ( i<response.length() && (byte)response.charAt(i) <= 32 )
999
            i++;
1000
        if ( i>=response.length() )
1001
            throw new ParserException( "jsonParse: Value expected after `"+parameter+"'" );
1002
        int j=i;
1003
        if ( i<response.length() && response.charAt(i)=='"' ) {
1004
            i+=1;
1005
            j=i;
1006
            while ( j<response.length() && response.charAt(j) != '"' )
1007
                j++;
1008
            if ( j>=response.length() )
1009
                throw new ParserException( "jsonParse: No closing `\"' found for value of paramter `"+parameter+"'" );
1010
        }
1011
        else {
1012
            while ( j<response.length() && response.charAt(j) != ',' && response.charAt(j) != /*{*/'}'  )
1013
                j++;
1014
        }
1015
        return response.substring(i,j);
1016
    }
1017
 
1018
 
1019
// ******* checkSnString *******************************************************
1020
// make sure that snString is 10 chars long
1021
    public static String checkSnString ( String snString ) {
1022
        if ( snString.length()>10 ) {
1023
            snString = snString.substring(0,10);
1024
            System.err.println( "Serial number too long (max. 10 characters), truncated to `" + snString + "'" );
1025
        }
1026
        while ( snString.length()<10 )
1027
            snString = '0' + snString;
1028
        return snString;
1029
    }
1030
 
1031
 
1032
// ******* getType *************************************************************
1033
    private static String getType ( ZtexDevice1 pDev ) {
1034
        byte[] buf = new byte[64];
1035
        try {
1036
            Ztex1v1 ztex = new Ztex1v1 ( pDev );
1037
            ztex.vendorRequest2( 0x82, "Read descriptor", 0, 0, buf, 64 );
1038
            if ( buf[0] < 1 || buf[0] > 5 )
1039
                throw new FirmwareException("Invalid BTCMiner descriptor version");
1040
 
1041
            int i0 = buf[0] > 4 ? 11 : ( buf[0] > 2 ? 10 : 8 );
1042
            int i = i0;
1043
            while ( i<64 && buf[i]!=0 )
1044
                i++;
1045
            if ( i < i0+1 )
1046
                throw new FirmwareException("Invalid bitstream file name");
1047
 
1048
            return new String(buf, i0, i-i0);
1049
        }
1050
        catch ( Exception e ) {
1051
            System.out.println("Warning: "+e.getLocalizedMessage() );
1052
        }
1053
        return null;
1054
    }
1055
 
1056
 
1057
// ******* sha256_transform ****************************************************
1058
    public static void sha256_transform(byte[] state, int state_offs, byte[] data, int data_offs, byte[] out, int out_offs) throws NumberFormatException {
1059
        if ( state.length < state_offs+32 )
1060
            throw new NumberFormatException("Invalid length of state");
1061
        if ( data.length < data_offs+64 )
1062
            throw new NumberFormatException("Invalid length of data");
1063
        if ( out.length < out_offs+32 )
1064
            throw new NumberFormatException("Invalid length of out");
1065
 
1066
      final int[] k = {
1067
         0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5,
1068
         0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
1069
         0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3,
1070
         0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
1071
         0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc,
1072
         0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
1073
         0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7,
1074
         0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
1075
         0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13,
1076
         0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
1077
         0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3,
1078
         0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
1079
         0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5,
1080
         0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
1081
         0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208,
1082
         0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2
1083
     };
1084
 
1085
      int A = dataToInt(state,state_offs+0);
1086
      int B = dataToInt(state,state_offs+4);
1087
      int C = dataToInt(state,state_offs+8);
1088
      int D = dataToInt(state,state_offs+12);
1089
      int E = dataToInt(state,state_offs+16);
1090
      int F = dataToInt(state,state_offs+20);
1091
      int G = dataToInt(state,state_offs+24);
1092
      int H = dataToInt(state,state_offs+28);
1093
      int T, T2;
1094
      int[] wBuf = new int[64];
1095
 
1096
      for (int i = 0; i < 16; i++)
1097
         wBuf[i] = dataToInt(data, data_offs+4*i);
1098
 
1099
      for (int i = 16; i < 64; i++) {
1100
         T = wBuf[i - 2];
1101
         T2 = wBuf[i - 15];
1102
         wBuf[i] = (((T >>> 17) | (T << 15)) ^ ((T >>> 19) | (T << 13)) ^ (T >>> 10)) + wBuf[i - 7] + (((T2 >>> 7) | (T2 << 25)) ^ ((T2 >>> 18) | (T2 << 14)) ^ (T2 >>> 3)) + wBuf[i - 16];
1103
      }
1104
 
1105
      for (int i=0; i < 64; i++) {
1106
         T = H + (((E >>> 6) | (E << 26)) ^ ((E >>> 11) | (E << 21)) ^ ((E >>> 25) | (E << 7))) + ((E & F) ^ (~E & G)) + k[i] + wBuf[i];
1107
         T2 = (((A >>> 2) | (A << 30)) ^ ((A >>> 13) | (A << 19)) ^ ((A >>> 22) | (A << 10))) + ((A & B) ^ (A & C) ^ (B & C));
1108
         H = G;
1109
         G = F;
1110
         F = E;
1111
         E = D + T;
1112
         D = C;
1113
         C = B;
1114
         B = A;
1115
         A = T + T2;
1116
      }
1117
 
1118
      intToData( A+dataToInt(state,state_offs+0), out, out_offs+0 );
1119
      intToData( B+dataToInt(state,state_offs+4), out, out_offs+4 );
1120
      intToData( C+dataToInt(state,state_offs+8), out, out_offs+8 );
1121
      intToData( D+dataToInt(state,state_offs+12), out, out_offs+12 );
1122
      intToData( E+dataToInt(state,state_offs+16), out, out_offs+16 );
1123
      intToData( F+dataToInt(state,state_offs+20), out, out_offs+20 );
1124
      intToData( G+dataToInt(state,state_offs+24), out, out_offs+24 );
1125
      intToData( H+dataToInt(state,state_offs+28), out, out_offs+28 );
1126
   }
1127
 
1128
    public static void printBus ( ZtexScanBus1 bus ) {
1129
        for (int i=0; i<bus.numberOfDevices(); i++ ) {
1130
            ZtexDevice1 dev = bus.device(i);
1131
            System.out.println( i + ": " + dev.toString() );
1132
            try {
1133
                byte buf[] = new byte[6];
1134
                new Ztex1v1(dev).macRead(buf);
1135
                System.out.println("   MAC address: " + dataToHexStr(buf));
1136
            }
1137
            catch (Exception e) {
1138
            }
1139
        }
1140
    }
1141
 
1142
// *****************************************************************************
1143
// ******* non-static methods **************************************************
1144
// *****************************************************************************
1145
    private Ztex1v1 ztex = null;
1146
    private int fpgaNum = 0;
1147
 
1148
    public int numNonces, offsNonces, freqM, freqMDefault, freqMaxM, extraSolutions;
1149
    public double freqM1;
1150
    public double hashesPerClock;
1151
    private String bitFileName = null;
1152
    public String name;
1153
    public String fatalError = null;
1154
    private boolean suspendSupported = false;
1155
 
1156
    public int ioErrorCount[] = new int[maxRpcCount];
1157
    public long disableTime[] = new long[maxRpcCount];
1158
 
1159
    public int rpcNum = 0;
1160
    private int prevRpcNum = 0;
1161
 
1162
    public boolean verbose = false;
1163
    public boolean clusterMode = false;
1164
 
1165
    public Vector<LogString> logBuf = new Vector<LogString>();
1166
 
1167
    private byte[] dataBuf = new byte[128];
1168
    private byte[] dataBuf2 = new byte[128];
1169
    private byte[] midstateBuf = new byte[32];
1170
    private byte[] sendBuf = new byte[44];
1171
    private byte[] hashBuf = hexStrToData("00000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000010000");
1172
    private byte[] targetBuf = hexStrToData("ffffffffffffffffffffffffffffffffffffffffffffffffffffffff00000000");
1173
    private double difficulity = 1.0;
1174
 
1175
    private int newCount = 0;
1176
 
1177
    public boolean isRunning = false;
1178
    public boolean suspended = false;
1179
 
1180
    MessageDigest digest = null;
1181
 
1182
    public int[] lastGoldenNonces = { 0, 0, 0, 0, 0, 0, 0, 0 };
1183
    public int[] goldenNonce, nonce, hash7;
1184
    public int submittedCount = 0;
1185
    public double totalSubmittedCount = 0.0;
1186
    public long startTime, startTimeAdjust;
1187
 
1188
    public int overflowCount = 0;
1189
    public long usbTime = 0;
1190
    public double getTime = 0.0;
1191
    public double getTimeW = 1e-6;
1192
    public double submitTime = 0.0;
1193
    public double submitTimeW = 1e-6;
1194
 
1195
    public long maxPollInterval = 20000;
1196
    public long infoInterval = 15000;
1197
 
1198
    public long lastGetWorkTime = 0;
1199
    public long ignoreErrorTime = 0;
1200
    public long lastInfoTime = 0;
1201
 
1202
    public double[] errorCount = new double[256];
1203
    public double[] errorWeight = new double[256];
1204
    public double[] errorRate = new double[256];
1205
    public double[] maxErrorRate = new double[256];
1206
    public final double errorHysteresis = 0.1; // in frequency steps
1207
 
1208
    private double maxHashRate = 0;
1209
 
1210
    private int numberOfFpgas = 0;
1211
    private int[] fpgaMap;
1212
 
1213
// ******* BTCMiner ************************************************************
1214
// constructor
1215
    public BTCMiner ( Ztex1v1 pZtex, String firmwareFile, boolean v ) throws UsbException, FirmwareException, NoSuchAlgorithmException {
1216
 
1217
        digest = MessageDigest.getInstance("SHA-256");
1218
        verbose = v;
1219
 
1220
        ztex = pZtex;
1221
        ztex.tempSensorUpdateInterval = 1000;
1222
        ztex.enableExtraFpgaConfigurationChecks = true;
1223
 
1224
        String snString=null;
1225
        if ( ( ztex.dev().productId(2)==0) && (firmwareFile==null) ) {
1226
            for ( int j=0; j<defaultFirmwarePID1.length; j++ )
1227
                if ( defaultFirmwarePID1[j]==ztex.dev().productId(1) && ztex.dev().productString().equals(dummyFirmwareNames[j]) )
1228
                    firmwareFile = firmwareFiles[j];
1229
            if ( firmwareFile != null ) {
1230
                msg("Using firmware `" + firmwareFile + "'" + " for `" + ztex.dev().productString() +"'" );
1231
                snString = ztex.dev().snString();
1232
            }
1233
        }
1234
 
1235
        if ( firmwareFile != null ) {
1236
            try {
1237
                ZtexIhxFile1 ihxFile = new ZtexIhxFile1( firmwareFile );
1238
                if ( snString != null )
1239
                    ihxFile.setSnString( snString );
1240
                ztex.uploadFirmware( ihxFile, false );
1241
            }
1242
            catch ( Exception e ) {
1243
                throw new FirmwareException ( e.getLocalizedMessage() );
1244
            }
1245
        }
1246
 
1247
        if ( ! ztex.valid() || ztex.dev().productId(0)!=10 || ztex.dev().productId(2)!=1 )
1248
            throw new FirmwareException("Wrong or no firmware");
1249
 
1250
        getDescriptor();
1251
 
1252
        goldenNonce = new int[numNonces*(1+extraSolutions)];
1253
        nonce = new int[numNonces];
1254
        hash7 = new int[numNonces];
1255
 
1256
        name = bitFileName+"-"+ztex.dev().snString();
1257
        msg( "New device: "+ descriptorInfo() );
1258
        try {
1259
            byte buf[] = new byte[6];
1260
            ztex.macRead(buf);
1261
            msg("MAC address: " + dataToHexStr(buf));
1262
        }
1263
        catch (Exception e) {
1264
            msg("No mac address support");
1265
        }
1266
 
1267
 
1268
//      long d = Math.round( 2500.0 / (freqM1 * (freqMaxM+1) * numNonces) * 1000.0 );
1269
//      if ( d < maxPollInterval ) maxPollInterval=d;
1270
 
1271
        numberOfFpgas = 0;
1272
        try {
1273
            fpgaMap = new int[ztex.numberOfFpgas()];
1274
            for (int i=0; i<ztex.numberOfFpgas(); i++ ) {
1275
                try {
1276
                    ztex.selectFpga(i);
1277
                    msg("FPGA "+ (i+1) + ": configuration time: " + ( forceEP0Config ? ztex.configureFpgaLS( "fpga/"+bitFileName+".bit" , true, 2 ) : ztex.configureFpga( "fpga/"+bitFileName+".bit" , true, 2 ) ) + " ms");
1278
                    try {
1279
                        Thread.sleep( 100 );
1280
                    }
1281
                    catch ( InterruptedException e) {
1282
                    }
1283
                    fpgaMap[numberOfFpgas] = i;
1284
                    numberOfFpgas += 1;
1285
                }
1286
                catch ( Exception e ) {
1287
                    msg( "Error configuring FPGA " + i + ": " + e.getLocalizedMessage() );
1288
                }
1289
            }
1290
        }
1291
        catch ( InvalidFirmwareException e ) {
1292
            throw new FirmwareException( e.getLocalizedMessage() );
1293
        }
1294
 
1295
        if ( numberOfFpgas < 1 )
1296
            throw new FirmwareException("No FPGA's found");
1297
 
1298
        fpgaNum = fpgaMap[0];
1299
        name += "-" + (fpgaNum+1);
1300
        msg( "New FPGA" );
1301
        freqM = -1;
1302
        updateFreq();
1303
 
1304
        lastInfoTime = new Date().getTime();
1305
 
1306
        for (int i=0; i<255; i++) {
1307
            errorCount[i] = 0;
1308
            errorWeight[i] = 0;
1309
            errorRate[i] = 0;
1310
            maxErrorRate[i] = 0;
1311
        }
1312
        maxHashRate = freqMDefault + 1.0;
1313
 
1314
        startTime = new Date().getTime();
1315
        startTimeAdjust = startTime;
1316
 
1317
        for (int i=0; i<rpcCount; i++) {
1318
            disableTime[i] = 0;
1319
            ioErrorCount[i] = 0;
1320
        }
1321
 
1322
        if ( newBlockMonitor == null ) {
1323
            newBlockMonitor = new NewBlockMonitor();
1324
        }
1325
 
1326
    }
1327
 
1328
 
1329
    public BTCMiner ( ZtexDevice1 pDev, String firmwareFile, boolean v ) throws UsbException, FirmwareException, NoSuchAlgorithmException {
1330
        this ( new Ztex1v1 ( pDev ), firmwareFile, v );
1331
    }
1332
 
1333
 
1334
    public BTCMiner ( Ztex1v1 pZtex, int pFpgaNum, boolean v ) throws UsbException, FirmwareException, NoSuchAlgorithmException {
1335
        digest = MessageDigest.getInstance("SHA-256");
1336
        verbose = v;
1337
 
1338
        ztex  = pZtex;
1339
        fpgaNum = pFpgaNum;
1340
 
1341
        if ( ! ztex.valid() || ztex.dev().productId(0)!=10 || ztex.dev().productId(2)!=1 || ( ztex.dev().productId(3)<1 && ztex.dev().productId(3)>2 ) )
1342
            throw new FirmwareException("Wrong or no firmware");
1343
 
1344
        getDescriptor();
1345
 
1346
        goldenNonce = new int[numNonces*(1+extraSolutions)];
1347
        nonce = new int[numNonces];
1348
        hash7 = new int[numNonces];
1349
 
1350
        name = bitFileName+"-"+ztex.dev().snString()+"-"+(fpgaNum+1);
1351
 
1352
        try {
1353
            msg( "New FPGA" );
1354
            freqM = -1;
1355
            updateFreq();
1356
 
1357
            lastInfoTime = new Date().getTime();
1358
        }
1359
        catch ( Exception e ) {
1360
            throw new FirmwareException ( e.getLocalizedMessage() );
1361
        }
1362
 
1363
 
1364
        for (int i=0; i<255; i++) {
1365
            errorCount[i] = 0;
1366
            errorWeight[i] = 0;
1367
            errorRate[i] = 0;
1368
            maxErrorRate[i] = 0;
1369
        }
1370
        maxHashRate = freqMDefault + 1.0;
1371
 
1372
        startTime = new Date().getTime();
1373
        startTimeAdjust = startTime;
1374
 
1375
        for (int i=0; i<rpcCount; i++) {
1376
            disableTime[i] = 0;
1377
            ioErrorCount[i] = 0;
1378
        }
1379
 
1380
    }
1381
 
1382
// ******* ztex ****************************************************************
1383
    public Ztex1v1 ztex() {
1384
        return ztex;
1385
    }
1386
 
1387
// ******* numberofFpgas *******************************************************
1388
    public int numberOfFpgas() {
1389
        return numberOfFpgas;
1390
    }
1391
 
1392
// ******* selectFpga **********************************************************
1393
    public void selectFpga() throws UsbException, InvalidFirmwareException, IndexOutOfBoundsException {
1394
        ztex.selectFpga(fpgaNum);
1395
    }
1396
 
1397
// ******* fpgaNum *************************************************************
1398
    public int fpgaNum() {
1399
        return fpgaNum;
1400
    }
1401
 
1402
    public int fpgaNum(int n) throws IndexOutOfBoundsException { // only valid for root miner
1403
        if ( n<0 || n>=numberOfFpgas )
1404
            throw new IndexOutOfBoundsException( "fpgaNum: Invalid FPGA number" );
1405
        return fpgaMap[n];
1406
    }
1407
 
1408
// ******* msg *****************************************************************
1409
    public void msg(String s) {
1410
        if ( clusterMode ) {
1411
            synchronized ( logBuf ) {
1412
                logBuf.add( new LogString( s ) );
1413
            }
1414
        }
1415
        else {
1416
            printMsg( ( name!=null ? name + ": " : "" ) + s );
1417
        }
1418
    }
1419
 
1420
// ******* dmsg *****************************************************************
1421
    void dmsg(String s) {
1422
        if ( verbose )
1423
            msg(s);
1424
    }
1425
 
1426
// ******* print ***************************************************************
1427
    public void print () {
1428
        synchronized ( logBuf ) {
1429
            for ( int j=0; j<logBuf.size(); j++ ) {
1430
                LogString ls = logBuf.elementAt(j);
1431
                System.out.println( name + ": " + ls.msg );
1432
                if ( logFile != null ) {
1433
                    logFile.println( dateFormat.format(ls.time) + ": " + name + ": " + ls.msg );
1434
                }
1435
                if ( logFile2 != null ) {
1436
                    logFile2.println( dateFormat.format(ls.time) + ": " + name + ": " + ls.msg );
1437
                }
1438
            }
1439
            logBuf.clear();
1440
        }
1441
    }
1442
 
1443
// ******* httpGet *************************************************************
1444
    public static String httpGet(MsgObj msgObj, String url, String user, String passw, String request) throws MalformedURLException, IOException {
1445
        HttpURLConnection con = (HttpURLConnection) new URL(url).openConnection();
1446
        con.setRequestMethod("POST");
1447
        con.setConnectTimeout((int) Math.round(2000.0*BTCMiner.connectionEffort));
1448
        con.setReadTimeout(url == longPollURL ? 1000000 : (int) Math.round(2000.0*BTCMiner.connectionEffort));
1449
        con.setRequestProperty("Authorization", "Basic " + encodeBase64(user + ":" + passw));
1450
        con.setRequestProperty("Accept-Encoding", "gzip,deflate");
1451
        con.setRequestProperty("Content-Type", "application/json");
1452
        con.setRequestProperty("Cache-Control", "no-cache");
1453
        con.setRequestProperty("User-Agent", "ztexBTCMiner");
1454
        con.setRequestProperty("X-Mining-Extensions", "longpoll midstate submitold");
1455
        con.setRequestProperty("Content-Length", "" + request.length());
1456
        con.setUseCaches(false);
1457
        con.setDoInput(true);
1458
        con.setDoOutput(true);
1459
 
1460
        // Send request
1461
        OutputStreamWriter wr = new OutputStreamWriter ( con.getOutputStream ());
1462
        wr.write(request);
1463
        wr.flush();
1464
        wr.close();
1465
 
1466
        // read response header
1467
        String str = con.getHeaderField("X-Reject-Reason");
1468
        if( str != null && ! str.equals("") && ! str.equals("high-hash") && ! str.equals("stale-prevblk") && ! str.equals("duplicate") ) {
1469
            msgObj.msg("Warning: Rejected block: " + str);
1470
        }
1471
 
1472
        // read response header
1473
        str = con.getHeaderField("X-Long-Polling");
1474
        if ( str != null && ! str.equals("") && longPollURL==null ) {
1475
            synchronized ( BTCMiner.newBlockMonitor ) {
1476
                if ( longPollURL==null ) {
1477
                    longPollURL = (str.length()>7 && str.substring(0,4).equalsIgnoreCase("http") ) ? str : url+str;
1478
                    msgObj.msg("Using LongPolling URL " + longPollURL);
1479
                    longPollUser = user;
1480
                    longPollPassw = passw;
1481
                }
1482
            }
1483
        }
1484
 
1485
        // read response        
1486
        InputStream is;
1487
        if ( con.getContentEncoding() == null )
1488
            is = con.getInputStream();
1489
        else if ( con.getContentEncoding().equalsIgnoreCase("gzip") )
1490
            is = new GZIPInputStream(con.getInputStream());
1491
        else if (con.getContentEncoding().equalsIgnoreCase("deflate") )
1492
            is = new InflaterInputStream(con.getInputStream());
1493
        else
1494
            throw new IOException( "httpGet: Unknown encoding: " + con.getContentEncoding() );
1495
 
1496
        byte[] buf = new byte[1024];
1497
        StringBuffer response = new StringBuffer();
1498
        int len;
1499
        while ( (len = is.read(buf)) > 0 ) {
1500
            response.append(new String(buf,0,len));
1501
        }
1502
        is.close();
1503
        con.disconnect();
1504
 
1505
        return response.toString();
1506
    }
1507
 
1508
/*    String httpGet(String request) throws MalformedURLException, IOException {
1509
        return httpGet(rpcurl[rpcNum], rpcuser[rpcNum], rpcpassw[rpcNum], request )
1510
    } */
1511
 
1512
// ******* bitcoinRequest ******************************************************
1513
    public static String bitcoinRequest( MsgObj msgObj, String url, String user, String passw, String request, String params) throws MalformedURLException, IOException {
1514
        bcid += 1;
1515
        return httpGet( msgObj, url, user, passw, "{\"jsonrpc\":\"1.0\",\"id\":" + bcid + ",\"method\":\""+ request + "\",\"params\":["+ (params.equals("") ? "" : ("\""+params+"\"")) + "]}" );
1516
    }
1517
 
1518
    public String bitcoinRequest( String request, String params) throws MalformedURLException, IOException {
1519
        String s = bitcoinRequest( this, rpcurl[rpcNum], rpcuser[rpcNum], rpcpassw[rpcNum], request, params );
1520
        ioErrorCount[rpcNum] = 0;
1521
        return s;
1522
    }
1523
 
1524
 
1525
// ******* getWork *************************************************************
1526
    public boolean getWork() throws UsbException, MalformedURLException, IOException, ParserException {
1527
 
1528
        long t = new Date().getTime();
1529
 
1530
        int i = 0;
1531
        while ( i<rpcCount && (disableTime[i]>t) )
1532
            i++;
1533
        if ( i >= rpcCount )
1534
            return false;
1535
 
1536
        rpcNum = i;
1537
        String response = bitcoinRequest("getwork","" );
1538
        t = new Date().getTime() - t;
1539
        getTime = getTime * 0.99 + t;
1540
        getTimeW = getTimeW * 0.99 + 1;
1541
 
1542
        try {
1543
            hexStrToData(jsonParse(response,"data"), dataBuf2);
1544
            newBlockMonitor.checkNew( dataBuf2 );
1545
        }
1546
        catch ( NumberFormatException e ) {
1547
        }
1548
 
1549
        if ( newCount >= newBlockMonitor.newCount || newBlockMonitor.submitOld ) {
1550
            while ( getNonces() ) {}
1551
        }
1552
 
1553
        newCount = newBlockMonitor.newCount;
1554
 
1555
        try {
1556
            hexStrToData(jsonParse(response,"data"), dataBuf);
1557
        }
1558
        catch ( NumberFormatException e ) {
1559
            throw new ParserException( e.getLocalizedMessage() );
1560
        }
1561
 
1562
 
1563
 
1564
        try {
1565
            if ( targetCheck ) {
1566
                hexStrToData(jsonParse(response,"target"), targetBuf);
1567
            }
1568
            else {
1569
                hexStrToData("ffffffffffffffffffffffffffffffffffffffffffffffffffffffff00000000", targetBuf);
1570
            }
1571
        }
1572
        catch ( Exception e ) {
1573
            hexStrToData("ffffffffffffffffffffffffffffffffffffffffffffffffffffffff00000000", targetBuf);
1574
        }
1575
 
1576
        double d = 1.0;
1577
        difficulity = 0;
1578
        for (int j=0; j<28; j++) {
1579
            difficulity += (targetBuf[j] & 255) * d;
1580
            d*=256;
1581
        }
1582
        difficulity = d / difficulity;
1583
 
1584
        boolean b = false;
1585
        int k = dataBuf.length - sha256_pad1.length;
1586
        for ( int j=0; j<sha256_pad1.length; j++ )
1587
            if ( sha256_pad1[j] != dataBuf[k+j] ) {
1588
                b=true;
1589
                dataBuf[k+j] = sha256_pad1[j];
1590
            }
1591
        if ( b )
1592
            msg("Warning: Invalid data from " + rpcurl[rpcNum]);
1593
 
1594
 
1595
        sha256_transform( sha256_init_state,0, dataBuf,0, midstateBuf,0);
1596
 
1597
        lastGetWorkTime = new Date().getTime();
1598
        prevRpcNum = i;
1599
        return true;
1600
    }
1601
 
1602
// ******* submitWork **********************************************************
1603
    public void submitWork( int n ) throws MalformedURLException, IOException {
1604
        long t = new Date().getTime();
1605
 
1606
        intToData(n, dataBuf, 76);
1607
 
1608
        dmsg( "Submitting new nonce " + intToHexStr(n) );
1609
        if ( blkLogFile != null )
1610
            blkLogFile.println( dateFormat.format( new Date() ) + ": " + name + ": submitted " + dataToHexStr(dataBuf) + " to " + rpcurl[rpcNum]);
1611
        String response = bitcoinRequest( "getwork", dataToHexStr(dataBuf) );
1612
        String err = null;
1613
        try {
1614
            err = jsonParse(response,"error");
1615
        }
1616
        catch ( ParserException e ) {
1617
        }
1618
        if ( err!=null && !err.equals("null") && !err.equals("") )
1619
            msg( "Error attempting to submit new nonce: " + err );
1620
 
1621
        for (int i=lastGoldenNonces.length-1; i>0; i-- )
1622
            lastGoldenNonces[i]=lastGoldenNonces[i-1];
1623
        lastGoldenNonces[0] = n;
1624
 
1625
        t = new Date().getTime() - t;
1626
        submitTime = submitTime * 0.99 + t;
1627
        submitTimeW = submitTimeW * 0.99 + 1;
1628
    }
1629
 
1630
// ******* initWork **********************************************************
1631
    public void initWork (byte[] data, byte[] midstate) {
1632
        if ( data.length != 128 )
1633
            throw new NumberFormatException("Invalid length of data");
1634
        if ( midstate.length != 32 )
1635
            throw new NumberFormatException("Invalid length of midstate");
1636
        for (int i=0; i<128; i++)
1637
            dataBuf[i] = data[i];
1638
        for (int i=0; i<32; i++)
1639
            midstateBuf[i] = midstate[i];
1640
    }
1641
 
1642
// ******* getHash ***********************************************************
1643
    public int getHash(int n) throws NumberFormatException {
1644
        intToData(n, dataBuf, 76);
1645
        sha256_transform( midstateBuf,0, dataBuf,64, hashBuf,0 );
1646
        sha256_transform( sha256_init_state,0, hashBuf,0, hashBuf,0 );
1647
        return dataToInt( hashBuf,28 );
1648
    }
1649
 
1650
// ******* compareWithTarget ***************************************************
1651
    // returns true if smaller than or equal to target
1652
    public boolean compareWithTarget(int n) throws NumberFormatException {
1653
        intToData(n, dataBuf, 76);
1654
        sha256_transform( midstateBuf,0, dataBuf,64, hashBuf,0 );
1655
        sha256_transform( sha256_init_state,0, hashBuf,0, hashBuf,0 );
1656
        for ( int i=0; i<32; i++ ) {
1657
            int j=i+3-2*(i%4);
1658
            if ( (hashBuf[31-j] & 255) < (targetBuf[31-i] & 255) )
1659
                return true;
1660
            if ( (hashBuf[31-j] & 255) > (targetBuf[31-i] & 255) )
1661
                return false;
1662
        }
1663
        return true;
1664
    }
1665
 
1666
// ******* getMidstate *********************************************************
1667
    public byte[] getMidstate() {
1668
        sha256_transform( sha256_init_state,0, dataBuf,0, hashBuf,0);
1669
        return hashBuf;
1670
    }
1671
 
1672
// ******* sendData ***********************************************************
1673
    public void sendData () throws UsbException {
1674
        for ( int i=0; i<12; i++ )
1675
            sendBuf[i] = dataBuf[i+64];
1676
        for ( int i=0; i<32; i++ )
1677
            sendBuf[i+12] = midstateBuf[i];
1678
 
1679
        long t = new Date().getTime();
1680
        synchronized (ztex) {
1681
            try {
1682
                selectFpga();
1683
            }
1684
            catch ( InvalidFirmwareException e )  {
1685
                // shouldn't occur
1686
            }
1687
            ztex.vendorCommand2( 0x80, "Send hash data", 0, 0, sendBuf, 44 );
1688
        }
1689
        usbTime += new Date().getTime() - t;
1690
 
1691
        ignoreErrorTime = new Date().getTime() + 500; // ignore errors for next 1s
1692
        for ( int i=0; i<numNonces; i++ )
1693
            nonce[i] = 0;
1694
        isRunning = true;
1695
    }
1696
 
1697
// ******* setFreq *************************************************************
1698
    public void setFreq (int m) throws UsbException {
1699
        if ( m > freqMaxM ) m = freqMaxM;
1700
 
1701
        long t = new Date().getTime();
1702
        synchronized (ztex) {
1703
            try {
1704
                selectFpga();
1705
            }
1706
            catch ( InvalidFirmwareException e )  {
1707
                // shouldn't occur
1708
            }
1709
            ztex.vendorCommand( 0x83, "Send hash data", m, 0 );
1710
        }
1711
        usbTime += new Date().getTime() - t;
1712
 
1713
        ignoreErrorTime = new Date().getTime() + 2000; // ignore errors for next 2s
1714
    }
1715
 
1716
// ******* suspend *************************************************************
1717
    public boolean suspend ( )  {
1718
        suspended = true;
1719
        if ( suspendSupported ) {
1720
            try {
1721
                synchronized (ztex) {
1722
                    selectFpga();
1723
                    ztex.vendorCommand( 0x84, "Suspend" );
1724
                }
1725
            }
1726
            catch ( Exception e )  {
1727
                msg( "Suspend command failed: " + e.getLocalizedMessage() );
1728
                return false;
1729
            }
1730
        }
1731
        else {
1732
            msg( "Suspend command not supported. Update Firmware." );
1733
            return false;
1734
        }
1735
        return true;
1736
    }
1737
 
1738
// ******* updateFreq **********************************************************
1739
    public void updateFreq() throws UsbException {
1740
 
1741
        for ( int i=0; i<freqMaxM; i++ )  {
1742
            if ( maxErrorRate[i+1]*i < maxErrorRate[i]*(i+20) )
1743
                maxErrorRate[i+1] = maxErrorRate[i]*(1.0+20.0/i);
1744
        }
1745
 
1746
        int maxM = 0;
1747
        while ( maxM<freqMDefault && maxErrorRate[maxM+1]<maxMaxErrorRate )
1748
            maxM++;
1749
        while ( maxM<freqMaxM && errorWeight[maxM]>150 && maxErrorRate[maxM+1]<maxMaxErrorRate )
1750
            maxM++;
1751
 
1752
        int bestM=0;
1753
        double bestR=0;
1754
        for ( int i=0; i<=maxM; i++ )  {
1755
            double r = (i + 1 + ( i == freqM ? errorHysteresis : 0))*(1-maxErrorRate[i]);
1756
            if ( r > bestR ) {
1757
                bestM = i;
1758
                bestR = r;
1759
            }
1760
        }
1761
 
1762
        if ( bestM != freqM ) {
1763
            msg ( "Set frequency " + ( freqM<0 ? "" : "from " + String.format("%.2f",(freqM+1)*(freqM1)) + "MHz ") + "to " + String.format("%.2f",(bestM+1)*(freqM1)) +"MHz" );
1764
            freqM = bestM;
1765
            setFreq( freqM );
1766
        }
1767
 
1768
        maxM = freqMDefault;
1769
        while ( maxM<freqMaxM && errorWeight[maxM+1]>100 )
1770
            maxM++;
1771
        if ( ( bestM+1 < (1.0-overheatThreshold )*maxHashRate ) && bestM < maxM-1 )  {
1772
            try {
1773
                synchronized (ztex) {
1774
                    selectFpga();
1775
                    ztex.resetFpga();
1776
                }
1777
            }
1778
            catch ( Exception e ) {
1779
            }
1780
            throw new UsbException("Hash rate drop of " + String.format("%.1f",(1.0-1.0*(bestM+1)/maxHashRate)*100) + "% detect. This may be caused by overheating. FPGA is shut down to prevent damage." );
1781
        }
1782
 
1783
        double temp;
1784
        try {
1785
            temp = ztex.tempSensorRead(fpgaNum);
1786
        }
1787
        catch ( Exception e ) {
1788
            temp = tempLimit - 1e12;
1789
        }
1790
        if ( temp > tempLimit ) {
1791
            try {
1792
                synchronized (ztex) {
1793
                    selectFpga();
1794
                    ztex.resetFpga();
1795
                }
1796
            }
1797
            catch ( Exception e ) {
1798
            }
1799
            throw new UsbException("Overheating detected: T=" + String.format("%.1f",temp) + "°C. FPGA is shut down to prevent damage." );
1800
        }
1801
 
1802
    }
1803
 
1804
// ******* getNonces ***********************************************************
1805
    public boolean getNonces() throws UsbException, MalformedURLException, IOException {
1806
        if ( !isRunning || disableTime[prevRpcNum] > new Date().getTime() ) return false;
1807
 
1808
        rpcNum = prevRpcNum;
1809
 
1810
        getNoncesInt();
1811
 
1812
        if ( ignoreErrorTime < new Date().getTime() ) {
1813
            errorCount[freqM] *= 0.995;
1814
            errorWeight[freqM] = errorWeight[freqM]*0.995 + 1.0;
1815
            for ( int i=0; i<numNonces; i++ ) {
1816
                if ( ! checkNonce( nonce[i], hash7[i] ) )
1817
                    errorCount[freqM] +=1.0/numNonces;
1818
            }
1819
 
1820
            errorRate[freqM] = errorCount[freqM] / errorWeight[freqM] * Math.min(1.0, errorWeight[freqM]*0.01) ;
1821
            if ( errorRate[freqM] > maxErrorRate[freqM] )
1822
                maxErrorRate[freqM] = errorRate[freqM];
1823
            if ( errorWeight[freqM] > 120 )
1824
                maxHashRate = Math.max(maxHashRate, (freqM+1.0)*(1-errorRate[freqM]));
1825
        }
1826
 
1827
        boolean submitted = false;
1828
        for ( int i=0; i<numNonces*(1+extraSolutions); i++ ) {
1829
            int n = goldenNonce[i];
1830
            if ( n != -offsNonces ) {
1831
                if ( compareWithTarget(n) ) {
1832
                    int j=0;
1833
                    while ( j<lastGoldenNonces.length && lastGoldenNonces[j]!=n )
1834
                        j++;
1835
                    if  (j>=lastGoldenNonces.length) {
1836
                        submitWork( n );
1837
                        submittedCount+=1;
1838
                        totalSubmittedCount+=difficulity;
1839
                        submitted = true;
1840
                    }
1841
                }
1842
            }
1843
        }
1844
        return submitted;
1845
    }
1846
 
1847
// ******* getNoncesInt ********************************************************
1848
    public void getNoncesInt() throws UsbException {
1849
        int bs = 12+extraSolutions*4;
1850
        byte[] buf = new byte[numNonces*bs];
1851
        boolean overflow = false;
1852
 
1853
        long t = new Date().getTime();
1854
        synchronized (ztex) {
1855
            try {
1856
                selectFpga();
1857
            }
1858
            catch ( InvalidFirmwareException e )  {
1859
            // shouldn't occur
1860
            }
1861
            ztex.vendorRequest2( 0x81, "Read hash data", 0, 0, buf, numNonces*bs );
1862
        }
1863
        usbTime += new Date().getTime() - t;
1864
 
1865
//      System.out.print(dataToHexStr(buf)+"            ");
1866
        for ( int i=0; i<numNonces; i++ ) {
1867
            goldenNonce[i*(1+extraSolutions)] = dataToInt(buf,i*bs+0) - offsNonces;
1868
            int j = dataToInt(buf,i*bs+4) - offsNonces;
1869
            overflow |= ((j >> 4) & 0xfffffff) < ((nonce[i]>>4) & 0xfffffff);
1870
            nonce[i] = j;
1871
            hash7[i] = dataToInt(buf,i*bs+8);
1872
            for ( j=0; j<extraSolutions; j++ )
1873
                goldenNonce[i*(1+extraSolutions)+1+j] = dataToInt(buf,i*bs+12+j*4) - offsNonces;
1874
        }
1875
        if ( overflow && ! PollLoop.scanMode )
1876
            overflowCount += 1;
1877
    }
1878
 
1879
// ******* checkNonce *******************************************************
1880
    public boolean checkNonce( int n, int h ) throws UsbException {
1881
        int offs[] = { 0, 1, -1, 2, -2 };
1882
//      int offs[] = { 0 };
1883
        for (int i=0; i<offs.length; i++ ) {
1884
            if ( getHash(n + offs[i]) == h + 0x5be0cd19 )
1885
                return true;
1886
        }
1887
        return false;
1888
    }
1889
 
1890
// ******* totalHashRate *******************************************************
1891
    public double totalHashRate () {
1892
        return fatalError == null ? (freqM+1)*freqM1*(1-errorRate[freqM])*hashesPerClock : 0;
1893
    }
1894
 
1895
// ******* submittedHashRate ***************************************************
1896
    public double submittedHashRate () {
1897
        return fatalError == null ? 4.294967296e6 * totalSubmittedCount / (new Date().getTime()-startTime) : 0;
1898
    }
1899
 
1900
// ******* printInfo ***********************************************************
1901
    public void printInfo( boolean force ) {
1902
        long t = new Date().getTime();
1903
        if ( !force && (clusterMode || lastInfoTime+infoInterval > t || !isRunning) )
1904
            return;
1905
 
1906
        if ( fatalError != null ) {
1907
            printMsg2(name + ": " + fatalError);
1908
            return;
1909
        }
1910
 
1911
        if ( suspended ) {
1912
            printMsg2(name + ": Suspended");
1913
            return;
1914
        }
1915
 
1916
        StringBuffer sb = new StringBuffer( "f=" + String.format("%.2f",(freqM+1)*freqM1)+"MHz" );
1917
 
1918
        if ( errorWeight[freqM]>20 )
1919
            sb.append(",  errorRate="+ String.format("%.2f",errorRate[freqM]*100)+"%");
1920
 
1921
        if ( errorWeight[freqM]>100 )
1922
            sb.append(",  maxErrorRate="+ String.format("%.2f",maxErrorRate[freqM]*100)+"%");
1923
 
1924
/*      if ( freqM<255 && (errorWeight[freqM+1]>100.1 || maxErrorRate[freqM+1]>0.001 ) )
1925
            sb.append(",  nextMaxErrorRate="+ String.format("%.2f",maxErrorRate[freqM+1]*100)+"%"); */
1926
 
1927
        double hr = (freqM+1)*freqM1*(1-errorRate[freqM])*hashesPerClock;
1928
 
1929
        if ( errorWeight[freqM]>20 )
1930
            sb.append(",  hashRate=" + String.format("%.1f", hr )+"MH/s" );
1931
 
1932
        try {
1933
            sb.append(", T=" + String.format("%.1f",ztex.tempSensorRead(fpgaNum)) + "°C");
1934
        }
1935
        catch ( Exception e ) {
1936
        }
1937
 
1938
        sb.append(",  submitted " +submittedCount+" new nonces,  luckFactor=" + String.format("%.2f", submittedHashRate()/hr+0.0049 ));
1939
        submittedCount = 0;
1940
 
1941
        printMsg2(name + ": " + sb.toString());
1942
 
1943
        lastInfoTime = t;
1944
    }
1945
 
1946
// ******* getDescriptor *******************************************************
1947
    private void getDescriptor () throws UsbException, FirmwareException {
1948
        byte[] buf = new byte[64];
1949
 
1950
        ztex.vendorRequest2( 0x82, "Read descriptor", 0, 0, buf, 64 );
1951
        if ( buf[0] != 5 ) {
1952
            if ( ( buf[0] != 2 ) && ( buf[0] != 4 ) ) {
1953
                throw new FirmwareException("Invalid BTCMiner descriptor version. Firmware must be updated.");
1954
            }
1955
            msg("Warning: Firmware out of date");
1956
        }
1957
        numNonces = (buf[1] & 255) + 1;
1958
        offsNonces = ((buf[2] & 255) | ((buf[3] & 255) << 8)) - 10000;
1959
        freqM1 = ( (buf[4] & 255) | ((buf[5] & 255) << 8) ) * 0.01;
1960
        freqM = (buf[6] & 255);
1961
        freqMaxM = (buf[7] & 255);
1962
        if ( freqM > freqMaxM )
1963
            freqM = freqMaxM;
1964
        freqMDefault = freqM;
1965
 
1966
        suspendSupported = buf[0] == 5;
1967
 
1968
        hashesPerClock = buf[0] > 2 ? ( ( (buf[8] & 255) | ((buf[9] & 255) << 8) ) +1 )/128.0 : 1.0;
1969
        extraSolutions = buf[0] > 4 ? buf[10] : 0;
1970
 
1971
        int i0 = buf[0] > 4 ? 11 : ( buf[0] == 4 ? 10 : 8 );
1972
        int i = i0;
1973
        while ( i<64 && buf[i]!=0 )
1974
            i++;
1975
        if ( i < i0+1)
1976
            throw new FirmwareException("Invalid bitstream file name");
1977
        bitFileName = new String(buf, i0, i-i0);
1978
 
1979
        if ( buf[0] < 4 ) {
1980
            if ( bitFileName.substring(0,13).equals("ztex_ufm1_15b") )
1981
                hashesPerClock = 0.5;
1982
            msg( "Warning: HASHES_PER_CLOCK not defined, assuming " + hashesPerClock );
1983
        }
1984
    }
1985
 
1986
// ******* checkUpdate **********************************************************
1987
    public boolean checkUpdate() {
1988
        long t = new Date().getTime();
1989
        if ( !isRunning ) return true;
1990
        if ( ignoreErrorTime > t ) return false;
1991
        if ( newCount < newBlockMonitor.newCount) return true;
1992
        if ( disableTime[prevRpcNum] > t ) return true;
1993
        if ( lastGetWorkTime + maxPollInterval < t ) return true;
1994
        for ( int i=0; i<numNonces ; i++ )
1995
            if ( ((nonce[i]>>1) & 0x7fffffff) > (0x38000000 + Math.round(Math.random()*0x10000000)) ) return true;
1996
        return false;
1997
    }
1998
 
1999
// ******* descriptorInfo ******************************************************
2000
    public String descriptorInfo () {
2001
        return "bitfile=" + bitFileName + "   f_default=" + String.format("%.2f",freqM1 * (freqMDefault+1)) + "MHz  f_max=" + String.format("%.2f",freqM1 * (freqMaxM+1))+ "MHz  HpC="+hashesPerClock+"H";
2002
    }
2003
 
2004
// ******* resetCounters ******************************************************Ü
2005
    public void resetCounters () {
2006
        while ( freqMDefault<freqM && errorWeight[freqMDefault+1]>100 )
2007
            freqMDefault++;
2008
 
2009
        for ( int i=0; i<255; i++ ) {
2010
            errorCount[i] *= 0.05;
2011
            errorWeight[i] *= 0.05;
2012
            errorRate[i]=0;
2013
            maxErrorRate[i]=0;
2014
        }
2015
        startTime = new Date().getTime();
2016
        totalSubmittedCount = 0;
2017
    }
2018
 
2019
// *****************************************************************************
2020
// ******* main ****************************************************************
2021
// *****************************************************************************
2022
    public static void main (String args[]) {
2023
 
2024
        int devNum = -1;
2025
        boolean workarounds = false;
2026
 
2027
        String firmwareFile = null, snString = null;
2028
        boolean printBus = false;
2029
        boolean verbose = false;
2030
        boolean eraseFirmware = false;
2031
 
2032
        String filterType = null;
2033
        String logFileName = "BTCMiner.log";
2034
 
2035
        char mode = 's';
2036
 
2037
        rpcCount = 1;
2038
        rpcurl[0] = "http://127.0.0.1:8332";
2039
        rpcuser[0] = null;
2040
        rpcpassw[0] = null;
2041
 
2042
        try {
2043
// init USB stuff
2044
            LibusbJava.usb_init();
2045
 
2046
 
2047
// scan the command line arguments
2048
            for (int i=0; i<args.length; i++ ) {
2049
                if ( args[i].equals("-d") ) {
2050
                    i++;
2051
                    try {
2052
                        if (i>=args.length) throw new Exception();
2053
                        devNum = Integer.parseInt( args[i] );
2054
                    }
2055
                    catch (Exception e) {
2056
                        throw new ParameterException("Device number expected after -d");
2057
                    }
2058
                }
2059
                else if ( args[i].equals("-l") ) {
2060
                    i++;
2061
                    if (i>=args.length) {
2062
                        throw new ParameterException("Error: File name expected after `-l'");
2063
                    }
2064
                    try {
2065
                        logFileName = args[i];
2066
                    }
2067
                    catch (Exception e) {
2068
                        throw new ParameterException("Error: File name expected after `-l': "+e.getLocalizedMessage() );
2069
                    }
2070
                }
2071
                else if ( args[i].equals("-l2") ) {
2072
                    i++;
2073
                    if (i>=args.length) {
2074
                        throw new ParameterException("Error: File name expected after `-l2'");
2075
                    }
2076
                    try {
2077
                        logFile2 = new PrintStream ( new FileOutputStream ( args[i], true ), true );
2078
                    }
2079
                    catch (Exception e) {
2080
                        throw new ParameterException("Error: File name expected after `-l2': "+e.getLocalizedMessage() );
2081
                    }
2082
                }
2083
                else if ( args[i].equals("-bl") ) {
2084
                    i++;
2085
                    if (i>=args.length) {
2086
                        throw new ParameterException("Error: File name expected after `-dl'");
2087
                    }
2088
                    try {
2089
                        blkLogFile = new PrintStream ( new FileOutputStream ( args[i], true ), true );
2090
                    }
2091
                    catch (Exception e) {
2092
                        throw new ParameterException("Error: File name expected after `-bl': "+e.getLocalizedMessage() );
2093
                    }
2094
                }
2095
                else if ( args[i].equals("-c") ) {
2096
                    i++;
2097
                    if (i>=args.length) {
2098
                        throw new ParameterException("Error: File name expected after `-c'");
2099
                    }
2100
                    try {
2101
                        in2FileName = args[i];
2102
                        new Thread() {
2103
                            public void run () {
2104
                                try {
2105
                                    in2 = new FileInputStream ( in2FileName );
2106
                                }
2107
                                catch (Exception e) {
2108
                                    System.err.println("Error: File name expected after `-c': "+e.getLocalizedMessage() );
2109
                                }
2110
                            }
2111
                        }.start();
2112
                    }
2113
                    catch (Exception e) {
2114
                        throw new ParameterException("Error: File name expected after `-c': "+e.getLocalizedMessage() );
2115
                    }
2116
                }
2117
                else if ( args[i].equals("-host") ) {
2118
                    i++;
2119
                    try {
2120
                        if (i>=args.length) throw new Exception();
2121
                        rpcurl[0] = args[i];
2122
                    }
2123
                    catch (Exception e) {
2124
                        throw new ParameterException("URL expected after -host");
2125
                    }
2126
                }
2127
                else if ( args[i].equals("-u") ) {
2128
                    i++;
2129
                    try {
2130
                        if (i>=args.length) throw new Exception();
2131
                        rpcuser[0] = args[i];
2132
                    }
2133
                    catch (Exception e) {
2134
                        throw new ParameterException("User expected after -u");
2135
                    }
2136
                }
2137
                else if ( args[i].equals("-p") ) {
2138
                    i++;
2139
                    try {
2140
                        if (i>=args.length) throw new Exception();
2141
                        rpcpassw[0] = args[i];
2142
                    }
2143
                    catch (Exception e) {
2144
                        throw new ParameterException("Password expected after -p");
2145
                    }
2146
                }
2147
                else if ( args[i].equals("-b") ) {
2148
                    i+=3;
2149
                    try {
2150
                        if (i>=args.length) throw new Exception();
2151
                        if ( rpcCount >= maxRpcCount )
2152
                            throw new IndexOutOfBoundsException("Maximum amount of backup servers reached");
2153
                        rpcurl[rpcCount] = args[i-2];
2154
                        rpcuser[rpcCount] = args[i-1];
2155
                        rpcpassw[rpcCount] = args[i];
2156
                        rpcCount+=1;
2157
                    }
2158
                    catch (Exception e) {
2159
                        throw new ParameterException("<URL> <user name> <password> expected after -b");
2160
                    }
2161
                }
2162
                else if ( args[i].equals("-lp") ) {
2163
                    i+=3;
2164
                    try {
2165
                        if (i>=args.length) throw new Exception();
2166
                        longPollURL = args[i-2];
2167
                        longPollUser = args[i-1];
2168
                        longPollPassw = args[i];
2169
                    }
2170
                    catch (Exception e) {
2171
                        throw new ParameterException("<URL> <user name> <password> expected after -lp");
2172
                    }
2173
                }
2174
                else if ( args[i].equals("-f") ) {
2175
                    i++;
2176
                    try {
2177
                        if (i>=args.length) throw new Exception();
2178
                        firmwareFile = args[i];
2179
                    }
2180
                    catch (Exception e) {
2181
                        throw new ParameterException("ihx file name expected after -f");
2182
                    }
2183
                }
2184
                else if ( args[i].equals("-pt") ) {
2185
                    i++;
2186
                    try {
2187
                        if (i>=args.length) throw new Exception();
2188
                        filterType = args[i];
2189
                    }
2190
                    catch (Exception e) {
2191
                        throw new ParameterException("<string> after -pt");
2192
                    }
2193
                }
2194
                else if ( args[i].equals("-ps") ) {
2195
                    i++;
2196
                    try {
2197
                        if (i>=args.length) throw new Exception();
2198
                        filterSN = args[i];
2199
                    }
2200
                    catch (Exception e) {
2201
                        throw new ParameterException("<string> after -ps");
2202
                    }
2203
                }
2204
                else if ( args[i].equals("-m") ) {
2205
                    i++;
2206
                    try {
2207
                        if (i>=args.length) throw new Exception();
2208
                        if ( args[i].length() < 1 ) throw new Exception();
2209
                        mode = Character.toLowerCase( args[i].charAt(0) );
2210
                        if ( mode != 's' && mode != 't'  && mode != 'p' && mode != 'c' ) throw new Exception();
2211
                    }
2212
                    catch (Exception e) {
2213
                        throw new ParameterException("s|t|p|c expected after -m");
2214
                    }
2215
                }
2216
                else if ( args[i].equals("-s") ) {
2217
                    i++;
2218
                    if ( i >= args.length ) {
2219
                        throw new ParameterException("Error: String expected after -s");
2220
                    }
2221
                    snString = checkSnString(args[i]);
2222
                }
2223
                else if ( args[i].equals("-i") ) {
2224
                    printBus = true;
2225
                }
2226
                else if ( args[i].equals("-v") ) {
2227
                    verbose = true;
2228
                }
2229
                else if ( args[i].equals("-rf") ) {
2230
                    eraseFirmware = true;
2231
                }
2232
                else if ( args[i].equals("-ep0") ) {
2233
                    forceEP0Config = true;
2234
                }
2235
                else if ( args[i].equals("-tc") ) {
2236
                    targetCheck = true;
2237
                }
2238
                else if ( args[i].equals("-h") ) {
2239
                        System.err.println(ParameterException.helpMsg);
2240
                        System.exit(0);
2241
                }
2242
                else if ( args[i].equals("-n") ) {
2243
                    i++;
2244
                    try {
2245
                        if (i>=args.length) throw new Exception();
2246
                         BTCMinerCluster.maxDevicesPerThread = Integer.parseInt( args[i] );
2247
                    }
2248
                    catch (Exception e) {
2249
                        throw new ParameterException("Number expected after -n");
2250
                    }
2251
                }
2252
                else if ( args[i].equals("-oh") ) {
2253
                    i++;
2254
                    try {
2255
                        if (i>=args.length) throw new Exception();
2256
                        overheatThreshold = Double.parseDouble( args[i] );
2257
                    }
2258
                    catch (Exception e) {
2259
                        throw new ParameterException("Number expected after -oh");
2260
                    }
2261
                }
2262
                else if ( args[i].equals("-t") ) {
2263
                    i++;
2264
                    try {
2265
                        if (i>=args.length) throw new Exception();
2266
                        tempLimit = Double.parseDouble( args[i] );
2267
                    }
2268
                    catch (Exception e) {
2269
                        throw new ParameterException("Number expected after -t");
2270
                    }
2271
                }
2272
                else if ( args[i].equals("-e") ) {
2273
                    i++;
2274
                    try {
2275
                        if (i>=args.length) throw new Exception();
2276
                        double d = Double.parseDouble( args[i] );
2277
                        if ( d < 0.0001 ) d = 0.0001;
2278
                        if ( d < maxMaxErrorRate ) maxMaxErrorRate = d;
2279
                    }
2280
                    catch (Exception e) {
2281
                        throw new ParameterException("Number expected after -e");
2282
                    }
2283
                }
2284
                else throw new ParameterException("Invalid Parameter: "+args[i]);
2285
            }
2286
 
2287
            logFile = new PrintStream ( new FileOutputStream ( logFileName, true ), true );
2288
 
2289
            if ( overheatThreshold > 0.1001 ) System.err.println("Warning: overheat threshold set to " + overheatThreshold +": overheat shutdown may be triggered too late, recommended values: 0..0.1");
2290
 
2291
            if ( BTCMinerCluster.maxDevicesPerThread < 1 )
2292
                BTCMinerCluster.maxDevicesPerThread = 127;
2293
 
2294
            if ( mode != 'c' && filterSN != null)
2295
                filterSN = checkSnString(filterSN);
2296
 
2297
            if ( mode != 't' && mode != 'p' ) {
2298
                if ( rpcuser[0] == null ) {
2299
                    System.out.print("Enter RPC user name: ");
2300
                    rpcuser[0] = new BufferedReader(new InputStreamReader( System.in) ).readLine();
2301
                }
2302
 
2303
                if ( rpcpassw[0] == null ) {
2304
                    System.out.print("Enter RPC password: ");
2305
                    rpcpassw[0] = new BufferedReader(new InputStreamReader(System.in) ).readLine();
2306
                }
2307
            }
2308
 
2309
/*          Authenticator.setDefault(new Authenticator() {
2310
                protected PasswordAuthentication getPasswordAuthentication() {
2311
                    return new PasswordAuthentication (BTCMiner.rpcuser, BTCMiner.rpcpassw.toCharArray());
2312
                }
2313
            }); */
2314
 
2315
 
2316
            if ( mode == 's' || mode == 't' ) {
2317
                if ( devNum < 0 )
2318
                    devNum = 0;
2319
 
2320
                ZtexScanBus1 bus = new ZtexScanBus1( ZtexDevice1.ztexVendorId, ZtexDevice1.ztexProductId, filterSN==null, false, 1,  filterSN, 10, 0, 1, 0 );
2321
                if ( bus.numberOfDevices() <= 0) {
2322
                    System.err.println("No devices found");
2323
                    System.exit(0);
2324
                }
2325
                if ( printBus ) {
2326
                    printBus(bus);
2327
                    System.exit(0);
2328
                }
2329
 
2330
                BTCMiner miner = new BTCMiner ( bus.device(devNum), firmwareFile, verbose );
2331
                if ( mode == 't' ) { // single mode
2332
                    miner.initWork(
2333
                        hexStrToData( "0000000122f3e795bb7a55b2b4a580e0dbba9f2a5aedbfc566632984000008de00000000e951667fbba0cfae7719ab2fb4ab8d291a20d387782f4610297f5899cc58b7d64e4056801a08e1e500000000000000800000000000000000000000000000000000000000000000000000000000000000000000000000000080020000" ),
2334
                        hexStrToData( "28b81bd40a0e1b75d18362cb9a2faa61669d42913f26194f776c349e97559190" )
2335
                    );
2336
 
2337
                    miner.sendData ( );
2338
                    for (int i=0; i<200; i++ ) {
2339
                        try {
2340
                            Thread.sleep( 250 );
2341
                        }
2342
                        catch ( InterruptedException e) {
2343
                        }
2344
                        miner.getNoncesInt();
2345
 
2346
                        for ( int j=0; j<miner.numNonces; j++ ) {
2347
                            System.out.println( i +"-" + j + ":  " + intToHexStr(miner.nonce[j]) + "    " + miner.checkNonce(miner.nonce[j],miner.hash7[j])  + "   " +  miner.overflowCount + "    " + intToHexStr(miner.goldenNonce[j*(1+miner.extraSolutions)]) + "      "  + intToHexStr( miner.getHash( miner.goldenNonce[j]) ) );
2348
                        }
2349
                    }
2350
                }
2351
                else { // single mode
2352
                    Vector<BTCMiner> v = new Vector<BTCMiner>();
2353
                    v.add ( miner );
2354
                    for ( int i=1; i<miner.numberOfFpgas(); i++ )
2355
                        v.add(new BTCMiner(miner.ztex(), miner.fpgaNum(i), verbose) );
2356
                    System.out.println("");
2357
                    if ( miner.ztex().numberOfFpgas()>1 )
2358
                        System.out.println("A multi-FPGA board is detected. Use the cluster mode for additional statistics.");
2359
                    System.out.println("Disconnect device or press Ctrl-C for exit\n");
2360
                    new PollLoop(v).run();
2361
                }
2362
            }
2363
            else if ( mode == 'p' ) {
2364
                if ( eraseFirmware && filterType == null && filterSN == null )
2365
                    throw new ParameterException("-rf requires -pt or -ps");
2366
 
2367
/*              ZtexScanBus1 bus = ( filterType == null && filterSN == null )
2368
                        ? new ZtexScanBus1( ZtexDevice1.cypressVendorId, ZtexDevice1.cypressProductId, true, false, 1)
2369
                        : new ZtexScanBus1( ZtexDevice1.ztexVendorId, ZtexDevice1.ztexProductId, false, false, 1, null, 10, 0, 1, 0 ); */
2370
                ZtexScanBus1 bus = new ZtexScanBus1( ZtexDevice1.ztexVendorId, ZtexDevice1.ztexProductId, filterType==null && filterSN==null, false, 1, null, 10, 0, 0, 0 );
2371
 
2372
                if ( bus.numberOfDevices() <= 0) {
2373
                    System.err.println("No devices found");
2374
                    System.exit(0);
2375
                }
2376
                if ( printBus ) {
2377
                    printBus(bus);
2378
                    System.exit(0);
2379
                }
2380
                if ( firmwareFile == null && !eraseFirmware )
2381
                    throw new Exception("Parameter -f or -rf required in programming mode");
2382
 
2383
                int imin=0, imax=bus.numberOfDevices()-1;
2384
                if ( devNum >= 0 ) {
2385
                    imin=devNum;
2386
                    imax=devNum;
2387
                }
2388
 
2389
                ZtexIhxFile1 ihxFile = eraseFirmware ? null : new ZtexIhxFile1( firmwareFile );
2390
 
2391
                int j = 0;
2392
                for (int i=imin; i<=imax; i++ ) {
2393
                    ZtexDevice1 dev = bus.device(i);
2394
                    if ( ( filterSN == null || filterSN.equals(dev.snString()) ) &&
2395
                         ( filterType == null || ( (dev.productId(2) == 1) && filterType.equals(getType(dev))) ) &&
2396
                         ( filterType != null || filterSN != null || dev.productId(2) == 0) ) {
2397
                        Ztex1v1 ztex = new Ztex1v1 ( dev );
2398
                        if ( snString != null && ihxFile != null )
2399
                            ihxFile.setSnString( snString );
2400
                        else if ( ztex.valid() && ihxFile != null )
2401
                            ihxFile.setSnString( dev.snString() );
2402
                        if ( eraseFirmware ) {
2403
                            ztex.eepromDisable();
2404
                            System.out.println("EEPROM erased: " + ztex.toString());
2405
                        }
2406
                        else {
2407
                            System.out.println("\nold: "+ztex.toString());
2408
                            System.out.println("Firmware upload time: " + ztex.uploadFirmware( ihxFile, false ) + " ms");
2409
                            System.out.println("EEPROM programming time: " + ztex.eepromUpload( ihxFile, false ) + " ms");
2410
                            System.out.println("new: " + ztex.toString());
2411
                        }
2412
                    j+=1;
2413
                    }
2414
                }
2415
                System.out.println("\ntotal amount of (re-)programmed devices: " + j);
2416
            }
2417
            else if ( mode == 'c' ) {
2418
                new BTCMinerCluster( verbose );
2419
            }
2420
 
2421
 
2422
        }
2423
        catch (Exception e) {
2424
            System.out.println("Error: "+e.getLocalizedMessage() );
2425
        }
2426
 
2427
        if ( BTCMiner.newBlockMonitor != null ) {
2428
            BTCMiner.newBlockMonitor.running = false;
2429
            BTCMiner.newBlockMonitor.interrupt();
2430
        }
2431
 
2432
        System.exit(0);
2433
 
2434
   }
2435
 
2436
}

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.