diff --git a/src/server/poolserver/Server/Server.cpp b/src/server/poolserver/Server/Server.cpp index 816b865..d006f56 100644 --- a/src/server/poolserver/Server/Server.cpp +++ b/src/server/poolserver/Server/Server.cpp @@ -81,11 +81,16 @@ int Server::Run() Stratum::Server srv(io_service); // Init Bitcoin RPC - JSONRPCConnectionInfo coninfo; + /*JSONRPCConnectionInfo coninfo; coninfo.Host = "84.240.15.208"; coninfo.Port = "8332"; coninfo.User = "user"; - coninfo.Pass = "DYAL6bC4RUHksL6ikdx7"; + coninfo.Pass = "DYAL6bC4RUHksL6ikdx7";*/ + JSONRPCConnectionInfo coninfo; + coninfo.Host = "127.0.0.1"; + coninfo.Port = "19001"; + coninfo.User = "test"; + coninfo.Pass = "123"; JSONRPC* bitcoinrpc = new JSONRPC(); bitcoinrpc->Connect(coninfo); diff --git a/src/server/poolserver/Stratum/Client.cpp b/src/server/poolserver/Stratum/Client.cpp index 0dd24c4..8159adc 100644 --- a/src/server/poolserver/Stratum/Client.cpp +++ b/src/server/poolserver/Stratum/Client.cpp @@ -53,7 +53,6 @@ namespace Stratum void Client::OnMiningSubmit(JSON msg) { - //msg = JSON::FromString("{\"params\": [\"slush.miner1\", \"00000000\", \"00000001\", \"504e86ed\", \"b2957c02\"], \"id\": 4, \"method\": \"mining.submit\"}"); JSON params = msg["params"]; std::string username = params[0].GetString(); @@ -102,55 +101,61 @@ namespace Stratum return; } - // Get block we are working on + // Copy block we are working on Bitcoin::Block block = *job.block; + // Start assembling the block timebuf >> block.time; noncebuf >> block.nonce; + // Assemble coinbase Bitcoin::Transaction coinbasetx; ByteBuffer coinbasebuf; coinbasebuf << job.coinbase1 << _extranonce << extranonce2 << job.coinbase2; coinbasebuf >> coinbasetx; + // Set coinbase tx block.tx[0] = coinbasetx; - ByteBuffer test; - test << coinbasetx; - sLog.Info(LOG_SERVER, "Coinbase: %s", Util::BinToASCII(test.Binary()).c_str()); sLog.Info(LOG_SERVER, "Coinbase hash1: %s", Util::BinToASCII(Crypto::SHA256D(coinbasebuf.Binary())).c_str()); sLog.Info(LOG_SERVER, "Coinbase hash2: %s", Util::BinToASCII(coinbasetx.GetHash()).c_str()); + // Rebuilds only left side of merkle tree block.RebuildMerkleTree(); sLog.Info(LOG_SERVER, "Merklehash: %s", Util::BinToASCII(block.merkleRootHash).c_str()); + + // Get block hash BinaryData hash = block.GetHash(); sLog.Info(LOG_SERVER, "Block hash: %s", Util::BinToASCII(hash).c_str()); + // Get block target BigInt target(Util::BinToASCII(Util::Reverse(hash)), 16); + + // Network target criteria BigInt criteria(Bitcoin::TargetFromBits(block.bits)); - BigInt diff = Bitcoin::TargetConvert(criteria); + + BigInt diff = Bitcoin::TargetToDiff(criteria); std::cout << "Target: " << target << std::endl; std::cout << "Criteria: " << criteria << std::endl; std::cout << "Diff: " << diff << std::endl; + // Check if block meets criteria if (target <= criteria) { sLog.Info(LOG_SERVER, "We have found a block candidate!"); - // Serialize block - ByteBuffer blockbuf; - blockbuf << block; - - JSON params; - params.Add(Util::BinToASCII(blockbuf.Binary())); - JSON response = _bitcoinrpc->Query("submitblock", params); + _server->SubmitBlock(block); } } void Client::OnMiningSubscribe(JSON msg) { _subscribed = true; + + // Get extranonce from server _extranonce = _server->GetExtranonce(); + ByteBuffer noncebuf; + noncebuf << _extranonce; JSON notify; notify.Add("mining.notify"); @@ -158,8 +163,6 @@ namespace Stratum JSON result; result.Add(notify); - ByteBuffer noncebuf; - noncebuf << _extranonce; result.Add(Util::BinToASCII(noncebuf.Binary())); result.Add(int64(4)); @@ -170,14 +173,14 @@ namespace Stratum SendMessage(response); - SendJob(true); + SendJob(false); } void Client::OnMiningAuthorize(JSON msg) { - sLog.Info(LOG_SERVER, "Test: %s", msg["params"].ToString().c_str()); std::string username = msg["params"][0].GetString(); std::string password = msg["params"][1].GetString(); + JSON response; response["id"] = msg["id"].GetInt(); response["error"]; @@ -190,15 +193,51 @@ namespace Stratum Job job; job.block = _server->GetWork(); - // Coinbase parts + // Serialize transaction ByteBuffer coinbasebuf; coinbasebuf << job.block->tx[0]; BinaryData coinbase = coinbasebuf.Binary(); - // tx.version + tx.in[0].outpoint.hash + tx.in[0].outpoint.n + tx.script (size) + tx.script (block height) + + // Split coinbase + // tx.version - 4 bytes + // tx.in[0].outpoint.hash - 32 bytes + // tx.in[0].outpoint.n - 4 bytes + // tx.script (varint) - 2 bytes + // tx.script (block height) - 4 bytes uint32 cbsplit = 4 + 32 + 4 + 2 + 4; job.coinbase1 = BinaryData(coinbase.begin(), coinbase.begin() + cbsplit); job.coinbase2 = BinaryData(coinbase.begin() + cbsplit + 8, coinbase.end()); // plus extranonce size return job; } + + void Client::_OnReceive(const boost::system::error_code& error, size_t bytes_transferred) + { + if (!error) { + std::istream is(&_recvBuffer); + std::stringstream iss; + iss << is.rdbuf(); + sLog.Debug(LOG_SERVER, "Received: %s", iss.str().c_str()); + OnMessage(JSON::FromString(iss.str())); + + StartRead(); + } else { + // Client disconnected + if ((error == asio::error::eof) || (error == asio::error::connection_reset)) { + _server->Disconnect(shared_from_this()); + } + } + } + + void Client::_OnSend(const boost::system::error_code& error) + { + if (!error) { + // Party + } else { + // Client disconnected + if ((error == asio::error::eof) || (error == asio::error::connection_reset)) { + _server->Disconnect(shared_from_this()); + } + } + } } diff --git a/src/server/poolserver/Stratum/Client.h b/src/server/poolserver/Stratum/Client.h index f760c4f..f6b8027 100644 --- a/src/server/poolserver/Stratum/Client.h +++ b/src/server/poolserver/Stratum/Client.h @@ -9,6 +9,8 @@ #include #include +#include +#include #define MAX_PACKET 2048 @@ -19,7 +21,7 @@ namespace Stratum { class Server; - class Client + class Client : public boost::enable_shared_from_this { public: Client(Server* server, asio::io_service& io_service) : _server(server), _socket(io_service), _subscribed(false), _jobid(0) @@ -33,11 +35,13 @@ namespace Stratum void Start() { + // Start reading socket StartRead(); } void StartRead() { + // Read until newline boost::asio::async_read_until( _socket, _recvBuffer, @@ -77,25 +81,17 @@ namespace Stratum sLog.Error(LOG_SERVER, "Method '%s' not found.", method.c_str()); } + // Gets new job from the server Job GetJob(); - public: - void _OnReceive(const boost::system::error_code& error, size_t bytes_transferred) - { - std::istream is(&_recvBuffer); - std::stringstream iss; - iss << is.rdbuf(); - sLog.Debug(LOG_SERVER, "Received: %s", iss.str().c_str()); - OnMessage(JSON::FromString(iss.str())); - - StartRead(); - } - void _OnSend(const boost::system::error_code& error) + + void Disconnect() { - if (error) - sLog.Error(LOG_SERVER, "Failed to send data"); - else - sLog.Debug(LOG_SERVER, "Data sent"); + _socket.close(); } + public: + void _OnReceive(const boost::system::error_code& error, size_t bytes_transferred); + void _OnSend(const boost::system::error_code& error); + private: // Networking asio::streambuf _recvBuffer; @@ -113,6 +109,8 @@ namespace Stratum std::map _jobs; uint32 _jobid; }; + + typedef boost::shared_ptr ClientPtr; } #endif diff --git a/src/server/poolserver/Stratum/Job.h b/src/server/poolserver/Stratum/Job.h index c636cd2..6aaff78 100644 --- a/src/server/poolserver/Stratum/Job.h +++ b/src/server/poolserver/Stratum/Job.h @@ -16,6 +16,8 @@ namespace Stratum BinaryData coinbase2; std::set shares; + // Submits share to a job + // Returns false if the same share already exists bool SubmitShare(BinaryData share) { std::string sharestr = Util::BinToASCII(Crypto::SHA256(share)); diff --git a/src/server/poolserver/Stratum/Server.cpp b/src/server/poolserver/Stratum/Server.cpp new file mode 100644 index 0000000..f5f2f87 --- /dev/null +++ b/src/server/poolserver/Stratum/Server.cpp @@ -0,0 +1,56 @@ +#include "Server.h" + +namespace Stratum +{ + void Server::Start(tcp::endpoint endpoint) + { + // Start block checking timer + _CheckBlocksTimer(); + + _acceptor.open(endpoint.protocol()); + _acceptor.set_option(tcp::acceptor::reuse_address(true)); + _acceptor.bind(endpoint); + _acceptor.listen(); + + _StartAccept(); + + sLog.Debug(LOG_STRATUM, "Stratum server started"); + } + + void Server::SendToAll(JSON msg) + { + std::set::iterator it; + for (it = _clients.begin(); it != _clients.end(); ++it) + _io_service.post(boost::bind(&Client::SendMessage, (*it), msg)); + } + + void Server::ResetWork() + { + std::set::iterator it; + for (it = _clients.begin(); it != _clients.end(); ++it) + _io_service.post(boost::bind(&Client::SendJob, (*it), true)); + } + + bool Server::SubmitBlock(Bitcoin::Block block) + { + // Serialize block + ByteBuffer blockbuf; + blockbuf << block; + + try { + JSON params; + params.Add(Util::BinToASCII(blockbuf.Binary())); + JSON response = _bitcoinrpc->Query("submitblock", params); + + if (response["result"].GetType() == JSON_NULL) { + sLog.Info(LOG_STRATUM, "Block accepted! YAY!"); + _io_service.post(boost::bind(&Server::_CheckBlocks, this)); + return true; + } else { + sLog.Info(LOG_STRATUM, "Block rejected! Booooo"); + } + } catch (...) { + return false; + } + } +} diff --git a/src/server/poolserver/Stratum/Server.h b/src/server/poolserver/Stratum/Server.h index 6a284f2..32a93fe 100644 --- a/src/server/poolserver/Stratum/Server.h +++ b/src/server/poolserver/Stratum/Server.h @@ -13,7 +13,8 @@ #include #include -#include +#include +#include using namespace boost; using namespace boost::asio::ip; @@ -36,66 +37,64 @@ namespace Stratum ~Server() { - std::list::iterator it; + std::set::iterator it; for (it = _clients.begin(); it != _clients.end(); ++it) - delete *it; + (*it)->Disconnect(); } - void Start(tcp::endpoint endpoint) - { - _CheckBlocks(); - - _acceptor.open(endpoint.protocol()); - _acceptor.set_option(tcp::acceptor::reuse_address(true)); - _acceptor.bind(endpoint); - _acceptor.listen(); - - _StartAccept(); - - sLog.Debug(LOG_STRATUM, "Stratum server started"); - } + // Starts accepting connections + void Start(tcp::endpoint endpoint); void SetBitcoinRPC(JSONRPC* bitcoinrpc) { _bitcoinrpc = bitcoinrpc; } - void SendToAll(JSON msg) - { - std::list::iterator it; - for (it = _clients.begin(); it != _clients.end(); ++it) - (*it)->SendMessage(msg); - } + // Sends message to all clients + void SendToAll(JSON msg); + // Returns new extranonce uint32 GetExtranonce() { - //return 134217730; - return 33554440; - //return _extranonce++; + return _extranonce++; } + // Returns current work Bitcoin::BlockPtr GetWork() { + boost::lock_guard guard(_mtx_workupdate); return _currentWork; } + // Resets work for all clients + void ResetWork(); + + // Submits block to bitcoind + bool SubmitBlock(Bitcoin::Block block); + + // Disconnects client + void Disconnect(ClientPtr client) + { + client->Disconnect(); + _clients.erase(client); + } + private: void _StartAccept() { - Client* client = new Client(this, _io_service); + ClientPtr client = ClientPtr(new Client(this, _io_service)); _acceptor.async_accept(client->GetSocket(), boost::bind(&Server::_OnAccept, this, client, asio::placeholders::error)); } - void _OnAccept(Client* client, const boost::system::error_code& error) + void _OnAccept(ClientPtr client, const boost::system::error_code& error) { if (!error) { sLog.Debug(LOG_STRATUM, "New stratum client accepted"); client->Start(); - _clients.push_back(client); + _clients.insert(client); } else { sLog.Debug(LOG_STRATUM, "Failed to accept stratum client"); - delete client; } _StartAccept(); @@ -118,30 +117,40 @@ namespace Stratum block->tx.push_back(CreateCoinbaseTX(_blockHeight, _pubkey, response["coinbasevalue"].GetInt())); // Add other transactions - /*JSON trans = response["transactions"]; + JSON trans = response["transactions"]; for (uint64 i = 0; i < trans.Size(); ++i) { ByteBuffer txbuf(Util::ASCIIToBin(trans[i]["data"].GetString())); Bitcoin::Transaction tx; txbuf >> tx; block->tx.push_back(tx); - }*/ - /*Bitcoin::BlockPtr block = Bitcoin::BlockPtr(new Bitcoin::Block()); - block->version = 2; - block->prevBlockHash = Util::Reverse(Util::ASCIIToBin("00000000440b921e1b77c6c0487ae5616de67f788f44ae2a5af6e2194d16b6f8")); - block->time = 1347323629; - block->bits = 472564911; - block->tx.push_back(CreateCoinbaseTX(25096, _pubkey, 50));*/ + } // Genrate merkle tree block->BuildMerkleTree(); // Set current work + _mtx_workupdate.lock(); _currentWork = block; + _mtx_workupdate.unlock(); + + // Requests all clients to reset work + if (reset) + ResetWork(); + } + + void _CheckBlocksTimer() + { + _CheckBlocks(); + _blockCheckTimer.expires_from_now(boost::posix_time::milliseconds(sConfig.Get("StratumBlockCheckTime"))); + _blockCheckTimer.async_wait(boost::bind(&Server::_CheckBlocksTimer, this)); } void _CheckBlocks() { - sLog.Debug(LOG_STRATUM, "Checking for new blocks..."); + // Might be called twice from timer and when block is found + boost::lock_guard guard(_mtx_checkblock); + + sLog.Debug(LOG_STRATUM, "Clients: %u", _clients.size()); JSON response = _bitcoinrpc->Query("getinfo"); uint32 curBlock = response["blocks"].GetInt(); @@ -151,9 +160,6 @@ namespace Stratum _blockHeight = curBlock; _UpdateWork(true); } - - _blockCheckTimer.expires_from_now(boost::posix_time::milliseconds(sConfig.Get("StratumBlockCheckTime"))); - _blockCheckTimer.async_wait(boost::bind(&Server::_CheckBlocks, this)); } Bitcoin::Transaction CreateCoinbaseTX(uint32 blockHeight, BinaryData pubkey, int64 value) @@ -186,10 +192,14 @@ namespace Stratum } private: // Network - std::list _clients; + std::set _clients; asio::io_service& _io_service; tcp::acceptor _acceptor; + // Mutexes + boost::mutex _mtx_checkblock; + boost::mutex _mtx_workupdate; + // RPC JSONRPC* _bitcoinrpc; diff --git a/src/server/shared/Bitcoin/Bitcoin.h b/src/server/shared/Bitcoin/Bitcoin.h index 152dd98..3909ee8 100644 --- a/src/server/shared/Bitcoin/Bitcoin.h +++ b/src/server/shared/Bitcoin/Bitcoin.h @@ -10,12 +10,18 @@ namespace Bitcoin { - inline BigInt TargetConvert(BigInt val) + inline BigInt TargetToDiff(BigInt val) { static BigInt c("0x00000000ffff0000000000000000000000000000000000000000000000000000"); return (c / val); } + inline BigInt DiffToTarget(BigInt val) + { + // Algebra says that it is the same! + return TargetToDiff(val); + } + inline BigInt TargetFromBits(uint32 bits) { uint8 nbytes = (bits >> 24) & 0xFF;