mirror of
https://github.com/Xahau/xahaud.git
synced 2025-12-06 17:27:52 +00:00
Start cleanup into ripple_data, split out some hash_value() instances Tidy up CBigNum into ripple_data, moving definitions to .cpp Split and clean up base58 stuff Remove unused files from VS2012 project Clean up some bignum stuff and remove unused files Partial cleanup of RFC1751 Enormous cleanup with RippleAddress and related, into ripple_data Remove unused VS project files Move ECIES stuff into CKey
414 lines
13 KiB
C++
414 lines
13 KiB
C++
#include "ProofOfWork.h"
|
|
|
|
#include <string>
|
|
|
|
#include <boost/test/unit_test.hpp>
|
|
#include <boost/algorithm/string.hpp>
|
|
#include <boost/format.hpp>
|
|
#include <boost/regex.hpp>
|
|
|
|
#include <openssl/rand.h>
|
|
|
|
SETUP_LOG (ProofOfWork)
|
|
|
|
bool powResultInfo(POWResult powCode, std::string& strToken, std::string& strHuman)
|
|
{
|
|
static struct {
|
|
POWResult powCode;
|
|
const char* cpToken;
|
|
const char* cpHuman;
|
|
} powResultInfoA[] = {
|
|
{ powREUSED, "powREUSED", "Proof-of-work has already been used." },
|
|
{ powBADNONCE, "powBADNONCE", "The solution does not meet the required difficulty." },
|
|
{ powEXPIRED, "powEXPIRED", "Token is expired." },
|
|
{ powCORRUPT, "powCORRUPT", "Invalid token." },
|
|
{ powTOOEASY, "powTOOEASY", "Difficulty has increased since token was issued." },
|
|
|
|
{ powOK, "powOK", "Valid proof-of-work." },
|
|
};
|
|
|
|
int iIndex = NUMBER(powResultInfoA);
|
|
|
|
while (iIndex-- && powResultInfoA[iIndex].powCode != powCode)
|
|
;
|
|
|
|
if (iIndex >= 0)
|
|
{
|
|
strToken = powResultInfoA[iIndex].cpToken;
|
|
strHuman = powResultInfoA[iIndex].cpHuman;
|
|
}
|
|
|
|
return iIndex >= 0;
|
|
}
|
|
|
|
const uint256 ProofOfWork::sMinTarget("00000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF");
|
|
const int ProofOfWork::sMaxIterations(1 << 23);
|
|
const int ProofOfWork::sMaxDifficulty(30);
|
|
|
|
ProofOfWork::ProofOfWork(const std::string& token)
|
|
{
|
|
std::vector<std::string> fields;
|
|
boost::split(fields, token, boost::algorithm::is_any_of("-"));
|
|
if (fields.size() != 5)
|
|
throw std::runtime_error("invalid token");
|
|
|
|
mToken = token;
|
|
mChallenge.SetHex(fields[0]);
|
|
mTarget.SetHex(fields[1]);
|
|
mIterations = lexical_cast_s<int>(fields[2]);
|
|
}
|
|
|
|
bool ProofOfWork::isValid() const
|
|
{
|
|
if ((mIterations <= sMaxIterations) && (mTarget >= sMinTarget))
|
|
return true;
|
|
WriteLog (lsWARNING, ProofOfWork) << "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))
|
|
{
|
|
WriteLog (lsINFO, ProofOfWork) << "Iterations:" << iterations;
|
|
WriteLog (lsINFO, ProofOfWork) << "MaxIterat: " << sMaxIterations;
|
|
WriteLog (lsINFO, ProofOfWork) << "Target: " << target;
|
|
WriteLog (lsINFO, ProofOfWork) << "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;
|
|
getRand(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;
|
|
}
|
|
|
|
bool ProofOfWork::validateToken(const std::string& strToken)
|
|
{
|
|
static boost::regex reToken("[[:xdigit:]]{64}-[[:xdigit:]]{64}-[[:digit:]]+-[[:digit:]]+-[[:xdigit:]]{64}");
|
|
boost::smatch smMatch;
|
|
|
|
return boost::regex_match(strToken, smMatch, reToken);
|
|
}
|
|
|
|
ProofOfWorkGenerator::ProofOfWorkGenerator() : mValidTime(180)
|
|
{
|
|
setDifficulty(1);
|
|
getRand(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;
|
|
getRand(challenge.begin(), challenge.size());
|
|
|
|
boost::mutex::scoped_lock sl(mLock);
|
|
|
|
std::string s = boost::str(boost::format(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)
|
|
{
|
|
WriteLog (lsDEBUG, ProofOfWork) << "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())
|
|
{
|
|
WriteLog (lsDEBUG, ProofOfWork) << "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))
|
|
{
|
|
WriteLog (lsDEBUG, ProofOfWork) << "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
|
|
WriteLog (lsINFO, ProofOfWork) << "Difficulty has increased since PoW requested";
|
|
return powTOOEASY;
|
|
}
|
|
}
|
|
|
|
ProofOfWork pow(token, iterations, challenge, target);
|
|
if (!pow.checkSolution(solution))
|
|
{
|
|
WriteLog (lsDEBUG, ProofOfWork) << "PoW " << token << " has a bad nonce";
|
|
return powBADNONCE;
|
|
}
|
|
|
|
{
|
|
boost::mutex::scoped_lock sl(mLock);
|
|
if (!mSolvedChallenges.insert(powMap_vt(now, challenge)).second)
|
|
{
|
|
WriteLog (lsDEBUG, ProofOfWork) << "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()
|
|
{
|
|
time_t now = time(NULL);
|
|
|
|
boost::mutex::scoped_lock sl(mLock);
|
|
if (mLastDifficultyChange == now)
|
|
return;
|
|
if (mPowEntry == 30)
|
|
return;
|
|
++mPowEntry;
|
|
mLastDifficultyChange = now;
|
|
}
|
|
|
|
void ProofOfWorkGenerator::loadLow()
|
|
{
|
|
time_t now = time(NULL);
|
|
|
|
boost::mutex::scoped_lock sl(mLock);
|
|
if (mLastDifficultyChange == now)
|
|
return;
|
|
if (mPowEntry == 0)
|
|
return;
|
|
--mPowEntry;
|
|
mLastDifficultyChange = now;
|
|
}
|
|
|
|
struct PowEntry
|
|
{
|
|
const char *target;
|
|
int iterations;
|
|
};
|
|
|
|
PowEntry PowEntries[ProofOfWork::sMaxDifficulty + 1] =
|
|
{ // 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 <= ProofOfWork::sMaxDifficulty));
|
|
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();
|
|
WriteLog (lsINFO, ProofOfWork) << "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");
|
|
|
|
WriteLog (lsDEBUG, ProofOfWork) << "A bad nonce error is expected";
|
|
POWResult r = gen.checkProof(pow.getToken(), uint256());
|
|
if (r != powBADNONCE)
|
|
{
|
|
Log(lsFATAL) << "POWResult = " << static_cast<int>(r);
|
|
BOOST_FAIL("Empty solution didn't show bad nonce");
|
|
}
|
|
if (gen.checkProof(pow.getToken(), solution) != powOK)
|
|
BOOST_FAIL("Solution did not check with issuer");
|
|
WriteLog (lsDEBUG, ProofOfWork) << "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();
|
|
WriteLog (lsINFO, ProofOfWork) << "Level: " << i << ", Estimated difficulty: " << pow.getDifficulty();
|
|
uint256 solution = pow.solve(131072);
|
|
if (solution.isZero())
|
|
WriteLog (lsINFO, ProofOfWork) << "Giving up";
|
|
else
|
|
{
|
|
WriteLog (lsINFO, ProofOfWork) << "Solution found";
|
|
if (gen.checkProof(pow.getToken(), solution) != powOK)
|
|
{
|
|
WriteLog (lsFATAL, ProofOfWork) << "Solution fails";
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
}
|
|
|
|
BOOST_AUTO_TEST_SUITE_END()
|
|
|
|
// vim:ts=4
|