diff --git a/src/server/poolserver/Server/Server.cpp b/src/server/poolserver/Server/Server.cpp index 6393730..816b865 100644 --- a/src/server/poolserver/Server/Server.cpp +++ b/src/server/poolserver/Server/Server.cpp @@ -35,7 +35,8 @@ int Server::Run() //InitDatabase(); - JSON node = JSON::FromString("{\"test\":{\"omg\":\"smth\"},\"other\":\"smth2\"}"); + JSON node = JSON::FromString("{\"test\":{\"omg\":\"smth\"},\"other\":[\"smth2\", \"smth3\"] }"); + sLog.Info(LOG_SERVER, "Something2: %s", node["other"][0].GetString().c_str()); sLog.Info(LOG_SERVER, "Something: %s", node.ToString().c_str()); /*std::vector test = Util::ASCIIToBin("4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b"); diff --git a/src/server/poolserver/Stratum/Client.cpp b/src/server/poolserver/Stratum/Client.cpp index 2b2c2f9..0dd24c4 100644 --- a/src/server/poolserver/Stratum/Client.cpp +++ b/src/server/poolserver/Stratum/Client.cpp @@ -1,5 +1,7 @@ #include "Server.h" #include "Client.h" +#include "BigNum.h" +#include namespace Stratum { @@ -9,19 +11,10 @@ namespace Stratum _jobs.clear(); Job job = GetJob(); - uint64 jobid = _jobid++; + uint32 jobid = _jobid++; _jobs[jobid] = job; - // Coinbase parts - 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 (block height) - uint32 cbsplit = 4 + 32 + 4 + 4; - BinaryData coinbase1 = BinaryData(coinbase.begin(), coinbase.begin() + cbsplit); - BinaryData coinbase2 = BinaryData(coinbase.begin() + cbsplit + 8, coinbase.end()); // plus extranonce size - // Build merkle branch array JSON merkle_branch(JSON_ARRAY); uint64 branches = job.block->merkleBranches; @@ -32,11 +25,18 @@ namespace Stratum branches /= 2; } + // Reverse prev block hash every 4 bytes... Makes a lot of sense... + ByteBuffer prevhashbuf(Util::Reverse(job.block->prevBlockHash)); + std::vector prevhash(8); + prevhashbuf >> prevhash[7] >> prevhash[6] >> prevhash[5] >> prevhash[4] >> prevhash[3] >> prevhash[2] >> prevhash[1] >> prevhash[0]; + ByteBuffer prevhashfixed; + prevhashfixed << prevhash[0] << prevhash[1] << prevhash[2] << prevhash[3] << prevhash[4] << prevhash[5] << prevhash[6] << prevhash[7]; + JSON params; - params.Add("bf"); - params.Add(Util::BinToASCII(Util::Reverse(job.block->prevBlockHash))); - params.Add(Util::BinToASCII(coinbase1)); - params.Add(Util::BinToASCII(coinbase2)); + params.Add(Util::BinToASCII(ByteBuffer(jobid).Binary())); + params.Add(Util::BinToASCII(prevhashfixed.Binary())); + params.Add(Util::BinToASCII(job.coinbase1)); + params.Add(Util::BinToASCII(job.coinbase2)); params.Add(merkle_branch); params.Add(Util::BinToASCII(Util::Reverse(ByteBuffer(job.block->version).Binary()))); params.Add(Util::BinToASCII(Util::Reverse(ByteBuffer(job.block->bits).Binary()))); @@ -51,6 +51,102 @@ namespace Stratum SendMessage(msg); } + 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(); + uint32 jobid; + ByteBuffer jobbuf(Util::ASCIIToBin(params[1].GetString())); + jobbuf >> jobid; + + if (!_jobs.count(jobid)) { + JSON response; + response["id"] = msg["id"]; + response["result"]; + response["error"].Add(int64(21)); + response["error"].Add("Job not found"); + response["error"].Add(JSON()); + SendMessage(response); + return; + } + + // check username + + BinaryData extranonce2 = Util::ASCIIToBin(params[2].GetString()); + if (extranonce2.size() != 4) { + sLog.Error(LOG_SERVER, "Wrong extranonce size"); + return; + } + + ByteBuffer timebuf(Util::Reverse(Util::ASCIIToBin(params[3].GetString()))); + if (timebuf.Size() != 4) { + sLog.Error(LOG_SERVER, "Wrong ntime size"); + return; + } + + ByteBuffer noncebuf(Util::Reverse(Util::ASCIIToBin(params[4].GetString()))); + if (noncebuf.Size() != 4) { + sLog.Error(LOG_SERVER, "Wrong nonce size"); + return; + } + + // Get job + Job& job = _jobs[jobid]; + + ByteBuffer share; + share << extranonce2 << timebuf << noncebuf; + if (!job.SubmitShare(share.Binary())) { + sLog.Error(LOG_SERVER, "Duplicate share"); + return; + } + + // Get block we are working on + Bitcoin::Block block = *job.block; + + timebuf >> block.time; + noncebuf >> block.nonce; + + Bitcoin::Transaction coinbasetx; + ByteBuffer coinbasebuf; + coinbasebuf << job.coinbase1 << _extranonce << extranonce2 << job.coinbase2; + coinbasebuf >> coinbasetx; + + 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()); + + block.RebuildMerkleTree(); + sLog.Info(LOG_SERVER, "Merklehash: %s", Util::BinToASCII(block.merkleRootHash).c_str()); + BinaryData hash = block.GetHash(); + + sLog.Info(LOG_SERVER, "Block hash: %s", Util::BinToASCII(hash).c_str()); + + BigInt target(Util::BinToASCII(Util::Reverse(hash)), 16); + BigInt criteria(Bitcoin::TargetFromBits(block.bits)); + BigInt diff = Bitcoin::TargetConvert(criteria); + std::cout << "Target: " << target << std::endl; + std::cout << "Criteria: " << criteria << std::endl; + std::cout << "Diff: " << diff << std::endl; + + 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); + } + } + void Client::OnMiningSubscribe(JSON msg) { _subscribed = true; @@ -74,13 +170,35 @@ namespace Stratum SendMessage(response); - SendJob(false); + SendJob(true); + } + + 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"]; + response["result"] = true; + SendMessage(response); } Job Client::GetJob() { Job job; job.block = _server->GetWork(); + + // Coinbase parts + 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) + 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; } } diff --git a/src/server/poolserver/Stratum/Client.h b/src/server/poolserver/Stratum/Client.h index 7604be0..f760c4f 100644 --- a/src/server/poolserver/Stratum/Client.h +++ b/src/server/poolserver/Stratum/Client.h @@ -32,6 +32,11 @@ namespace Stratum } void Start() + { + StartRead(); + } + + void StartRead() { boost::asio::async_read_until( _socket, @@ -47,27 +52,24 @@ namespace Stratum std::string data = msg.ToString(); data += '\n'; sLog.Debug(LOG_SERVER, "Sending: %s", data.c_str()); - _socket.send(boost::asio::buffer(data.c_str(), data.length())); + boost::asio::async_write( + _socket, + boost::asio::buffer(data.c_str(), data.length()), + boost::bind(&Client::_OnSend, this, boost::asio::placeholders::error)); } + void OnMiningSubmit(JSON msg); void OnMiningSubscribe(JSON msg); - - void OnMiningAuthorize(JSON msg) - { - std::string username = msg["result"][0].GetString(); - JSON response; - response["id"] = msg["id"].GetInt(); - response["error"]; - response["result"] = true; - SendMessage(response); - } + void OnMiningAuthorize(JSON msg); void OnMessage(JSON msg) { std::string method = msg["method"].GetString(); - sLog.Debug(LOG_SERVER, "Method: %s", method.c_str()); + sLog.Debug(LOG_SERVER, "Decoded: %s", msg.ToString().c_str()); - if (method.compare("mining.subscribe") == 0) + if (method.compare("mining.submit") == 0) + OnMiningSubmit(msg); + else if (method.compare("mining.subscribe") == 0) OnMiningSubscribe(msg); else if (method.compare("mining.authorize") == 0) OnMiningAuthorize(msg); @@ -84,6 +86,15 @@ namespace Stratum 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) + { + if (error) + sLog.Error(LOG_SERVER, "Failed to send data"); + else + sLog.Debug(LOG_SERVER, "Data sent"); } private: // Networking @@ -100,7 +111,7 @@ namespace Stratum bool _subscribed; uint32 _extranonce; std::map _jobs; - uint64 _jobid; + uint32 _jobid; }; } diff --git a/src/server/poolserver/Stratum/Job.h b/src/server/poolserver/Stratum/Job.h index 93938e9..c636cd2 100644 --- a/src/server/poolserver/Stratum/Job.h +++ b/src/server/poolserver/Stratum/Job.h @@ -2,6 +2,9 @@ #define STRATUM_JOB_H_ #include "Bitcoin.h" +#include "Crypto.h" +#include "Util.h" +#include namespace Stratum { @@ -9,6 +12,20 @@ namespace Stratum { public: Bitcoin::BlockPtr block; + BinaryData coinbase1; + BinaryData coinbase2; + std::set shares; + + bool SubmitShare(BinaryData share) + { + std::string sharestr = Util::BinToASCII(Crypto::SHA256(share)); + std::set::iterator it = shares.find(sharestr); + if (it == shares.end()) { + shares.insert(sharestr); + return true; + } else + return false; + } }; } diff --git a/src/server/poolserver/Stratum/Server.h b/src/server/poolserver/Stratum/Server.h index e50075d..6a284f2 100644 --- a/src/server/poolserver/Stratum/Server.h +++ b/src/server/poolserver/Stratum/Server.h @@ -69,7 +69,9 @@ namespace Stratum uint32 GetExtranonce() { - return _extranonce++; + //return 134217730; + return 33554440; + //return _extranonce++; } Bitcoin::BlockPtr GetWork() @@ -106,23 +108,29 @@ namespace Stratum Bitcoin::BlockPtr block = Bitcoin::BlockPtr(new Bitcoin::Block()); block->version = response["version"].GetInt(); - block->prevBlockHash = Util::ASCIIToBin(response["previousblockhash"].GetString()); + block->prevBlockHash = Util::Reverse(Util::ASCIIToBin(response["previousblockhash"].GetString())); block->time = response["curtime"].GetInt(); // Set bits - ByteBuffer bitbuf(Util::ASCIIToBin(response["bits"].GetString())); + ByteBuffer bitbuf(Util::Reverse(Util::ASCIIToBin(response["bits"].GetString()))); bitbuf >> block->bits; // Add coinbase tx 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(); diff --git a/src/server/shared/BigNum.h b/src/server/shared/BigNum.h index befc461..2bc4218 100644 --- a/src/server/shared/BigNum.h +++ b/src/server/shared/BigNum.h @@ -3,6 +3,6 @@ #include -typedef mpz_class BigNum; +typedef mpz_class BigInt; #endif diff --git a/src/server/shared/Bitcoin/Bitcoin.h b/src/server/shared/Bitcoin/Bitcoin.h index 721e52c..152dd98 100644 --- a/src/server/shared/Bitcoin/Bitcoin.h +++ b/src/server/shared/Bitcoin/Bitcoin.h @@ -5,5 +5,25 @@ #include "Transaction.h" #include "VarInt.h" #include "Script.h" +#include "BigNum.h" +#include + +namespace Bitcoin +{ + inline BigInt TargetConvert(BigInt val) + { + static BigInt c("0x00000000ffff0000000000000000000000000000000000000000000000000000"); + return (c / val); + } + + inline BigInt TargetFromBits(uint32 bits) + { + uint8 nbytes = (bits >> 24) & 0xFF; + mpz_t power; + mpz_init(power); + mpz_ui_pow_ui(power, 2, 8 * (nbytes - 3)); + return BigInt(bits & 0xFFFFFF) * BigInt(power); + } +} #endif diff --git a/src/server/shared/Bitcoin/Block.cpp b/src/server/shared/Bitcoin/Block.cpp new file mode 100644 index 0000000..1f0c482 --- /dev/null +++ b/src/server/shared/Bitcoin/Block.cpp @@ -0,0 +1,65 @@ +#include "Block.h" + +namespace Bitcoin +{ + void Block::BuildMerkleTree() + { + merkleTree.clear(); + + uint64 branches = 1; + uint32 levels = 0; + while (branches < tx.size()) { + branches *= 2; + ++levels; + } + + // Used when sending merkle branches + merkleBranches = branches; + + // Add transactions + for (uint64 i = 0; i < branches; ++i) + merkleTree.push_back(tx[std::min(i, tx.size()-1)].GetHash()); + + uint32 merkleIndex = 0; + for (uint32 level = levels; level > 0; --level) + { + // Decrease before calculating because bottom level is transactions + branches /= 2; + + for (uint32 branch = 0; branch < branches; ++branch) + merkleTree.push_back(Crypto::SHA256D(Util::Join(merkleTree[merkleIndex++], merkleTree[merkleIndex++]))); + } + + // Last hash is merkle root + merkleRootHash = merkleTree[merkleTree.size()-1]; + } + + void Block::RebuildMerkleTree() + { + // Set coinbase tx hash + merkleTree[0] = tx[0].GetHash(); + + uint64 branches = merkleBranches; + uint64 index = 0; + while (branches > 1) { + merkleTree[index+branches] = Crypto::SHA256D(Util::Join(merkleTree[index], merkleTree[index+1])); + index += branches; + branches /= 2; + } + + // Last hash is merkle root + merkleRootHash = merkleTree[merkleTree.size()-1]; + } + + BinaryData Block::GetHash() + { + ByteBuffer buf; + buf << version; + buf << prevBlockHash; + buf << merkleRootHash; + buf << time; + buf << bits; + buf << nonce; + return Crypto::SHA256D(buf.Binary()); + } +} diff --git a/src/server/shared/Bitcoin/Block.h b/src/server/shared/Bitcoin/Block.h index 0cdc6e6..084c181 100644 --- a/src/server/shared/Bitcoin/Block.h +++ b/src/server/shared/Bitcoin/Block.h @@ -30,37 +30,10 @@ namespace Bitcoin std::vector merkleTree; uint64 merkleBranches; - void BuildMerkleTree() - { - merkleTree.clear(); - - uint64 branches = 1; - uint32 levels = 0; - while (branches < tx.size()) { - branches *= 2; - ++levels; - } - - // Used when sending merkle branches - merkleBranches = branches; - - // Add transactions - for (uint64 i = 0; i < branches; ++i) - merkleTree.push_back(tx[std::min(i, tx.size()-1)].GetHash()); - - uint32 merkleIndex = 0; - for (uint32 level = levels; level > 0; --level) - { - // Decrease before calculating because bottom level is transactions - branches /= 2; - - for (uint32 branch = 0; branch < branches; ++branch) - merkleTree.push_back(Crypto::SHA256D(Util::Join(merkleTree[merkleIndex++], merkleTree[merkleIndex++]))); - } - - // Last hash is merkle root - merkleRootHash = merkleTree[merkleTree.size()-1]; - } + void BuildMerkleTree(); + // Rebuilds only left side of merkle tree + void RebuildMerkleTree(); + BinaryData GetHash(); }; typedef boost::shared_ptr BlockPtr; diff --git a/src/server/shared/ByteBuffer.h b/src/server/shared/ByteBuffer.h index e57965f..fb067c5 100644 --- a/src/server/shared/ByteBuffer.h +++ b/src/server/shared/ByteBuffer.h @@ -85,6 +85,11 @@ public: return vec; } + uint64 Size() + { + return vec.size(); + } + uint64 pointer; BinaryData vec; };