@@ -254,10 +254,28 @@ void EthStratumClient::disconnect_finalize() {
254
254
m_connected.store (false , std::memory_order_relaxed);
255
255
m_disconnecting.store (false , std::memory_order::memory_order_relaxed);
256
256
257
- // Trigger handlers
258
- if (m_onDisconnected) { m_onDisconnected (); }
259
-
260
257
258
+ // If we got disconnected during autodetection phase
259
+ // reissue a connect lowering stratum mode checks
260
+ if (!m_conn->StratumModeConfirmed ())
261
+ {
262
+ unsigned l = m_conn->StratumMode ();
263
+ if (l > 0 )
264
+ {
265
+ l--;
266
+ m_conn->SetStratumMode (l);
267
+
268
+ // Repost a new connection attempt
269
+ m_io_service.post (m_io_strand.wrap (boost::bind (&EthStratumClient::connect, this )));
270
+ return ;
271
+ }
272
+ }
273
+
274
+ // Trigger handlers
275
+ if (m_onDisconnected)
276
+ {
277
+ m_onDisconnected ();
278
+ }
261
279
}
262
280
263
281
void EthStratumClient::resolve_handler (const boost::system::error_code& ec, tcp::resolver::iterator i)
@@ -304,14 +322,24 @@ void EthStratumClient::reset_work_timeout()
304
322
void EthStratumClient::start_connect ()
305
323
{
306
324
if (!m_endpoints.empty ()) {
325
+
326
+ // If still in the middle of autodetection keep
327
+ // lastly used endpoint
328
+ if (m_conn->StratumModeConfirmed () == true )
329
+ {
330
+ // Sets active end point and removes
331
+ // it from queue
332
+ m_endpoint = m_endpoints.front ();
333
+ m_endpoints.pop ();
334
+ }
335
+ else
336
+ {
337
+ m_endpoint = m_endpoints.front ();
338
+ }
307
339
308
- // Sets active end point and removes
309
- // it from queue
310
- m_endpoint = m_endpoints.front ();
311
- m_endpoints.pop ();
312
340
313
- dev::setThreadName (" stratum" );
314
- cnote << (" Trying " + toString (m_endpoint) + " ..." );
341
+ dev::setThreadName (" stratum" );
342
+ cnote << (" Trying " + toString (m_endpoint) + " ..." );
315
343
316
344
m_conntimer.expires_from_now (boost::posix_time::seconds (m_responsetimeout));
317
345
m_conntimer.async_wait (m_io_strand.wrap (boost::bind (&EthStratumClient::check_connect_timeout, this , boost::asio::placeholders::error)));
@@ -461,53 +489,110 @@ void EthStratumClient::connect_handler(const boost::system::error_code& ec)
461
489
if (m_onConnected) { m_onConnected (); }
462
490
reset_work_timeout ();
463
491
464
- string user;
465
- size_t p;
466
-
467
- Json::Value jReq;
468
- jReq[" id" ] = unsigned (1 );
469
- jReq[" method" ] = " mining.subscribe" ;
470
- jReq[" params" ] = Json::Value (Json::arrayValue);
471
-
472
- m_worker.clear ();
473
- p = m_conn->User ().find_first_of (" ." );
474
- if (p != string::npos) {
475
- user = m_conn->User ().substr (0 , p);
476
-
477
- // There should be at least one char after dot
478
- // returned p is zero based
479
- if (p < (m_conn->User ().length () -1 ))
480
- m_worker = m_conn->User ().substr (++p);
481
- }
482
- else
483
- user = m_conn->User ();
484
-
485
- switch (m_conn->Version ()) {
492
+ // Extract user and worker
493
+ size_t p;
494
+ m_worker.clear ();
495
+ p = m_conn->User ().find_first_of (" ." );
496
+ if (p != string::npos)
497
+ {
498
+ m_user = m_conn->User ().substr (0 , p);
499
+
500
+ // There should be at least one char after dot
501
+ // returned p is zero based
502
+ if (p < (m_conn->User ().length () - 1 ))
503
+ m_worker = m_conn->User ().substr (++p);
504
+ }
505
+ else
506
+ {
507
+ m_user = m_conn->User ();
508
+ }
509
+
510
+ /*
511
+ If this connection has not gone through an autodetection of stratum mode
512
+ begin it now.
513
+ Autodetection process passes all known stratum modes.
514
+ - 1st pass EthStratumClient::ETHEREUMSTRATUM (2)
515
+ - 2nd pass EthStratumClient::ETHPROXY (1)
516
+ - 3rd pass EthStratumClient::STRATUM (0)
517
+ */
518
+
519
+ Json::Value jReq;
520
+ jReq[" id" ] = unsigned (1 );
521
+ jReq[" method" ] = " mining.subscribe" ;
522
+ jReq[" params" ] = Json::Value (Json::arrayValue);
523
+
524
+ if (!m_conn->StratumModeConfirmed ())
525
+ {
526
+ switch (m_conn->StratumMode ())
527
+ {
486
528
487
- case EthStratumClient::STRATUM :
529
+ case 0 :
488
530
531
+ m_conn->SetStratumMode (0 , false );
532
+ jReq[" id" ] = unsigned (1 );
489
533
jReq[" jsonrpc" ] = " 2.0" ;
534
+ jReq[" method" ] = " mining.subscribe" ;
535
+ jReq[" params" ] = Json::Value (Json::arrayValue);
490
536
491
- break ;
492
-
493
- case EthStratumClient::ETHPROXY:
537
+ case 1 :
494
538
539
+ m_conn->SetStratumMode (1 , false );
540
+ jReq[" id" ] = unsigned (1 );
495
541
jReq[" method" ] = " eth_submitLogin" ;
496
- if (m_worker.length ()) jReq[" worker" ] = m_worker;
497
- jReq[" params" ].append (user + m_conn->Path ());
498
- if (!m_email.empty ()) jReq[" params" ].append (m_email);
542
+ jReq[" params" ] = Json::Value (Json::arrayValue);
543
+ if (m_worker.length ())
544
+ jReq[" worker" ] = m_worker;
545
+ jReq[" params" ].append (m_user + m_conn->Path ());
546
+ if (!m_email.empty ())
547
+ jReq[" params" ].append (m_email);
499
548
500
549
break ;
501
550
502
- case EthStratumClient::ETHEREUMSTRATUM:
503
-
504
- jReq[" params" ].append (" ethminer " + std::string (ethminer_get_buildinfo ()->project_version ));
551
+ case 999 :
552
+ case 2 :
553
+ m_conn->SetStratumMode (2 , false );
554
+ jReq[" params" ].append (
555
+ " ethminer " + std::string (ethminer_get_buildinfo ()->project_version ));
505
556
jReq[" params" ].append (" EthereumStratum/1.0.0" );
557
+ break ;
506
558
559
+ default :
507
560
break ;
508
- }
561
+ }
562
+
563
+ }
564
+ else
565
+ {
566
+ switch (m_conn->StratumMode ())
567
+ {
568
+ case EthStratumClient::STRATUM:
569
+
570
+ jReq[" jsonrpc" ] = " 2.0" ;
509
571
510
- // Send first message
572
+ break ;
573
+
574
+ case EthStratumClient::ETHPROXY:
575
+
576
+ jReq[" method" ] = " eth_submitLogin" ;
577
+ if (m_worker.length ())
578
+ jReq[" worker" ] = m_worker;
579
+ jReq[" params" ].append (m_user + m_conn->Path ());
580
+ if (!m_email.empty ())
581
+ jReq[" params" ].append (m_email);
582
+
583
+ break ;
584
+
585
+ case EthStratumClient::ETHEREUMSTRATUM:
586
+
587
+ jReq[" params" ].append (
588
+ " ethminer " + std::string (ethminer_get_buildinfo ()->project_version ));
589
+ jReq[" params" ].append (" EthereumStratum/1.0.0" );
590
+
591
+ break ;
592
+ }
593
+ }
594
+
595
+ // Send first message
511
596
sendSocketData (jReq);
512
597
513
598
// Begin receive data
@@ -588,7 +673,7 @@ void EthStratumClient::processReponse(Json::Value& responseObject)
588
673
_isNotification = (_id == unsigned (0 ) || _method != " " );
589
674
590
675
// Notifications of new jobs are like responses to get_work requests
591
- if (_isNotification && _method == " " && m_conn->Version () == EthStratumClient::ETHPROXY && responseObject[" result" ].isArray ()) {
676
+ if (_isNotification && _method == " " && m_conn->StratumMode () == EthStratumClient::ETHPROXY && responseObject[" result" ].isArray ()) {
592
677
_method = " mining.notify" ;
593
678
}
594
679
@@ -622,17 +707,123 @@ void EthStratumClient::processReponse(Json::Value& responseObject)
622
707
623
708
case 1 :
624
709
625
- // Response to "mining.subscribe" (https://en.bitcoin.it/wiki/Stratum_mining_protocol#mining.subscribe)
710
+ /*
711
+ This is the response to very first message after connection.
712
+ I wish I could manage to have different Ids but apparently ethermine.org always replies
713
+ to first message with id=1 regardless the id originally sent.
714
+ */
715
+ if (!m_conn->StratumModeConfirmed ())
716
+ {
717
+ switch (m_conn->StratumMode ())
718
+ {
719
+ case EthStratumClient::ETHEREUMSTRATUM:
720
+
721
+ // In case of success we also need to verify third parameter of "result" array
722
+ // member is exactly "EthereumStratum/1.0.0". Otherwise try with another mode
723
+ if (_isSuccess)
724
+ {
725
+ if (!jResult.isArray () || !jResult[0 ].isArray () || jResult[0 ].size () != 3 ||
726
+ jResult[0 ].get ((Json::Value::ArrayIndex)2 , " " ).asString () !=
727
+ " EthereumStratum/1.0.0" )
728
+ {
729
+ // This is not a proper ETHEREUMSTRATUM response.
730
+ // Proceed with next step of autodetection ETHPROXY compatible
731
+ m_conn->SetStratumMode (1 );
732
+ jReq[" id" ] = unsigned (1 );
733
+ jReq[" method" ] = " eth_submitLogin" ;
734
+ jReq[" params" ] = Json::Value (Json::arrayValue);
735
+ if (m_worker.length ())
736
+ jReq[" worker" ] = m_worker;
737
+ jReq[" params" ].append (m_user + m_conn->Path ());
738
+ if (!m_email.empty ())
739
+ jReq[" params" ].append (m_email);
740
+
741
+ sendSocketData (jReq);
742
+ return ;
743
+ }
744
+ else
745
+ {
746
+ // ETHEREUMSTRATUM is confirmed
747
+ cnote << " Stratum mode detected : ETHEREUMSTRATUM (NiceHash)" ;
748
+ m_conn->SetStratumMode (2 , true );
749
+ }
750
+ }
751
+ else
752
+ {
753
+ // This is not a proper ETHEREUMSTRATUM response.
754
+ // Proceed with next step of autodetection ETHPROXY compatible
755
+ m_conn->SetStratumMode (1 );
756
+ jReq[" id" ] = unsigned (1 );
757
+ jReq[" method" ] = " eth_submitLogin" ;
758
+ jReq[" params" ] = Json::Value (Json::arrayValue);
759
+ if (m_worker.length ())
760
+ jReq[" worker" ] = m_worker;
761
+ jReq[" params" ].append (m_user + m_conn->Path ());
762
+ if (!m_email.empty ())
763
+ jReq[" params" ].append (m_email);
764
+
765
+ sendSocketData (jReq);
766
+ return ;
767
+ }
768
+
769
+ break ;
770
+
771
+ case EthStratumClient::ETHPROXY:
772
+
773
+ if (!_isSuccess)
774
+ {
775
+ // In case of failure try next step which is STRATUM
776
+ m_conn->SetStratumMode (0 );
777
+ jReq[" id" ] = unsigned (1 );
778
+ jReq[" jsonrpc" ] = " 2.0" ;
779
+ jReq[" method" ] = " mining.subscribe" ;
780
+ jReq[" params" ] = Json::Value (Json::arrayValue);
781
+
782
+ sendSocketData (jReq);
783
+ return ;
784
+ }
785
+ else
786
+ {
787
+ // ETHPROXY is confirmed
788
+ cnote << " Stratum mode detected : ETHPROXY compatible" ;
789
+ m_conn->SetStratumMode (1 , true );
790
+ }
791
+
792
+ break ;
793
+
794
+ case EthStratumClient::STRATUM:
795
+
796
+ if (!_isSuccess)
797
+ {
798
+ // In case of failure we can't manage this connection
799
+ cwarn << " Unable to find suitable Stratum Mode" ;
800
+ m_conn->MarkUnrecoverable ();
801
+ disconnect ();
802
+ return ;
803
+ }
804
+ else
805
+ {
806
+ // STRATUM is confirmed
807
+ cnote << " Stratum mode detected : STRATUM" ;
808
+ m_conn->SetStratumMode (0 , true );
809
+ }
810
+
811
+ break ;
812
+ }
813
+ }
814
+
815
+
816
+ // Response to "mining.subscribe" (https://en.bitcoin.it/wiki/Stratum_mining_protocol#mining.subscribe)
626
817
// Result should be an array with multiple dimensions, we only care about the data if EthStratumClient::ETHEREUMSTRATUM
627
-
628
- switch (m_conn->Version ()) {
818
+ switch (m_conn->StratumMode ()) {
629
819
630
820
case EthStratumClient::STRATUM:
631
821
632
822
m_subscribed.store (_isSuccess, std::memory_order_relaxed);
633
823
if (!m_subscribed)
634
824
{
635
825
cnote << " Could not subscribe to stratum server" ;
826
+ m_conn->MarkUnrecoverable ();
636
827
disconnect ();
637
828
return ;
638
829
}
@@ -656,6 +847,7 @@ void EthStratumClient::processReponse(Json::Value& responseObject)
656
847
if (!m_subscribed)
657
848
{
658
849
cnote << " Could not login to ethproxy server:" << _errReason;
850
+ m_conn->MarkUnrecoverable ();
659
851
disconnect ();
660
852
return ;
661
853
}
@@ -678,6 +870,7 @@ void EthStratumClient::processReponse(Json::Value& responseObject)
678
870
if (!m_subscribed)
679
871
{
680
872
cnote << " Could not subscribe to stratum server:" << _errReason;
873
+ m_conn->MarkUnrecoverable ();
681
874
disconnect ();
682
875
return ;
683
876
}
@@ -785,7 +978,7 @@ void EthStratumClient::processReponse(Json::Value& responseObject)
785
978
// This is the response we get on first get_work request issued
786
979
// in mode EthStratumClient::ETHPROXY
787
980
// thus we change it to a mining.notify notification
788
- if (m_conn->Version () == EthStratumClient::ETHPROXY && responseObject[" result" ].isArray ()) {
981
+ if (m_conn->StratumMode () == EthStratumClient::ETHPROXY && responseObject[" result" ].isArray ()) {
789
982
_method = " mining.notify" ;
790
983
_isNotification = true ;
791
984
}
@@ -850,7 +1043,7 @@ void EthStratumClient::processReponse(Json::Value& responseObject)
850
1043
851
1044
unsigned prmIdx;
852
1045
853
- if (m_conn->Version () == EthStratumClient::ETHPROXY) {
1046
+ if (m_conn->StratumMode () == EthStratumClient::ETHPROXY) {
854
1047
855
1048
jPrm = responseObject.get (" result" , Json::Value::null);
856
1049
prmIdx = 0 ;
@@ -874,7 +1067,7 @@ void EthStratumClient::processReponse(Json::Value& responseObject)
874
1067
if (m_response_pending)
875
1068
m_stale = true ;
876
1069
877
- if (m_conn->Version () == EthStratumClient::ETHEREUMSTRATUM)
1070
+ if (m_conn->StratumMode () == EthStratumClient::ETHEREUMSTRATUM)
878
1071
{
879
1072
string sSeedHash = jPrm.get (1 , " " ).asString ();
880
1073
string sHeaderHash = jPrm.get (2 , " " ).asString ();
@@ -889,7 +1082,7 @@ void EthStratumClient::processReponse(Json::Value& responseObject)
889
1082
m_current.startNonce = bswap (*((uint64_t *)m_extraNonce.data ()));
890
1083
m_current.exSizeBits = m_extraNonceHexSize * 4 ;
891
1084
m_current.job_len = job.size ();
892
- if (m_conn->Version () == EthStratumClient::ETHEREUMSTRATUM)
1085
+ if (m_conn->StratumMode () == EthStratumClient::ETHEREUMSTRATUM)
893
1086
job.resize (64 , ' 0' );
894
1087
m_current.job = h256 (job);
895
1088
@@ -932,7 +1125,7 @@ void EthStratumClient::processReponse(Json::Value& responseObject)
932
1125
}
933
1126
}
934
1127
}
935
- else if (_method == " mining.set_difficulty" && m_conn->Version () == EthStratumClient::ETHEREUMSTRATUM)
1128
+ else if (_method == " mining.set_difficulty" && m_conn->StratumMode () == EthStratumClient::ETHEREUMSTRATUM)
936
1129
{
937
1130
jPrm = responseObject.get (" params" , Json::Value::null);
938
1131
if (jPrm.isArray ())
@@ -946,7 +1139,7 @@ void EthStratumClient::processReponse(Json::Value& responseObject)
946
1139
}
947
1140
}
948
1141
}
949
- else if (_method == " mining.set_extranonce" && m_conn->Version () == EthStratumClient::ETHEREUMSTRATUM)
1142
+ else if (_method == " mining.set_extranonce" && m_conn->StratumMode () == EthStratumClient::ETHEREUMSTRATUM)
950
1143
{
951
1144
jPrm = responseObject.get (" params" , Json::Value::null);
952
1145
if (jPrm.isArray ())
@@ -1056,7 +1249,7 @@ void EthStratumClient::submitSolution(const Solution& solution) {
1056
1249
jReq[" method" ] = " mining.submit" ;
1057
1250
jReq[" params" ] = Json::Value (Json::arrayValue);
1058
1251
1059
- switch (m_conn->Version ()) {
1252
+ switch (m_conn->StratumMode ()) {
1060
1253
1061
1254
case EthStratumClient::STRATUM:
1062
1255
@@ -1160,14 +1353,14 @@ void EthStratumClient::onRecvSocketDataCompleted(const boost::system::error_code
1160
1353
(ERR_GET_REASON (ec.value ()) == SSL_RECEIVED_SHUTDOWN)
1161
1354
)
1162
1355
{
1163
- cnote << " SSL Stream remotely closed by" << m_conn->Host ();
1356
+ cnote << " SSL Stream remotely closed by " << m_conn->Host ();
1164
1357
}
1165
1358
else if (ec == boost::asio::error::eof)
1166
1359
{
1167
- cnote << " Connection remotely closed by" << m_conn->Host ();
1360
+ cnote << " Connection remotely closed by " << m_conn->Host ();
1168
1361
}
1169
1362
else {
1170
- cwarn << " Socket read failed:" << ec.message ();
1363
+ cwarn << " Socket read failed: " << ec.message ();
1171
1364
}
1172
1365
disconnect ();
1173
1366
}
0 commit comments