mirror of
https://github.com/XRPLF/rippled.git
synced 2025-11-27 06:25:51 +00:00
Split and refactor ProofOfWork
This commit is contained in:
@@ -47,6 +47,7 @@ Application::Application ()
|
||||
, mHashRouter (IHashRouter::New (IHashRouter::getDefaultHoldTime ()))
|
||||
, mValidations (IValidations::New ())
|
||||
, mUNL (IUniqueNodeList::New (mIOService))
|
||||
, mProofOfWorkFactory (IProofOfWorkFactory::New ())
|
||||
// VFALCO: End new stuff
|
||||
// VFALCO: TODO replace all NULL with nullptr
|
||||
, mRpcDB (NULL)
|
||||
|
||||
@@ -12,12 +12,10 @@
|
||||
#include "LedgerAcquire.h"
|
||||
#include "TransactionMaster.h"
|
||||
#include "Wallet.h"
|
||||
#include "Peer.h"
|
||||
#include "NetworkOPs.h"
|
||||
#include "WSDoor.h"
|
||||
#include "SNTPClient.h"
|
||||
#include "RPCHandler.h"
|
||||
#include "ProofOfWork.h"
|
||||
#include "LoadManager.h"
|
||||
#include "TransactionQueue.h"
|
||||
#include "OrderBookDB.h"
|
||||
@@ -31,6 +29,7 @@ class IHashRouter;
|
||||
class ILoadFeeTrack;
|
||||
class IValidations;
|
||||
class IUniqueNodeList;
|
||||
class IProofOfWorkFactory;
|
||||
|
||||
class RPCDoor;
|
||||
class PeerDoor;
|
||||
@@ -54,7 +53,6 @@ class Application
|
||||
SLECache mSLECache;
|
||||
SNTPClient mSNTPClient;
|
||||
JobQueue mJobQueue;
|
||||
ProofOfWorkGenerator mPOWGen;
|
||||
LoadManager mLoadMgr;
|
||||
TXQueue mTxnQueue;
|
||||
OrderBookDB mOrderBookDB;
|
||||
@@ -66,6 +64,7 @@ class Application
|
||||
beast::ScopedPointer <IHashRouter> mHashRouter;
|
||||
beast::ScopedPointer <IValidations> mValidations;
|
||||
beast::ScopedPointer <IUniqueNodeList> mUNL;
|
||||
beast::ScopedPointer <IProofOfWorkFactory> mProofOfWorkFactory;
|
||||
// VFALCO: End Clean stuff
|
||||
|
||||
DatabaseCon *mRpcDB, *mTxnDB, *mLedgerDB, *mWalletDB, *mNetNodeDB, *mPathFindDB, *mHashNodeDB;
|
||||
@@ -111,7 +110,6 @@ public:
|
||||
HashedObjectStore& getHashedObjectStore() { return mHashedObjectStore; }
|
||||
JobQueue& getJobQueue() { return mJobQueue; }
|
||||
boost::recursive_mutex& getMasterLock() { return mMasterLock; }
|
||||
ProofOfWorkGenerator& getPowGen() { return mPOWGen; }
|
||||
LoadManager& getLoadManager() { return mLoadMgr; }
|
||||
TXQueue& getTxnQueue() { return mTxnQueue; }
|
||||
PeerDoor& getPeerDoor() { return *mPeerDoor; }
|
||||
@@ -123,6 +121,7 @@ public:
|
||||
IFeeVote& getFeeVote() { return *mFeeVote; }
|
||||
IHashRouter& getHashRouter() { return *mHashRouter; }
|
||||
IValidations& getValidations() { return *mValidations; }
|
||||
IProofOfWorkFactory& getProofOfWorkFactory() { return *mProofOfWorkFactory; }
|
||||
|
||||
// VFALCO: TODO, Move these to the .cpp
|
||||
bool running() { return mTxnDB != NULL; } // VFALCO: TODO, replace with nullptr when beast is available
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
#include <boost/algorithm/string.hpp>
|
||||
#include <algorithm>
|
||||
|
||||
#include "Peer.h"
|
||||
#include "PeerDoor.h"
|
||||
#include "Application.h"
|
||||
|
||||
|
||||
@@ -6,8 +6,6 @@
|
||||
#include <boost/asio/ssl.hpp>
|
||||
#include <boost/thread/mutex.hpp>
|
||||
|
||||
#include "Peer.h"
|
||||
|
||||
//
|
||||
// Access to the Ripple network.
|
||||
//
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
#include <boost/weak_ptr.hpp>
|
||||
|
||||
#include "Ledger.h"
|
||||
#include "Peer.h"
|
||||
|
||||
// How long before we try again to acquire the same ledger
|
||||
#ifndef LEDGER_REACQUIRE_INTERVAL
|
||||
|
||||
@@ -11,7 +11,6 @@
|
||||
#include "Transaction.h"
|
||||
#include "LedgerAcquire.h"
|
||||
#include "LedgerProposal.h"
|
||||
#include "Peer.h"
|
||||
#include "CanonicalTXSet.h"
|
||||
#include "TransactionEngine.h"
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
|
||||
#include "Ledger.h"
|
||||
#include "LedgerHistory.h"
|
||||
#include "Peer.h"
|
||||
#include "LedgerAcquire.h"
|
||||
#include "Transaction.h"
|
||||
#include "TransactionEngine.h"
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
#include <boost/ref.hpp>
|
||||
|
||||
#include "Version.h"
|
||||
#include "Peer.h"
|
||||
#include "Application.h"
|
||||
#include "SerializedTransaction.h"
|
||||
|
||||
@@ -1319,7 +1318,7 @@ void Peer::recvProofWork(ripple::TMProofWork& packet)
|
||||
}
|
||||
uint256 response;
|
||||
memcpy(response.begin(), packet.response().data(), 256 / 8);
|
||||
POWResult r = theApp->getPowGen().checkProof(packet.token(), response);
|
||||
POWResult r = theApp->getProofOfWorkFactory().checkProof(packet.token(), response);
|
||||
if (r == powOK)
|
||||
{
|
||||
// credit peer
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
|
||||
#include "Ledger.h"
|
||||
#include "Transaction.h"
|
||||
#include "ProofOfWork.h"
|
||||
#include "LoadManager.h"
|
||||
|
||||
typedef std::pair<std::string,int> ipPort;
|
||||
|
||||
@@ -4,8 +4,6 @@
|
||||
#include <boost/asio.hpp>
|
||||
#include <boost/asio/ssl.hpp>
|
||||
|
||||
#include "Peer.h"
|
||||
|
||||
/*
|
||||
Handles incoming connections from other Peers
|
||||
*/
|
||||
|
||||
@@ -1,97 +0,0 @@
|
||||
#ifndef PROOF_OF_WORK__H
|
||||
#define PROOF_OF_WORK__H
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <boost/thread/mutex.hpp>
|
||||
#include <boost/bimap.hpp>
|
||||
#include <boost/bimap/unordered_set_of.hpp>
|
||||
#include <boost/bimap/multiset_of.hpp>
|
||||
|
||||
enum POWResult
|
||||
{
|
||||
powOK = 0,
|
||||
powREUSED = 1, // already submitted
|
||||
powBADNONCE = 2, // you didn't solve it
|
||||
powEXPIRED = 3, // time is up
|
||||
powCORRUPT = 4,
|
||||
powTOOEASY = 5, // the difficulty increased too much while you solved it
|
||||
};
|
||||
|
||||
bool powResultInfo(POWResult powCode, std::string& strToken, std::string& strHuman);
|
||||
|
||||
class ProofOfWork
|
||||
{
|
||||
public:
|
||||
static const int sMaxDifficulty;
|
||||
|
||||
typedef boost::shared_ptr<ProofOfWork> pointer;
|
||||
|
||||
ProofOfWork(const std::string& token, int iterations, const uint256& challenge, const uint256& target) :
|
||||
mToken(token), mChallenge(challenge), mTarget(target), mIterations(iterations)
|
||||
{ ; }
|
||||
|
||||
ProofOfWork(const std::string& token);
|
||||
|
||||
bool isValid() const;
|
||||
|
||||
uint256 solve(int maxIterations = 2 * sMaxIterations) const;
|
||||
bool checkSolution(const uint256& solution) const;
|
||||
|
||||
const std::string& getToken() const { return mToken; }
|
||||
const uint256& getChallenge() const { return mChallenge; }
|
||||
|
||||
// approximate number of hashes needed to solve
|
||||
static uint64 getDifficulty(const uint256& target, int iterations);
|
||||
uint64 getDifficulty() const { return getDifficulty(mTarget, mIterations); }
|
||||
|
||||
static bool validateToken(const std::string& strToken);
|
||||
|
||||
private:
|
||||
std::string mToken;
|
||||
uint256 mChallenge;
|
||||
uint256 mTarget;
|
||||
int mIterations;
|
||||
|
||||
static const uint256 sMinTarget;
|
||||
static const int sMaxIterations;
|
||||
};
|
||||
|
||||
class ProofOfWorkGenerator
|
||||
{
|
||||
public:
|
||||
typedef boost::bimap< boost::bimaps::multiset_of<time_t>, boost::bimaps::unordered_set_of<uint256> > powMap_t;
|
||||
typedef powMap_t::value_type powMap_vt;
|
||||
|
||||
public:
|
||||
ProofOfWorkGenerator();
|
||||
|
||||
ProofOfWork getProof();
|
||||
POWResult checkProof(const std::string& token, const uint256& solution);
|
||||
uint64 getDifficulty() { return ProofOfWork::getDifficulty(mTarget, mIterations); }
|
||||
void setDifficulty(int i);
|
||||
|
||||
void loadHigh();
|
||||
void loadLow();
|
||||
void sweep(void);
|
||||
|
||||
const uint256& getSecret() const { return mSecret; }
|
||||
void setSecret(const uint256& secret) { mSecret = secret; }
|
||||
|
||||
static int getPowEntry(const uint256& target, int iterations);
|
||||
|
||||
private:
|
||||
uint256 mSecret;
|
||||
int mIterations;
|
||||
uint256 mTarget;
|
||||
time_t mLastDifficultyChange;
|
||||
int mValidTime;
|
||||
int mPowEntry;
|
||||
|
||||
powMap_t mSolvedChallenges;
|
||||
boost::mutex mLock;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
// vim:ts=4
|
||||
@@ -20,7 +20,6 @@
|
||||
#include "NicknameState.h"
|
||||
#include "Offer.h"
|
||||
#include "PFRequest.h"
|
||||
#include "ProofOfWork.h"
|
||||
|
||||
SETUP_LOG (RPCHandler)
|
||||
|
||||
@@ -889,7 +888,7 @@ Json::Value RPCHandler::doProofCreate(Json::Value jvRequest, int& cost, ScopedLo
|
||||
|
||||
if (jvRequest.isMember("difficulty") || jvRequest.isMember("secret"))
|
||||
{
|
||||
ProofOfWorkGenerator pgGen;
|
||||
ProofOfWorkFactory pgGen;
|
||||
|
||||
if (jvRequest.isMember("difficulty"))
|
||||
{
|
||||
@@ -913,7 +912,7 @@ Json::Value RPCHandler::doProofCreate(Json::Value jvRequest, int& cost, ScopedLo
|
||||
jvResult["token"] = pgGen.getProof().getToken();
|
||||
jvResult["secret"] = pgGen.getSecret().GetHex();
|
||||
} else {
|
||||
jvResult["token"] = theApp->getPowGen().getProof().getToken();
|
||||
jvResult["token"] = theApp->getProofOfWorkFactory().getProof().getToken();
|
||||
}
|
||||
|
||||
return jvResult;
|
||||
@@ -971,7 +970,7 @@ Json::Value RPCHandler::doProofVerify(Json::Value jvRequest, int& cost, ScopedLo
|
||||
POWResult prResult;
|
||||
if (jvRequest.isMember("difficulty") || jvRequest.isMember("secret"))
|
||||
{
|
||||
ProofOfWorkGenerator pgGen;
|
||||
ProofOfWorkFactory pgGen;
|
||||
|
||||
if (jvRequest.isMember("difficulty"))
|
||||
{
|
||||
@@ -999,7 +998,7 @@ Json::Value RPCHandler::doProofVerify(Json::Value jvRequest, int& cost, ScopedLo
|
||||
else
|
||||
{
|
||||
// XXX Proof should not be marked as used from this
|
||||
prResult = theApp->getPowGen().checkProof(strToken, uSolution);
|
||||
prResult = theApp->getProofOfWorkFactory().checkProof(strToken, uSolution);
|
||||
}
|
||||
|
||||
std::string sToken;
|
||||
|
||||
54
src/cpp/ripple/ripple_IProofOfWorkFactory.h
Normal file
54
src/cpp/ripple/ripple_IProofOfWorkFactory.h
Normal file
@@ -0,0 +1,54 @@
|
||||
#ifndef RIPPLE_IPROOFOFWORKFACTORY_H
|
||||
#define RIPPLE_IPROOFOFWORKFACTORY_H
|
||||
|
||||
enum POWResult
|
||||
{
|
||||
powOK = 0,
|
||||
powREUSED = 1, // already submitted
|
||||
powBADNONCE = 2, // you didn't solve it
|
||||
powEXPIRED = 3, // time is up
|
||||
powCORRUPT = 4,
|
||||
powTOOEASY = 5, // the difficulty increased too much while you solved it
|
||||
};
|
||||
|
||||
// VFALCO: TODO move this to the class as a static member and rename it
|
||||
bool powResultInfo (POWResult powCode, std::string& strToken, std::string& strHuman);
|
||||
|
||||
class IProofOfWorkFactory
|
||||
{
|
||||
public:
|
||||
typedef boost::bimap< boost::bimaps::multiset_of<time_t>, boost::bimaps::unordered_set_of<uint256> > powMap_t;
|
||||
typedef powMap_t::value_type powMap_vt;
|
||||
|
||||
public:
|
||||
static IProofOfWorkFactory* New ();
|
||||
|
||||
virtual ~IProofOfWorkFactory () { }
|
||||
|
||||
// VFALCO: TODO which members can be const?
|
||||
|
||||
virtual ProofOfWork getProof () = 0;
|
||||
|
||||
virtual POWResult checkProof (const std::string& token, const uint256& solution) = 0;
|
||||
|
||||
virtual uint64 getDifficulty() = 0;
|
||||
|
||||
virtual void setDifficulty (int i) = 0;
|
||||
|
||||
virtual void loadHigh () = 0;
|
||||
|
||||
virtual void loadLow () = 0;
|
||||
|
||||
virtual void sweep () = 0;
|
||||
|
||||
virtual const uint256& getSecret () const = 0;
|
||||
|
||||
virtual void setSecret (const uint256& secret) = 0;
|
||||
|
||||
public:
|
||||
static int getPowEntry (const uint256& target, int iterations);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
// vim:ts=4
|
||||
172
src/cpp/ripple/ripple_ProofOfWork.cpp
Normal file
172
src/cpp/ripple/ripple_ProofOfWork.cpp
Normal file
@@ -0,0 +1,172 @@
|
||||
|
||||
#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,
|
||||
int iterations,
|
||||
const uint256& challenge,
|
||||
const uint256& target)
|
||||
: mToken (token)
|
||||
, mChallenge (challenge)
|
||||
, mTarget (target)
|
||||
, mIterations (iterations)
|
||||
{
|
||||
}
|
||||
|
||||
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;
|
||||
RandomNumbers::getInstance ().fill (&nonce);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
// vim:ts=4
|
||||
48
src/cpp/ripple/ripple_ProofOfWork.h
Normal file
48
src/cpp/ripple/ripple_ProofOfWork.h
Normal file
@@ -0,0 +1,48 @@
|
||||
#ifndef RIPPLE_PROOFOFWORK_H
|
||||
#define RIPPLE_PROOFOFWORK_H
|
||||
|
||||
class ProofOfWork
|
||||
{
|
||||
public:
|
||||
static const int sMaxDifficulty;
|
||||
|
||||
typedef boost::shared_ptr <ProofOfWork> pointer;
|
||||
|
||||
ProofOfWork (const std::string& token,
|
||||
int iterations,
|
||||
const uint256& challenge,
|
||||
const uint256& target);
|
||||
|
||||
explicit ProofOfWork (const std::string& token);
|
||||
|
||||
bool isValid() const;
|
||||
|
||||
uint256 solve(int maxIterations = 2 * sMaxIterations) const;
|
||||
bool checkSolution(const uint256& solution) const;
|
||||
|
||||
const std::string& getToken() const { return mToken; }
|
||||
const uint256& getChallenge() const { return mChallenge; }
|
||||
|
||||
uint64 getDifficulty() const
|
||||
{
|
||||
return getDifficulty(mTarget, mIterations);
|
||||
}
|
||||
|
||||
// approximate number of hashes needed to solve
|
||||
static uint64 getDifficulty (const uint256& target, int iterations);
|
||||
|
||||
static bool validateToken (const std::string& strToken);
|
||||
|
||||
private:
|
||||
std::string mToken;
|
||||
uint256 mChallenge;
|
||||
uint256 mTarget;
|
||||
int mIterations;
|
||||
|
||||
static const uint256 sMinTarget;
|
||||
static const int sMaxIterations;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
// vim:ts=4
|
||||
@@ -1,171 +1,44 @@
|
||||
#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)
|
||||
class ProofOfWorkFactory : public IProofOfWorkFactory
|
||||
{
|
||||
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." },
|
||||
public:
|
||||
ProofOfWorkFactory ();
|
||||
|
||||
{ powOK, "powOK", "Valid proof-of-work." },
|
||||
};
|
||||
ProofOfWork getProof();
|
||||
POWResult checkProof(const std::string& token, const uint256& solution);
|
||||
uint64 getDifficulty() { return ProofOfWork::getDifficulty(mTarget, mIterations); }
|
||||
void setDifficulty(int i);
|
||||
|
||||
int iIndex = NUMBER(powResultInfoA);
|
||||
void loadHigh();
|
||||
void loadLow();
|
||||
void sweep(void);
|
||||
|
||||
while (iIndex-- && powResultInfoA[iIndex].powCode != powCode)
|
||||
;
|
||||
const uint256& getSecret() const { return mSecret; }
|
||||
void setSecret(const uint256& secret) { mSecret = secret; }
|
||||
|
||||
if (iIndex >= 0)
|
||||
{
|
||||
strToken = powResultInfoA[iIndex].cpToken;
|
||||
strHuman = powResultInfoA[iIndex].cpHuman;
|
||||
}
|
||||
static int getPowEntry (const uint256& target, int iterations);
|
||||
|
||||
return iIndex >= 0;
|
||||
}
|
||||
private:
|
||||
uint256 mSecret;
|
||||
int mIterations;
|
||||
uint256 mTarget;
|
||||
time_t mLastDifficultyChange;
|
||||
int mValidTime;
|
||||
int mPowEntry;
|
||||
|
||||
const uint256 ProofOfWork::sMinTarget("00000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF");
|
||||
const int ProofOfWork::sMaxIterations(1 << 23);
|
||||
const int ProofOfWork::sMaxDifficulty(30);
|
||||
powMap_t mSolvedChallenges;
|
||||
boost::mutex mLock;
|
||||
};
|
||||
|
||||
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;
|
||||
RandomNumbers::getInstance ().fill (&nonce);
|
||||
|
||||
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)
|
||||
ProofOfWorkFactory::ProofOfWorkFactory() : mValidTime(180)
|
||||
{
|
||||
setDifficulty(1);
|
||||
RandomNumbers::getInstance ().fillBytes (mSecret.begin(), mSecret.size());
|
||||
}
|
||||
|
||||
ProofOfWork ProofOfWorkGenerator::getProof()
|
||||
ProofOfWork ProofOfWorkFactory::getProof()
|
||||
{
|
||||
// challenge - target - iterations - time - validator
|
||||
static boost::format f("%s-%s-%d-%d");
|
||||
@@ -184,7 +57,7 @@ ProofOfWork ProofOfWorkGenerator::getProof()
|
||||
return ProofOfWork(s, mIterations, challenge, mTarget);
|
||||
}
|
||||
|
||||
POWResult ProofOfWorkGenerator::checkProof(const std::string& token, const uint256& solution)
|
||||
POWResult ProofOfWorkFactory::checkProof(const std::string& token, const uint256& solution)
|
||||
{ // challenge - target - iterations - time - validator
|
||||
|
||||
std::vector<std::string> fields;
|
||||
@@ -245,7 +118,7 @@ POWResult ProofOfWorkGenerator::checkProof(const std::string& token, const uint2
|
||||
return powOK;
|
||||
}
|
||||
|
||||
void ProofOfWorkGenerator::sweep()
|
||||
void ProofOfWorkFactory::sweep()
|
||||
{
|
||||
time_t expire = time(NULL) - mValidTime;
|
||||
|
||||
@@ -261,7 +134,7 @@ void ProofOfWorkGenerator::sweep()
|
||||
} while(1);
|
||||
}
|
||||
|
||||
void ProofOfWorkGenerator::loadHigh()
|
||||
void ProofOfWorkFactory::loadHigh()
|
||||
{
|
||||
time_t now = time(NULL);
|
||||
|
||||
@@ -274,7 +147,7 @@ void ProofOfWorkGenerator::loadHigh()
|
||||
mLastDifficultyChange = now;
|
||||
}
|
||||
|
||||
void ProofOfWorkGenerator::loadLow()
|
||||
void ProofOfWorkFactory::loadLow()
|
||||
{
|
||||
time_t now = time(NULL);
|
||||
|
||||
@@ -335,7 +208,7 @@ PowEntry PowEntries[ProofOfWork::sMaxDifficulty + 1] =
|
||||
{ "00003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 262144}, // 77309411328, 8 MB
|
||||
};
|
||||
|
||||
int ProofOfWorkGenerator::getPowEntry(const uint256& target, int iterations)
|
||||
int ProofOfWorkFactory::getPowEntry(const uint256& target, int iterations)
|
||||
{
|
||||
for (int i = 0; i < 31; ++i)
|
||||
if (PowEntries[i].iterations == iterations)
|
||||
@@ -348,7 +221,7 @@ int ProofOfWorkGenerator::getPowEntry(const uint256& target, int iterations)
|
||||
return -1;
|
||||
}
|
||||
|
||||
void ProofOfWorkGenerator::setDifficulty(int i)
|
||||
void ProofOfWorkFactory::setDifficulty(int i)
|
||||
{
|
||||
assert((i >= 0) && (i <= ProofOfWork::sMaxDifficulty));
|
||||
time_t now = time(NULL);
|
||||
@@ -360,11 +233,16 @@ void ProofOfWorkGenerator::setDifficulty(int i)
|
||||
mLastDifficultyChange = now;
|
||||
}
|
||||
|
||||
IProofOfWorkFactory* IProofOfWorkFactory::New ()
|
||||
{
|
||||
return new ProofOfWorkFactory;
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE(ProofOfWork_suite)
|
||||
|
||||
BOOST_AUTO_TEST_CASE( ProofOfWork_test )
|
||||
{
|
||||
ProofOfWorkGenerator gen;
|
||||
ProofOfWorkFactory gen;
|
||||
ProofOfWork pow = gen.getProof();
|
||||
WriteLog (lsINFO, ProofOfWork) << "Estimated difficulty: " << pow.getDifficulty();
|
||||
uint256 solution = pow.solve(16777216);
|
||||
Reference in New Issue
Block a user