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 |
|
|
}
|