538 lines
17 KiB
C++
538 lines
17 KiB
C++
//===
|
|
// by xolokram/TB
|
|
// 2013
|
|
//===
|
|
|
|
#include <iostream>
|
|
#include <fstream>
|
|
#include <cstdio>
|
|
#include <cstdlib>
|
|
#include <csignal>
|
|
#include <map>
|
|
|
|
#include "prime.h"
|
|
#include "serialize.h"
|
|
#include "bitcoinrpc.h"
|
|
#include "json/json_spirit_value.h"
|
|
#include <boost/thread.hpp>
|
|
#include <boost/asio.hpp>
|
|
|
|
#define VERSION_MAJOR 0
|
|
#define VERSION_MINOR 2
|
|
|
|
// <START> be compatible to original code (not actually used!)
|
|
#include "txdb.h"
|
|
#include "walletdb.h"
|
|
#include "wallet.h"
|
|
#include "ui_interface.h"
|
|
CWallet *pwalletMain;
|
|
CClientUIInterface uiInterface;
|
|
void StartShutdown() {
|
|
exit(0);
|
|
}
|
|
// </END>
|
|
|
|
/*********************************
|
|
* global variables, structs and extern functions
|
|
*********************************/
|
|
|
|
extern CBlockIndex *pindexBest;
|
|
extern void BitcoinMiner(CWallet *pwallet,
|
|
CBlockProvider *CBlockProvider,
|
|
unsigned int thread_id);
|
|
extern bool fPrintToConsole;
|
|
extern bool fDebug;
|
|
extern json_spirit::Object CallRPC(const std::string & strMethod,
|
|
const json_spirit::Array& params,
|
|
const std::string & server,
|
|
const std::string & port,
|
|
std::map<std::string,
|
|
std::string> & mapHeadersRet);
|
|
extern int FormatHashBlocks(void *pbuffer,
|
|
unsigned int len);
|
|
|
|
struct blockHeader_t {
|
|
// comments: BYTES <index> + <length>
|
|
int nVersion; // 0+4
|
|
uint256 hashPrevBlock; // 4+32
|
|
uint256 hashMerkleRoot; // 36+32
|
|
unsigned int nTime; // 68+4
|
|
unsigned int nBits; // 72+4
|
|
unsigned int nNonce; // 76+4
|
|
unsigned char primemultiplier[48]; // 80+48
|
|
}; // =128 bytes header (80 default + 48 primemultiplier)
|
|
|
|
static size_t thread_num_max;
|
|
static boost::asio::ip::tcp::socket* socket_to_server;
|
|
static boost::posix_time::ptime t_start;
|
|
static std::map<int,unsigned long> statistics;
|
|
static bool running;
|
|
static volatile int submitting_share;
|
|
|
|
/*********************************
|
|
* helping functions
|
|
*********************************/
|
|
|
|
void convertDataToBlock(unsigned char* blockData, CBlock& block) {
|
|
{
|
|
std::stringstream ss;
|
|
for (int i = 7; i >= 0; --i)
|
|
ss << std::setw(8) << std::setfill('0') << std::hex << *((int *)(blockData + 4) + i);
|
|
ss.flush();
|
|
block.hashPrevBlock.SetHex(ss.str().c_str());
|
|
}
|
|
{
|
|
std::stringstream ss;
|
|
for (int i = 7; i >= 0; --i)
|
|
ss << std::setw(8) << std::setfill('0') << std::hex << *((int *)(blockData + 36) + i);
|
|
ss.flush();
|
|
block.hashMerkleRoot.SetHex(ss.str().c_str());
|
|
}
|
|
block.nVersion = *((int *)(blockData));
|
|
block.nTime = *((unsigned int *)(blockData + 68));
|
|
block.nBits = *((unsigned int *)(blockData + 72));
|
|
block.nNonce = *((unsigned int *)(blockData + 76));
|
|
block.bnPrimeChainMultiplier = 0;
|
|
}
|
|
|
|
/*********************************
|
|
* class CBlockProviderGW to (incl. SUBMIT_BLOCK)
|
|
*********************************/
|
|
|
|
class CBlockProviderGW : public CBlockProvider {
|
|
public:
|
|
|
|
CBlockProviderGW() : CBlockProvider(), _blocks(NULL) {}
|
|
|
|
virtual ~CBlockProviderGW() { /* TODO */ }
|
|
|
|
virtual CBlock* getBlock(unsigned int thread_id) {
|
|
boost::unique_lock<boost::shared_mutex> lock(_mutex_getwork);
|
|
if (_blocks == NULL) return NULL;
|
|
CBlock* block = NULL;
|
|
block = new CBlock((_blocks+thread_id)->GetBlockHeader());
|
|
block->nTime = GetAdjustedTime();
|
|
//std::cout << "[WORKER" << thread_id << "] got_work block=" << block->GetHash().ToString().c_str() << std::endl;
|
|
return block;
|
|
}
|
|
|
|
void setBlocksFromData(unsigned char* data) {
|
|
CBlock* blocks = new CBlock[thread_num_max];
|
|
for (size_t i = 0; i < thread_num_max; ++i)
|
|
convertDataToBlock(data+i*128,blocks[i]);
|
|
CBlock* old_blocks = NULL;
|
|
{
|
|
boost::unique_lock<boost::shared_mutex> lock(_mutex_getwork);
|
|
old_blocks = _blocks;
|
|
_blocks = blocks;
|
|
}
|
|
if (old_blocks != NULL) delete[] old_blocks;
|
|
}
|
|
|
|
void submitBlock(CBlock *block) {
|
|
blockHeader_t blockraw;
|
|
blockraw.nVersion = block->nVersion;
|
|
blockraw.hashPrevBlock = block->hashPrevBlock;
|
|
blockraw.hashMerkleRoot = block->hashMerkleRoot;
|
|
blockraw.nTime = block->nTime;
|
|
blockraw.nBits = block->nBits;
|
|
blockraw.nNonce = block->nNonce;
|
|
|
|
//std::cout << "submit: " << block->hashMerkleRoot.ToString().c_str() << std::endl;
|
|
|
|
std::vector<unsigned char> primemultiplier = block->bnPrimeChainMultiplier.getvch();
|
|
if (primemultiplier.size() > 47) {
|
|
std::cerr << "[WORKER] share submission warning: not enough space for primemultiplier" << std::endl;
|
|
return;
|
|
}
|
|
blockraw.primemultiplier[0] = primemultiplier.size();
|
|
for (size_t i = 0; i < primemultiplier.size(); ++i)
|
|
blockraw.primemultiplier[1 + i] = primemultiplier[i];
|
|
|
|
boost::system::error_code error;
|
|
++submitting_share;
|
|
while (socket_to_server == NULL && running)
|
|
boost::this_thread::sleep(boost::posix_time::milliseconds(100));
|
|
if (running) {
|
|
socket_to_server->write_some(boost::asio::buffer((unsigned char*)&blockraw, 128));
|
|
if (error)
|
|
std::cout << error << " @ write_some_submit" << std::endl;
|
|
}
|
|
--submitting_share;
|
|
}
|
|
|
|
protected:
|
|
boost::shared_mutex _mutex_getwork;
|
|
CBlock* _blocks;
|
|
};
|
|
|
|
/*********************************
|
|
* multi-threading
|
|
*********************************/
|
|
|
|
class CMasterThreadStub {
|
|
public:
|
|
virtual void wait_for_master() = 0;
|
|
virtual boost::shared_mutex& get_working_lock() = 0;
|
|
};
|
|
|
|
class CWorkerThread { // worker=miner
|
|
public:
|
|
|
|
CWorkerThread(CMasterThreadStub *master, unsigned int id, CBlockProviderGW *bprovider)
|
|
: _working_lock(NULL), _id(id), _master(master), _bprovider(bprovider), _thread(&CWorkerThread::run, this) { }
|
|
|
|
void run() {
|
|
std::cout << "[WORKER" << _id << "] Hello, World!" << std::endl;
|
|
_master->wait_for_master();
|
|
std::cout << "[WORKER" << _id << "] GoGoGo!" << std::endl;
|
|
boost::this_thread::sleep(boost::posix_time::seconds(2));
|
|
BitcoinMiner(NULL, _bprovider, _id);
|
|
std::cout << "[WORKER" << _id << "] Bye Bye!" << std::endl;
|
|
}
|
|
|
|
void work() { // called from within master thread
|
|
_working_lock = new boost::shared_lock<boost::shared_mutex>(_master->get_working_lock());
|
|
}
|
|
|
|
protected:
|
|
boost::shared_lock<boost::shared_mutex> *_working_lock;
|
|
unsigned int _id;
|
|
CMasterThreadStub *_master;
|
|
CBlockProviderGW *_bprovider;
|
|
boost::thread _thread;
|
|
};
|
|
|
|
class CMasterThread : public CMasterThreadStub {
|
|
public:
|
|
|
|
CMasterThread(CBlockProviderGW *bprovider) : CMasterThreadStub(), _bprovider(bprovider) {}
|
|
|
|
void run() {
|
|
|
|
{
|
|
boost::unique_lock<boost::shared_mutex> lock(_mutex_master);
|
|
std::cout << "spawning " << thread_num_max << " worker thread(s)" << std::endl;
|
|
|
|
for (unsigned int i = 0; i < thread_num_max; ++i) {
|
|
CWorkerThread *worker = new CWorkerThread(this, i, _bprovider);
|
|
worker->work();
|
|
}
|
|
}
|
|
|
|
boost::asio::io_service io_service;
|
|
boost::asio::ip::tcp::resolver resolver(io_service); //resolve dns
|
|
boost::asio::ip::tcp::resolver::query query(GetArg("-poolip", "127.0.0.1"), GetArg("-poolport", "1337"));
|
|
boost::asio::ip::tcp::resolver::iterator endpoint;
|
|
boost::asio::ip::tcp::resolver::iterator end;
|
|
boost::asio::ip::tcp::no_delay nd_option(true);
|
|
|
|
while (running) {
|
|
endpoint = resolver.resolve(query);
|
|
boost::scoped_ptr<boost::asio::ip::tcp::socket> socket;
|
|
boost::system::error_code error_socket = boost::asio::error::host_not_found;
|
|
while (error_socket && endpoint != end)
|
|
{
|
|
//socket->close();
|
|
socket.reset(new boost::asio::ip::tcp::socket(io_service));
|
|
boost::asio::ip::tcp::endpoint tcp_ep = *endpoint++;
|
|
socket->connect(tcp_ep, error_socket);
|
|
std::cout << "connecting to " << tcp_ep << std::endl;
|
|
}
|
|
socket->set_option(nd_option);
|
|
|
|
if (error_socket) {
|
|
std::cout << error_socket << std::endl;
|
|
boost::this_thread::sleep(boost::posix_time::seconds(10));
|
|
continue;
|
|
}
|
|
|
|
{ //send hello message
|
|
std::string username = GetArg("-pooluser", "");
|
|
char* hello = new char[username.length()+2];
|
|
memcpy(hello+1, username.c_str(), username.length());
|
|
*((unsigned char*)hello) = username.length();
|
|
*((unsigned char*)hello+username.length()+1) = thread_num_max;
|
|
boost::system::error_code error;
|
|
socket->write_some(boost::asio::buffer(hello, username.length()+2), error);
|
|
if (error)
|
|
std::cout << error << " @ write_some_hello" << std::endl;
|
|
delete[] hello;
|
|
}
|
|
|
|
socket_to_server = socket.get(); //TODO: lock/mutex
|
|
|
|
int reject_counter = 0;
|
|
bool done = false;
|
|
while (!done) {
|
|
int type = -1;
|
|
{ //get the data header
|
|
unsigned char buf = 0; //get header
|
|
boost::system::error_code error;
|
|
size_t len = socket->read_some(boost::asio::buffer(&buf, 1), error);
|
|
if (error == boost::asio::error::eof)
|
|
break; // Connection closed cleanly by peer.
|
|
else if (error) {
|
|
std::cout << error << " @ read_some1" << std::endl;
|
|
break;
|
|
}
|
|
type = buf;
|
|
if (len != 1)
|
|
std::cout << "error on read_some1: " << len << " should be " << 1 << std::endl;
|
|
}
|
|
|
|
switch (type) {
|
|
case 0: {
|
|
size_t buf_size = 128*thread_num_max;
|
|
unsigned char* buf = new unsigned char[buf_size]; //get header
|
|
boost::system::error_code error;
|
|
size_t len = socket->read_some(boost::asio::buffer(buf, buf_size), error);
|
|
while (len < buf_size)
|
|
len += socket->read_some(boost::asio::buffer(buf+len, buf_size-len), error);
|
|
if (error == boost::asio::error::eof) {
|
|
done = true;
|
|
break; // Connection closed cleanly by peer.
|
|
} else if (error) {
|
|
std::cout << error << " @ read_some2a" << std::endl;
|
|
done = true;
|
|
break;
|
|
}
|
|
if (len == buf_size) {
|
|
_bprovider->setBlocksFromData(buf);
|
|
std::cout << "[MASTER] work received" << std::endl;
|
|
} else
|
|
std::cout << "error on read_some2a: " << len << " should be " << buf_size << std::endl;
|
|
delete[] buf;
|
|
CBlockIndex *pindexOld = pindexBest;
|
|
pindexBest = new CBlockIndex(); //=notify worker (this could need a efficient alternative)
|
|
delete pindexOld;
|
|
|
|
} break;
|
|
case 1: {
|
|
size_t buf_size = 4;
|
|
int buf; //get header
|
|
boost::system::error_code error;
|
|
size_t len = socket->read_some(boost::asio::buffer(&buf, buf_size), error);
|
|
while (len < buf_size)
|
|
len += socket->read_some(boost::asio::buffer(&buf+len, buf_size-len), error);
|
|
if (error == boost::asio::error::eof) {
|
|
done = true;
|
|
break; // Connection closed cleanly by peer.
|
|
} else if (error) {
|
|
std::cout << error << " @ read_some2b" << std::endl;
|
|
done = true;
|
|
break;
|
|
}
|
|
if (len == buf_size) {
|
|
int retval = buf > 100000 ? 1 : buf;
|
|
std::cout << "[MASTER] submitted share -> " <<
|
|
(retval == 0 ? "REJECTED" : retval < 0 ? "STALE" : retval ==
|
|
1 ? "BLOCK" : "SHARE") << std::endl;
|
|
std::map<int,unsigned long>::iterator it = statistics.find(retval);
|
|
if (retval > 0)
|
|
reject_counter = 0;
|
|
else
|
|
reject_counter++;
|
|
if (reject_counter >= 4) {
|
|
std::cout << "too many rejects (4), forcing reconnect." << std::endl;
|
|
socket->close();
|
|
done = true;
|
|
}
|
|
if (it == statistics.end())
|
|
statistics.insert(std::pair<int,unsigned long>(retval,1));
|
|
else
|
|
statistics[retval]++;
|
|
} else
|
|
std::cout << "error on read_some2b: " << len << " should be " << buf_size << std::endl;
|
|
} break;
|
|
case 2: {
|
|
//PING-PONG EVENT, nothing to do
|
|
} break;
|
|
default: {
|
|
std::cout << "unknown header type = " << type << std::endl;
|
|
}
|
|
}
|
|
}
|
|
|
|
socket_to_server = NULL; //TODO: lock/mutex
|
|
for (int i = 0; i < 100 && submitting_share < 1; ++i)
|
|
boost::this_thread::sleep(boost::posix_time::milliseconds(100));
|
|
}
|
|
}
|
|
|
|
~CMasterThread() {}
|
|
|
|
void wait_for_master() {
|
|
boost::shared_lock<boost::shared_mutex> lock(_mutex_master);
|
|
}
|
|
|
|
boost::shared_mutex& get_working_lock() {
|
|
return _mutex_working;
|
|
}
|
|
|
|
private:
|
|
|
|
void wait_for_workers() {
|
|
boost::unique_lock<boost::shared_mutex> lock(_mutex_working);
|
|
}
|
|
|
|
CBlockProviderGW *_bprovider;
|
|
|
|
boost::shared_mutex _mutex_master;
|
|
boost::shared_mutex _mutex_working;
|
|
};
|
|
|
|
/*********************************
|
|
* exit / end / shutdown
|
|
*********************************/
|
|
|
|
void stats_on_exit() {
|
|
if (!running) return;
|
|
boost::this_thread::sleep(boost::posix_time::seconds(1));
|
|
std::cout << std::fixed;
|
|
std::cout << std::setprecision(3);
|
|
boost::posix_time::ptime t_end = boost::posix_time::second_clock::universal_time();
|
|
unsigned long rejects = 0;
|
|
unsigned long stale = 0;
|
|
unsigned long valid = 0;
|
|
unsigned long blocks = 0;
|
|
for (std::map<int,unsigned long>::iterator it = statistics.begin(); it != statistics.end(); ++it) {
|
|
if (it->first < 0) stale += it->second;
|
|
if (it->first == 0) rejects = it->second;
|
|
if (it->first == 1) blocks = it->second;
|
|
if (it->first > 1) valid += it->second;
|
|
}
|
|
std::cout << std::endl;
|
|
std::cout << "********************************************" << std::endl;
|
|
std::cout << "*** running time: " << static_cast<double>((t_end - t_start).total_seconds()) / 3600.0 << "hrs" << std::endl;
|
|
std::cout << "***" << std::endl;
|
|
for (std::map<int,unsigned long>::iterator it = statistics.begin(); it != statistics.end(); ++it)
|
|
if (it->first > 1)
|
|
std::cout << "*** " << it->first << "-chains: " << it->second << "\t(" << ((valid+blocks > 0) ? (static_cast<double>(it->second) / static_cast<double>(valid+blocks)) * 100.0 : 0.0) << "%)" << std::endl;
|
|
if (valid+blocks+rejects+stale > 0) {
|
|
std::cout << "***" << std::endl;
|
|
std::cout << "*** valid: " << valid+blocks << "\t(" << (static_cast<double>(valid+blocks) / static_cast<double>(valid+blocks+rejects+stale)) * 100.0 << "%)" << std::endl;
|
|
std::cout << "*** rejects: " << rejects << "\t(" << (static_cast<double>(rejects) / static_cast<double>(valid+blocks+rejects+stale)) * 100.0 << "%)" << std::endl;
|
|
std::cout << "*** stale: " << stale << "\t(" << (static_cast<double>(stale) / static_cast<double>(valid+blocks+rejects+stale)) * 100.0 << "%)" << std::endl;
|
|
} else {
|
|
std::cout << "*** valid: " << 0 << "\t(" << 0.0 << "%)" << std::endl;
|
|
std::cout << "*** rejects: " << 0 << "\t(" << 0.0 << "%)" << std::endl;
|
|
std::cout << "*** stale: " << 0 << "\t(" << 0.0 << "%)" << std::endl;
|
|
}
|
|
std::cout << "********************************************" << std::endl;
|
|
}
|
|
|
|
void exit_handler() {
|
|
//cleanup for not-retarded OS
|
|
if (socket_to_server != NULL) {
|
|
socket_to_server->close();
|
|
socket_to_server = NULL;
|
|
}
|
|
stats_on_exit();
|
|
running = false;
|
|
}
|
|
|
|
#ifdef __MINGW__
|
|
|
|
#define WIN32_LEAN_AND_MEAN
|
|
#include <windows.h>
|
|
|
|
BOOL WINAPI ctrl_handler(DWORD dwCtrlType) {
|
|
//'special' cleanup for windows
|
|
switch(dwCtrlType) {
|
|
case CTRL_C_EVENT:
|
|
case CTRL_BREAK_EVENT: {
|
|
if (socket_to_server != NULL) {
|
|
socket_to_server->close();
|
|
socket_to_server = NULL;
|
|
}
|
|
stats_on_exit();
|
|
running = false;
|
|
} break;
|
|
default: break;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
#else
|
|
#ifdef __GNUG__
|
|
|
|
static sighandler_t set_signal_handler (int signum, sighandler_t signalhandler) {
|
|
struct sigaction new_sig, old_sig;
|
|
new_sig.sa_handler = signalhandler;
|
|
sigemptyset (&new_sig.sa_mask);
|
|
new_sig.sa_flags = SA_RESTART;
|
|
if (sigaction (signum, &new_sig, &old_sig) < 0)
|
|
return SIG_ERR;
|
|
return old_sig.sa_handler;
|
|
}
|
|
|
|
void ctrl_handler(int signum) {
|
|
exit(1);
|
|
}
|
|
|
|
#endif
|
|
#endif //TODO: __APPLE__ ?
|
|
|
|
/*********************************
|
|
* main - this is where it begins
|
|
*********************************/
|
|
int main(int argc, char **argv)
|
|
{
|
|
std::cout << "********************************************" << std::endl;
|
|
std::cout << "*** Primeminer - Primecoin Pool Miner v" << VERSION_MAJOR << "." << VERSION_MINOR << std::endl;
|
|
std::cout << "*** by xolokram/TB - www.beeeeer.org - glhf" << std::endl;
|
|
std::cout << "***" << std::endl;
|
|
std::cout << "*** thx to Sunny King & mikaelh" << std::endl;
|
|
std::cout << "*** press CTRL+C to exit" << std::endl;
|
|
std::cout << "********************************************" << std::endl;
|
|
|
|
t_start = boost::posix_time::second_clock::universal_time();
|
|
running = true;
|
|
|
|
#ifdef __MINGW__
|
|
SetConsoleCtrlHandler(ctrl_handler, TRUE);
|
|
#else
|
|
#ifdef __GNUG__
|
|
set_signal_handler(SIGINT, ctrl_handler);
|
|
#endif
|
|
#endif //TODO: __APPLE__
|
|
|
|
if (argc < 2)
|
|
{
|
|
std::cerr << "usage: " << argv[0] <<
|
|
" -poolip=<ip> -poolport=<port> -pooluser=<user> -poolpassword=<password>" <<
|
|
std::endl;
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
const int atexit_res = std::atexit(exit_handler);
|
|
if (atexit_res != 0)
|
|
std::cerr << "atexit registration failed, shutdown will be dirty!" << std::endl;
|
|
|
|
// init everything:
|
|
ParseParameters(argc, argv);
|
|
|
|
socket_to_server = NULL;
|
|
thread_num_max = GetArg("-genproclimit", 1); // what about boost's
|
|
// hardware_concurrency() ?
|
|
fPrintToConsole = true; // always on
|
|
fDebug = GetBoolArg("-debug");
|
|
|
|
pindexBest = new CBlockIndex();
|
|
|
|
GeneratePrimeTable();
|
|
|
|
// ok, start mining:
|
|
CBlockProviderGW* bprovider = new CBlockProviderGW();
|
|
CMasterThread *mt = new CMasterThread(bprovider);
|
|
mt->run();
|
|
|
|
// end:
|
|
return EXIT_SUCCESS;
|
|
}
|
|
|
|
/*********************************
|
|
* and this is where it ends
|
|
*********************************/
|