mirror of
https://github.com/Xahau/xahaud.git
synced 2025-12-06 17:27:52 +00:00
easy and then has unlimited use of our system for several minutes because they can keep turning in the easy proofs of work, causing us to keep raising the proof of work level to insane levels.
344 lines
10 KiB
C++
344 lines
10 KiB
C++
#include "ProofOfWork.h"
|
|
|
|
#include <string>
|
|
|
|
#include <boost/test/unit_test.hpp>
|
|
#include <boost/algorithm/string.hpp>
|
|
#include <boost/format.hpp>
|
|
|
|
#include <openssl/rand.h>
|
|
|
|
#include "Serializer.h"
|
|
#include "Log.h"
|
|
|
|
SETUP_LOG();
|
|
|
|
const uint256 ProofOfWork::sMinTarget("00000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF");
|
|
const int ProofOfWork::sMaxIterations(1 << 23);
|
|
|
|
bool ProofOfWork::isValid() const
|
|
{
|
|
if ((mIterations <= sMaxIterations) && (mTarget >= sMinTarget))
|
|
return true;
|
|
cLog(lsWARNING) << "Invalid PoW: " << mIterations << ", " << mTarget;
|
|
return false;
|
|
}
|
|
|
|
uint64 ProofOfWork::getDifficulty(const uint256& target, int iterations)
|
|
{ // calculate the approximate number of hashes required to solve this proof of work
|
|
if ((iterations > sMaxIterations) || (target < sMinTarget))
|
|
{
|
|
cLog(lsINFO) << "Iterations:" << iterations;
|
|
cLog(lsINFO) << "MaxIterat: " << sMaxIterations;
|
|
cLog(lsINFO) << "Target: " << target;
|
|
cLog(lsINFO) << "MinTarget: " << sMinTarget;
|
|
throw std::runtime_error("invalid proof of work target/iteration");
|
|
}
|
|
|
|
// more iterations means more hashes per iteration but also a larger final hash
|
|
uint64 difficulty = iterations + (iterations / 8);
|
|
|
|
// Multiply the number of hashes needed by 256 for each leading zero byte in the difficulty
|
|
const unsigned char *ptr = target.begin();
|
|
while (*ptr == 0)
|
|
{
|
|
difficulty *= 256;
|
|
++ptr;
|
|
}
|
|
difficulty = (difficulty * 256) / (*ptr + 1);
|
|
|
|
return difficulty;
|
|
}
|
|
|
|
static uint256 getSHA512Half(const std::vector<uint256>& vec)
|
|
{
|
|
return Serializer::getSHA512Half(vec.front().begin(), vec.size() * (256 / 8));
|
|
}
|
|
|
|
uint256 ProofOfWork::solve(int maxIterations) const
|
|
{
|
|
if (!isValid())
|
|
throw std::runtime_error("invalid proof of work target/iteration");
|
|
|
|
uint256 nonce;
|
|
RAND_bytes(nonce.begin(), nonce.size());
|
|
|
|
std::vector<uint256> buf2;
|
|
buf2.resize(mIterations);
|
|
|
|
std::vector<uint256> buf1;
|
|
buf1.resize(3);
|
|
buf1[0] = mChallenge;
|
|
|
|
while (maxIterations > 0)
|
|
{
|
|
buf1[1] = nonce;
|
|
buf1[2].zero();
|
|
for (int i = (mIterations - 1); i >= 0; --i)
|
|
{
|
|
buf1[2] = getSHA512Half(buf1);
|
|
buf2[i] = buf1[2];
|
|
}
|
|
|
|
if (getSHA512Half(buf2) <= mTarget)
|
|
return nonce;
|
|
|
|
++nonce;
|
|
--maxIterations;
|
|
}
|
|
return uint256();
|
|
}
|
|
|
|
bool ProofOfWork::checkSolution(const uint256& solution) const
|
|
{
|
|
if (mIterations > sMaxIterations)
|
|
return false;
|
|
|
|
std::vector<uint256> buf1;
|
|
buf1.push_back(mChallenge);
|
|
buf1.push_back(solution);
|
|
buf1.push_back(uint256());
|
|
|
|
std::vector<uint256> buf2;
|
|
buf2.resize(mIterations);
|
|
for (int i = (mIterations - 1); i >= 0; --i)
|
|
{
|
|
buf1[2] = getSHA512Half(buf1);
|
|
buf2[i] = buf1[2];
|
|
}
|
|
return getSHA512Half(buf2) <= mTarget;
|
|
}
|
|
|
|
ProofOfWorkGenerator::ProofOfWorkGenerator() : mValidTime(180)
|
|
{
|
|
setDifficulty(1);
|
|
RAND_bytes(mSecret.begin(), mSecret.size());
|
|
}
|
|
|
|
ProofOfWork ProofOfWorkGenerator::getProof()
|
|
{
|
|
// challenge - target - iterations - time - validator
|
|
static boost::format f("%s-%s-%d-%d");
|
|
|
|
int now = static_cast<int>(time(NULL) / 4);
|
|
|
|
uint256 challenge;
|
|
RAND_bytes(challenge.begin(), challenge.size());
|
|
|
|
boost::mutex::scoped_lock sl(mLock);
|
|
|
|
std::string s = boost::str(f % challenge.GetHex() % mTarget.GetHex() % mIterations % now);
|
|
std::string c = mSecret.GetHex() + s;
|
|
s += "-" + Serializer::getSHA512Half(c).GetHex();
|
|
|
|
return ProofOfWork(s, mIterations, challenge, mTarget);
|
|
}
|
|
|
|
POWResult ProofOfWorkGenerator::checkProof(const std::string& token, const uint256& solution)
|
|
{ // challenge - target - iterations - time - validator
|
|
|
|
std::vector<std::string> fields;
|
|
boost::split(fields, token, boost::algorithm::is_any_of("-"));
|
|
if (fields.size() != 5)
|
|
{
|
|
cLog(lsDEBUG) << "PoW " << token << " is corrupt";
|
|
return powCORRUPT;
|
|
}
|
|
|
|
std::string v = mSecret.GetHex() + fields[0] + "-" + fields[1] + "-" + fields[2] + "-" + fields[3];
|
|
if (fields[4] != Serializer::getSHA512Half(v).GetHex())
|
|
{
|
|
cLog(lsDEBUG) << "PoW " << token << " has a bad token";
|
|
return powCORRUPT;
|
|
}
|
|
|
|
uint256 challenge, target;
|
|
challenge.SetHex(fields[0]);
|
|
target.SetHex(fields[1]);
|
|
|
|
time_t t = lexical_cast_s<time_t>(fields[3]);
|
|
time_t now = time(NULL);
|
|
|
|
int iterations = lexical_cast_s<int>(fields[2]);
|
|
|
|
{
|
|
boost::mutex::scoped_lock sl(mLock);
|
|
if ((t * 4) > (now + mValidTime))
|
|
{
|
|
cLog(lsDEBUG) << "PoW " << token << " has expired";
|
|
return powEXPIRED;
|
|
}
|
|
|
|
if (((iterations != mIterations) || (target != mTarget)) && getPowEntry(target, iterations) < (mPowEntry - 2))
|
|
{ // difficulty has increased more than two times since PoW requested
|
|
cLog(lsINFO) << "Difficulty has increased since PoW requested";
|
|
return powTOOEASY;
|
|
}
|
|
}
|
|
|
|
ProofOfWork pow(token, iterations, challenge, target);
|
|
if (!pow.checkSolution(solution))
|
|
{
|
|
cLog(lsDEBUG) << "PoW " << token << " has a bad nonce";
|
|
return powBADNONCE;
|
|
}
|
|
|
|
{
|
|
boost::mutex::scoped_lock sl(mLock);
|
|
if (!mSolvedChallenges.insert(powMap_vt(now, challenge)).second)
|
|
{
|
|
cLog(lsDEBUG) << "PoW " << token << " has been reused";
|
|
return powREUSED;
|
|
}
|
|
}
|
|
|
|
return powOK;
|
|
}
|
|
|
|
void ProofOfWorkGenerator::sweep()
|
|
{
|
|
time_t expire = time(NULL) - mValidTime;
|
|
|
|
boost::mutex::scoped_lock sl(mLock);
|
|
do
|
|
{
|
|
powMap_t::left_map::iterator it = mSolvedChallenges.left.begin();
|
|
if (it == mSolvedChallenges.left.end())
|
|
return;
|
|
if (it->first >= expire)
|
|
return;
|
|
mSolvedChallenges.left.erase(it);
|
|
} while(1);
|
|
}
|
|
|
|
void ProofOfWorkGenerator::loadHigh()
|
|
{
|
|
// WRITEME
|
|
}
|
|
|
|
void ProofOfWorkGenerator::loadLow()
|
|
{
|
|
// WRITEME
|
|
}
|
|
|
|
struct PowEntry
|
|
{
|
|
const char *target;
|
|
int iterations;
|
|
};
|
|
|
|
PowEntry PowEntries[31] =
|
|
{ // target iterations hashes memory
|
|
{ "0CFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 65536 }, // 1451874, 2 MB
|
|
{ "0CFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 98304 }, // 2177811, 3 MB
|
|
{ "07FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 98304 }, // 3538944, 3 MB
|
|
{ "0CFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 196608}, // 4355623, 6 MB
|
|
|
|
{ "07FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 131072}, // 4718592, 4 MB
|
|
{ "0CFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 262144}, // 5807497, 8 MB
|
|
{ "07FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 196608}, // 7077888, 6 MB
|
|
{ "07FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 262144}, // 9437184, 8 MB
|
|
|
|
{ "07FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 393216}, // 14155776, 12MB
|
|
{ "03FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 393216}, // 28311552, 12MB
|
|
{ "00CFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 262144}, // 92919965, 8 MB
|
|
{ "00CFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 393216}, // 139379948, 12MB
|
|
|
|
{ "007FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 262144}, // 150994944, 8 MB
|
|
{ "007FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 393216}, // 226492416, 12MB
|
|
{ "000CFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 49152 }, // 278759896, 1.5MB
|
|
{ "003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 262144}, // 301989888, 8 MB
|
|
|
|
{ "003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 393216}, // 452984832, 12MB
|
|
{ "0007FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 98304 }, // 905969664, 3 MB
|
|
{ "000CFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 196608}, // 1115039586, 6 MB
|
|
{ "000CFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 262144}, // 1486719448 8 MB
|
|
|
|
{ "000CFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 393216}, // 2230079172 12MB
|
|
{ "0007FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 262144}, // 2415919104, 8 MB
|
|
{ "0007FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 393216}, // 3623878656, 12MB
|
|
{ "0003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 393216}, // 7247757312, 12MB
|
|
|
|
{ "0000CFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 262144}, // 23787511177, 8 MB
|
|
{ "0000CFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 393216}, // 35681266766, 12MB
|
|
{ "00003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 131072}, // 38654705664, 4 MB
|
|
{ "00007FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 262144}, // 38654705664, 8 MB
|
|
|
|
{ "00003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 196608}, // 57982058496, 6 MB
|
|
{ "00007FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 393216}, // 57982058496, 12MB
|
|
{ "00003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 262144}, // 77309411328, 8 MB
|
|
};
|
|
|
|
int ProofOfWorkGenerator::getPowEntry(const uint256& target, int iterations)
|
|
{
|
|
for (int i = 0; i < 31; ++i)
|
|
if (PowEntries[i].iterations == iterations)
|
|
{
|
|
uint256 t;
|
|
t.SetHex(PowEntries[i].target);
|
|
if (t == target)
|
|
return i;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
void ProofOfWorkGenerator::setDifficulty(int i)
|
|
{
|
|
assert((i >= 0) && (i <= 30));
|
|
time_t now = time(NULL);
|
|
|
|
boost::mutex::scoped_lock sl(mLock);
|
|
mPowEntry = i;
|
|
mIterations = PowEntries[i].iterations;
|
|
mTarget.SetHex(PowEntries[i].target);
|
|
mLastDifficultyChange = now;
|
|
}
|
|
|
|
BOOST_AUTO_TEST_SUITE(ProofOfWork_suite)
|
|
|
|
BOOST_AUTO_TEST_CASE( ProofOfWork_test )
|
|
{
|
|
ProofOfWorkGenerator gen;
|
|
ProofOfWork pow = gen.getProof();
|
|
cLog(lsINFO) << "Estimated difficulty: " << pow.getDifficulty();
|
|
uint256 solution = pow.solve(16777216);
|
|
if (solution.isZero())
|
|
BOOST_FAIL("Unable to solve proof of work");
|
|
if (!pow.checkSolution(solution))
|
|
BOOST_FAIL("Solution did not check");
|
|
|
|
cLog(lsDEBUG) << "A bad nonce error is expected";
|
|
if (gen.checkProof(pow.getToken(), uint256()) != powBADNONCE)
|
|
BOOST_FAIL("Empty solution didn't show bad nonce");
|
|
if (gen.checkProof(pow.getToken(), solution) != powOK)
|
|
BOOST_FAIL("Solution did not check with issuer");
|
|
cLog(lsDEBUG) << "A reused nonce error is expected";
|
|
if (gen.checkProof(pow.getToken(), solution) != powREUSED)
|
|
BOOST_FAIL("Reuse solution not detected");
|
|
|
|
#ifdef SOLVE_POWS
|
|
for (int i = 0; i < 12; ++i)
|
|
{
|
|
gen.setDifficulty(i);
|
|
ProofOfWork pow = gen.getProof();
|
|
cLog(lsINFO) << "Level: " << i << ", Estimated difficulty: " << pow.getDifficulty();
|
|
uint256 solution = pow.solve(131072);
|
|
if (solution.isZero())
|
|
cLog(lsINFO) << "Giving up";
|
|
else
|
|
{
|
|
cLog(lsINFO) << "Solution found";
|
|
if (gen.checkProof(pow.getToken(), solution) != powOK)
|
|
{
|
|
cLog(lsFATAL) << "Solution fails";
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
}
|
|
|
|
BOOST_AUTO_TEST_SUITE_END()
|
|
|
|
// vim:ts=4
|