mirror of
https://github.com/XRPLF/rippled.git
synced 2026-07-28 01:20:32 +00:00
Moved cpp code to src/cpp and js code to src/js.
This commit is contained in:
67
src/cpp/ripple/AccountState.cpp
Normal file
67
src/cpp/ripple/AccountState.cpp
Normal file
@@ -0,0 +1,67 @@
|
||||
|
||||
#include "AccountState.h"
|
||||
|
||||
#include <boost/lexical_cast.hpp>
|
||||
#include <boost/make_shared.hpp>
|
||||
#include <boost/algorithm/string.hpp>
|
||||
|
||||
#include "../json/writer.h"
|
||||
|
||||
#include "Ledger.h"
|
||||
#include "Serializer.h"
|
||||
#include "Log.h"
|
||||
|
||||
AccountState::AccountState(const RippleAddress& naAccountID) : mAccountID(naAccountID), mValid(false)
|
||||
{
|
||||
if (!naAccountID.isValid()) return;
|
||||
|
||||
mLedgerEntry = boost::make_shared<SerializedLedgerEntry>(ltACCOUNT_ROOT);
|
||||
mLedgerEntry->setIndex(Ledger::getAccountRootIndex(naAccountID));
|
||||
mLedgerEntry->setFieldAccount(sfAccount, naAccountID.getAccountID());
|
||||
|
||||
mValid = true;
|
||||
}
|
||||
|
||||
AccountState::AccountState(SLE::ref ledgerEntry, const RippleAddress& naAccountID) :
|
||||
mAccountID(naAccountID), mLedgerEntry(ledgerEntry), mValid(false)
|
||||
{
|
||||
if (!mLedgerEntry)
|
||||
return;
|
||||
if (mLedgerEntry->getType() != ltACCOUNT_ROOT)
|
||||
return;
|
||||
|
||||
mValid = true;
|
||||
}
|
||||
|
||||
std::string AccountState::createGravatarUrl(uint128 uEmailHash)
|
||||
{
|
||||
std::vector<unsigned char> vucMD5(uEmailHash.begin(), uEmailHash.end());
|
||||
std::string strMD5Lower = strHex(vucMD5);
|
||||
boost::to_lower(strMD5Lower);
|
||||
|
||||
return str(boost::format("http://www.gravatar.com/avatar/%s") % strMD5Lower);
|
||||
}
|
||||
|
||||
void AccountState::addJson(Json::Value& val)
|
||||
{
|
||||
val = mLedgerEntry->getJson(0);
|
||||
|
||||
if (mValid)
|
||||
{
|
||||
if (mLedgerEntry->isFieldPresent(sfEmailHash))
|
||||
val["UrlGravatar"] = createGravatarUrl(mLedgerEntry->getFieldH128(sfEmailHash));
|
||||
}
|
||||
else
|
||||
{
|
||||
val["Invalid"] = true;
|
||||
}
|
||||
}
|
||||
|
||||
void AccountState::dump()
|
||||
{
|
||||
Json::Value j(Json::objectValue);
|
||||
addJson(j);
|
||||
Log(lsINFO) << j;
|
||||
}
|
||||
|
||||
// vim:ts=4
|
||||
59
src/cpp/ripple/AccountState.h
Normal file
59
src/cpp/ripple/AccountState.h
Normal file
@@ -0,0 +1,59 @@
|
||||
#ifndef __ACCOUNTSTATE__
|
||||
#define __ACCOUNTSTATE__
|
||||
|
||||
//
|
||||
// Provide abstract access to an account's state, such that access to the serialized format is hidden.
|
||||
//
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include <boost/shared_ptr.hpp>
|
||||
|
||||
#include "../json/value.h"
|
||||
|
||||
#include "types.h"
|
||||
#include "RippleAddress.h"
|
||||
#include "SerializedLedger.h"
|
||||
|
||||
class AccountState
|
||||
{
|
||||
public:
|
||||
typedef boost::shared_ptr<AccountState> pointer;
|
||||
|
||||
private:
|
||||
RippleAddress mAccountID;
|
||||
RippleAddress mAuthorizedKey;
|
||||
SerializedLedgerEntry::pointer mLedgerEntry;
|
||||
|
||||
bool mValid;
|
||||
|
||||
public:
|
||||
AccountState(const RippleAddress& naAccountID); // For new accounts
|
||||
AccountState(SLE::ref ledgerEntry,const RippleAddress& naAccountI); // For accounts in a ledger
|
||||
|
||||
bool bHaveAuthorizedKey()
|
||||
{
|
||||
return mLedgerEntry->isFieldPresent(sfAuthorizedKey);
|
||||
}
|
||||
|
||||
RippleAddress getAuthorizedKey()
|
||||
{
|
||||
return mLedgerEntry->getFieldAccount(sfAuthorizedKey);
|
||||
}
|
||||
|
||||
STAmount getBalance() const { return mLedgerEntry->getFieldAmount(sfBalance); }
|
||||
uint32 getSeq() const { return mLedgerEntry->getFieldU32(sfSequence); }
|
||||
|
||||
SerializedLedgerEntry::pointer getSLE() { return mLedgerEntry; }
|
||||
const SerializedLedgerEntry& peekSLE() const { return *mLedgerEntry; }
|
||||
SerializedLedgerEntry& peekSLE() { return *mLedgerEntry; }
|
||||
|
||||
std::vector<unsigned char> getRaw() const;
|
||||
void addJson(Json::Value& value);
|
||||
void dump();
|
||||
|
||||
static std::string createGravatarUrl(uint128 uEmailHash);
|
||||
};
|
||||
|
||||
#endif
|
||||
// vim:ts=4
|
||||
1426
src/cpp/ripple/Amount.cpp
Normal file
1426
src/cpp/ripple/Amount.cpp
Normal file
File diff suppressed because it is too large
Load Diff
308
src/cpp/ripple/Application.cpp
Normal file
308
src/cpp/ripple/Application.cpp
Normal file
@@ -0,0 +1,308 @@
|
||||
|
||||
#include "Application.h"
|
||||
#include "Config.h"
|
||||
#include "PeerDoor.h"
|
||||
#include "RPCDoor.h"
|
||||
#include "BitcoinUtil.h"
|
||||
#include "key.h"
|
||||
#include "utils.h"
|
||||
#include "TaggedCache.h"
|
||||
#include "Log.h"
|
||||
|
||||
#include "../database/SqliteDatabase.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include <boost/bind.hpp>
|
||||
#include <boost/filesystem.hpp>
|
||||
#include <boost/thread.hpp>
|
||||
|
||||
SETUP_LOG();
|
||||
LogPartition TaggedCachePartition("TaggedCache");
|
||||
Application* theApp = NULL;
|
||||
|
||||
DatabaseCon::DatabaseCon(const std::string& strName, const char *initStrings[], int initCount)
|
||||
{
|
||||
boost::filesystem::path pPath = theConfig.DATA_DIR / strName;
|
||||
|
||||
mDatabase = new SqliteDatabase(pPath.string().c_str());
|
||||
mDatabase->connect();
|
||||
for(int i = 0; i < initCount; ++i)
|
||||
mDatabase->executeSQL(initStrings[i], true);
|
||||
}
|
||||
|
||||
DatabaseCon::~DatabaseCon()
|
||||
{
|
||||
mDatabase->disconnect();
|
||||
delete mDatabase;
|
||||
}
|
||||
|
||||
Application::Application() :
|
||||
mIOWork(mIOService), mAuxWork(mAuxService), mUNL(mIOService), mNetOps(mIOService, &mMasterLedger),
|
||||
mTempNodeCache("NodeCache", 16384, 90), mHashedObjectStore(16384, 300),
|
||||
mSNTPClient(mAuxService), mRPCHandler(&mNetOps),
|
||||
mRpcDB(NULL), mTxnDB(NULL), mLedgerDB(NULL), mWalletDB(NULL), mHashNodeDB(NULL), mNetNodeDB(NULL),
|
||||
mConnectionPool(mIOService), mPeerDoor(NULL), mRPCDoor(NULL), mWSPublicDoor(NULL), mWSPrivateDoor(NULL),
|
||||
mSweepTimer(mAuxService)
|
||||
{
|
||||
RAND_bytes(mNonce256.begin(), mNonce256.size());
|
||||
RAND_bytes(reinterpret_cast<unsigned char *>(&mNonceST), sizeof(mNonceST));
|
||||
mJobQueue.setThreadCount();
|
||||
mSweepTimer.expires_from_now(boost::posix_time::seconds(60));
|
||||
mSweepTimer.async_wait(boost::bind(&Application::sweep, this));
|
||||
}
|
||||
|
||||
extern const char *RpcDBInit[], *TxnDBInit[], *LedgerDBInit[], *WalletDBInit[], *HashNodeDBInit[], *NetNodeDBInit[];
|
||||
extern int RpcDBCount, TxnDBCount, LedgerDBCount, WalletDBCount, HashNodeDBCount, NetNodeDBCount;
|
||||
|
||||
void Application::stop()
|
||||
{
|
||||
mIOService.stop();
|
||||
mJobQueue.shutdown();
|
||||
mHashedObjectStore.bulkWrite();
|
||||
mValidations.flush();
|
||||
mAuxService.stop();
|
||||
|
||||
cLog(lsINFO) << "Stopped: " << mIOService.stopped();
|
||||
}
|
||||
|
||||
static void InitDB(DatabaseCon** dbCon, const char *fileName, const char *dbInit[], int dbCount)
|
||||
{
|
||||
*dbCon = new DatabaseCon(fileName, dbInit, dbCount);
|
||||
}
|
||||
|
||||
void Application::run()
|
||||
{
|
||||
assert(mTxnDB == NULL);
|
||||
if (!theConfig.DEBUG_LOGFILE.empty())
|
||||
{ // Let DEBUG messages go to the file but only WARNING or higher to regular output (unless verbose)
|
||||
Log::setLogFile(theConfig.DEBUG_LOGFILE);
|
||||
if (Log::getMinSeverity() > lsDEBUG)
|
||||
LogPartition::setSeverity(lsDEBUG);
|
||||
}
|
||||
|
||||
boost::thread auxThread(boost::bind(&boost::asio::io_service::run, &mAuxService));
|
||||
auxThread.detach();
|
||||
|
||||
|
||||
if (!theConfig.RUN_STANDALONE)
|
||||
mSNTPClient.init(theConfig.SNTP_SERVERS);
|
||||
|
||||
//
|
||||
// Construct databases.
|
||||
//
|
||||
boost::thread t1(boost::bind(&InitDB, &mRpcDB, "rpc.db", RpcDBInit, RpcDBCount));
|
||||
boost::thread t2(boost::bind(&InitDB, &mTxnDB, "transaction.db", TxnDBInit, TxnDBCount));
|
||||
boost::thread t3(boost::bind(&InitDB, &mLedgerDB, "ledger.db", LedgerDBInit, LedgerDBCount));
|
||||
boost::thread t4(boost::bind(&InitDB, &mWalletDB, "wallet.db", WalletDBInit, WalletDBCount));
|
||||
boost::thread t5(boost::bind(&InitDB, &mHashNodeDB, "hashnode.db", HashNodeDBInit, HashNodeDBCount));
|
||||
boost::thread t6(boost::bind(&InitDB, &mNetNodeDB, "netnode.db", NetNodeDBInit, NetNodeDBCount));
|
||||
t1.join(); t2.join(); t3.join(); t4.join(); t5.join(); t6.join();
|
||||
|
||||
if (theConfig.START_UP == Config::FRESH)
|
||||
{
|
||||
cLog(lsINFO) << "Starting new Ledger";
|
||||
startNewLedger();
|
||||
}
|
||||
else if (theConfig.START_UP == Config::LOAD)
|
||||
{
|
||||
cLog(lsINFO) << "Loading Old Ledger";
|
||||
loadOldLedger();
|
||||
}
|
||||
else if (theConfig.START_UP == Config::NETWORK)
|
||||
{ // This should probably become the default once we have a stable network
|
||||
if (!theConfig.RUN_STANDALONE)
|
||||
mNetOps.needNetworkLedger();
|
||||
startNewLedger();
|
||||
}
|
||||
else
|
||||
startNewLedger();
|
||||
|
||||
if (theConfig.FULL_HISTORY && (theConfig.START_UP != Config::LOAD))
|
||||
{
|
||||
Ledger::pointer ledger = Ledger::getLastFullLedger();
|
||||
if (ledger)
|
||||
mMasterLedger.setLedgerRangePresent(0, ledger->getLedgerSeq());
|
||||
}
|
||||
|
||||
//
|
||||
// Begin validation and ip maintenance.
|
||||
// - Wallet maintains local information: including identity and network connection persistence information.
|
||||
//
|
||||
mWallet.start();
|
||||
|
||||
//
|
||||
// Set up UNL.
|
||||
//
|
||||
if (!theConfig.RUN_STANDALONE)
|
||||
getUNL().nodeBootstrap();
|
||||
|
||||
|
||||
//
|
||||
// Allow peer connections.
|
||||
//
|
||||
if (!theConfig.RUN_STANDALONE && !theConfig.PEER_IP.empty() && theConfig.PEER_PORT)
|
||||
{
|
||||
mPeerDoor = new PeerDoor(mIOService);
|
||||
}
|
||||
else
|
||||
{
|
||||
cLog(lsINFO) << "Peer interface: disabled";
|
||||
}
|
||||
|
||||
//
|
||||
// Allow RPC connections.
|
||||
//
|
||||
if (!theConfig.RPC_IP.empty() && theConfig.RPC_PORT)
|
||||
{
|
||||
mRPCDoor = new RPCDoor(mIOService);
|
||||
}
|
||||
else
|
||||
{
|
||||
cLog(lsINFO) << "RPC interface: disabled";
|
||||
}
|
||||
|
||||
//
|
||||
// Allow private WS connections.
|
||||
//
|
||||
if (!theConfig.WEBSOCKET_IP.empty() && theConfig.WEBSOCKET_PORT)
|
||||
{
|
||||
mWSPrivateDoor = WSDoor::createWSDoor(theConfig.WEBSOCKET_IP, theConfig.WEBSOCKET_PORT, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
cLog(lsINFO) << "WS private interface: disabled";
|
||||
}
|
||||
|
||||
//
|
||||
// Allow public WS connections.
|
||||
//
|
||||
if (!theConfig.WEBSOCKET_PUBLIC_IP.empty() && theConfig.WEBSOCKET_PUBLIC_PORT)
|
||||
{
|
||||
mWSPublicDoor = WSDoor::createWSDoor(theConfig.WEBSOCKET_PUBLIC_IP, theConfig.WEBSOCKET_PUBLIC_PORT, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
cLog(lsINFO) << "WS public interface: disabled";
|
||||
}
|
||||
|
||||
//
|
||||
// Begin connecting to network.
|
||||
//
|
||||
if (!theConfig.RUN_STANDALONE)
|
||||
mConnectionPool.start();
|
||||
|
||||
|
||||
if (theConfig.RUN_STANDALONE)
|
||||
{
|
||||
cLog(lsWARNING) << "Running in standalone mode";
|
||||
mNetOps.setStandAlone();
|
||||
}
|
||||
else
|
||||
mNetOps.setStateTimer();
|
||||
|
||||
mIOService.run(); // This blocks
|
||||
|
||||
if (mWSPublicDoor)
|
||||
mWSPublicDoor->stop();
|
||||
|
||||
if (mWSPrivateDoor)
|
||||
mWSPrivateDoor->stop();
|
||||
|
||||
cLog(lsINFO) << "Done.";
|
||||
}
|
||||
|
||||
void Application::sweep()
|
||||
{
|
||||
mMasterTransaction.sweep();
|
||||
mHashedObjectStore.sweep();
|
||||
mMasterLedger.sweep();
|
||||
mTempNodeCache.sweep();
|
||||
mValidations.sweep();
|
||||
mSweepTimer.expires_from_now(boost::posix_time::seconds(60));
|
||||
mSweepTimer.async_wait(boost::bind(&Application::sweep, this));
|
||||
}
|
||||
|
||||
Application::~Application()
|
||||
{
|
||||
delete mTxnDB;
|
||||
delete mLedgerDB;
|
||||
delete mWalletDB;
|
||||
delete mHashNodeDB;
|
||||
delete mNetNodeDB;
|
||||
}
|
||||
|
||||
void Application::startNewLedger()
|
||||
{
|
||||
// New stuff.
|
||||
RippleAddress rootSeedMaster = RippleAddress::createSeedGeneric("masterpassphrase");
|
||||
RippleAddress rootGeneratorMaster = RippleAddress::createGeneratorPublic(rootSeedMaster);
|
||||
RippleAddress rootAddress = RippleAddress::createAccountPublic(rootGeneratorMaster, 0);
|
||||
|
||||
// Print enough information to be able to claim root account.
|
||||
cLog(lsINFO) << "Root master seed: " << rootSeedMaster.humanSeed();
|
||||
cLog(lsINFO) << "Root account: " << rootAddress.humanAccountID();
|
||||
|
||||
{
|
||||
Ledger::pointer firstLedger = boost::make_shared<Ledger>(rootAddress, SYSTEM_CURRENCY_START);
|
||||
assert(!!firstLedger->getAccountState(rootAddress));
|
||||
firstLedger->updateHash();
|
||||
firstLedger->setClosed();
|
||||
firstLedger->setAccepted();
|
||||
mMasterLedger.pushLedger(firstLedger);
|
||||
|
||||
Ledger::pointer secondLedger = boost::make_shared<Ledger>(true, boost::ref(*firstLedger));
|
||||
secondLedger->setClosed();
|
||||
secondLedger->setAccepted();
|
||||
mMasterLedger.pushLedger(secondLedger, boost::make_shared<Ledger>(true, boost::ref(*secondLedger)), false);
|
||||
assert(!!secondLedger->getAccountState(rootAddress));
|
||||
mNetOps.setLastCloseTime(secondLedger->getCloseTimeNC());
|
||||
}
|
||||
}
|
||||
|
||||
void Application::loadOldLedger()
|
||||
{
|
||||
try
|
||||
{
|
||||
Ledger::pointer lastLedger = Ledger::getLastFullLedger();
|
||||
|
||||
if (!lastLedger)
|
||||
{
|
||||
std::cout << "No Ledger found?" << std::endl;
|
||||
exit(-1);
|
||||
}
|
||||
lastLedger->setClosed();
|
||||
|
||||
cLog(lsINFO) << "Loading ledger " << lastLedger->getHash() << " seq:" << lastLedger->getLedgerSeq();
|
||||
|
||||
if (lastLedger->getAccountHash().isZero())
|
||||
{
|
||||
cLog(lsFATAL) << "Ledger is empty.";
|
||||
assert(false);
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
if (!lastLedger->walkLedger())
|
||||
{
|
||||
cLog(lsFATAL) << "Ledger is missing nodes.";
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
if (!lastLedger->assertSane())
|
||||
{
|
||||
cLog(lsFATAL) << "Ledger is not sane.";
|
||||
exit(-1);
|
||||
}
|
||||
mMasterLedger.setLedgerRangePresent(0, lastLedger->getLedgerSeq());
|
||||
|
||||
Ledger::pointer openLedger = boost::make_shared<Ledger>(false, boost::ref(*lastLedger));
|
||||
mMasterLedger.switchLedgers(lastLedger, openLedger);
|
||||
mNetOps.setLastCloseTime(lastLedger->getCloseTimeNC());
|
||||
}
|
||||
catch (SHAMapMissingNode& mn)
|
||||
{
|
||||
cLog(lsFATAL) << "Cannot load ledger. " << mn;
|
||||
exit(-1);
|
||||
}
|
||||
}
|
||||
// vim:ts=4
|
||||
129
src/cpp/ripple/Application.h
Normal file
129
src/cpp/ripple/Application.h
Normal file
@@ -0,0 +1,129 @@
|
||||
#ifndef __APPLICATION__
|
||||
#define __APPLICATION__
|
||||
|
||||
#include <boost/asio.hpp>
|
||||
|
||||
#include "LedgerMaster.h"
|
||||
#include "UniqueNodeList.h"
|
||||
#include "ConnectionPool.h"
|
||||
#include "ScopedLock.h"
|
||||
#include "LedgerAcquire.h"
|
||||
#include "TransactionMaster.h"
|
||||
#include "Wallet.h"
|
||||
#include "Peer.h"
|
||||
#include "NetworkOPs.h"
|
||||
#include "WSDoor.h"
|
||||
#include "TaggedCache.h"
|
||||
#include "ValidationCollection.h"
|
||||
#include "Suppression.h"
|
||||
#include "SNTPClient.h"
|
||||
#include "../database/database.h"
|
||||
#include "JobQueue.h"
|
||||
#include "RPCHandler.h"
|
||||
|
||||
class RPCDoor;
|
||||
class PeerDoor;
|
||||
typedef TaggedCache< uint256, std::vector<unsigned char> > NodeCache;
|
||||
|
||||
class DatabaseCon
|
||||
{
|
||||
protected:
|
||||
Database* mDatabase;
|
||||
boost::recursive_mutex mLock;
|
||||
|
||||
public:
|
||||
DatabaseCon(const std::string& name, const char *initString[], int countInit);
|
||||
~DatabaseCon();
|
||||
Database* getDB() { return mDatabase; }
|
||||
ScopedLock getDBLock() { return ScopedLock(mLock); }
|
||||
};
|
||||
|
||||
class Application
|
||||
{
|
||||
boost::asio::io_service mIOService, mAuxService;
|
||||
boost::asio::io_service::work mIOWork, mAuxWork;
|
||||
|
||||
Wallet mWallet;
|
||||
UniqueNodeList mUNL;
|
||||
LedgerMaster mMasterLedger;
|
||||
LedgerAcquireMaster mMasterLedgerAcquire;
|
||||
TransactionMaster mMasterTransaction;
|
||||
NetworkOPs mNetOps;
|
||||
NodeCache mTempNodeCache;
|
||||
ValidationCollection mValidations;
|
||||
SuppressionTable mSuppressions;
|
||||
HashedObjectStore mHashedObjectStore;
|
||||
SNTPClient mSNTPClient;
|
||||
JobQueue mJobQueue;
|
||||
RPCHandler mRPCHandler;
|
||||
|
||||
DatabaseCon *mRpcDB, *mTxnDB, *mLedgerDB, *mWalletDB, *mHashNodeDB, *mNetNodeDB;
|
||||
|
||||
ConnectionPool mConnectionPool;
|
||||
PeerDoor* mPeerDoor;
|
||||
RPCDoor* mRPCDoor;
|
||||
WSDoor* mWSPublicDoor;
|
||||
WSDoor* mWSPrivateDoor;
|
||||
|
||||
uint256 mNonce256;
|
||||
std::size_t mNonceST;
|
||||
|
||||
boost::asio::deadline_timer mSweepTimer;
|
||||
|
||||
std::map<std::string, Peer::pointer> mPeerMap;
|
||||
boost::recursive_mutex mPeerMapLock;
|
||||
|
||||
void startNewLedger();
|
||||
void loadOldLedger();
|
||||
|
||||
public:
|
||||
Application();
|
||||
~Application();
|
||||
|
||||
ConnectionPool& getConnectionPool() { return mConnectionPool; }
|
||||
|
||||
UniqueNodeList& getUNL() { return mUNL; }
|
||||
|
||||
Wallet& getWallet() { return mWallet ; }
|
||||
NetworkOPs& getOPs() { return mNetOps; }
|
||||
|
||||
boost::asio::io_service& getIOService() { return mIOService; }
|
||||
boost::asio::io_service& getAuxService() { return mAuxService; }
|
||||
|
||||
LedgerMaster& getMasterLedger() { return mMasterLedger; }
|
||||
LedgerAcquireMaster& getMasterLedgerAcquire() { return mMasterLedgerAcquire; }
|
||||
TransactionMaster& getMasterTransaction() { return mMasterTransaction; }
|
||||
NodeCache& getTempNodeCache() { return mTempNodeCache; }
|
||||
HashedObjectStore& getHashedObjectStore() { return mHashedObjectStore; }
|
||||
ValidationCollection& getValidations() { return mValidations; }
|
||||
JobQueue& getJobQueue() { return mJobQueue; }
|
||||
SuppressionTable& getSuppression() { return mSuppressions; }
|
||||
RPCHandler& getRPCHandler() { return mRPCHandler; }
|
||||
|
||||
|
||||
bool isNew(const uint256& s) { return mSuppressions.addSuppression(s); }
|
||||
bool isNew(const uint256& s, uint64 p) { return mSuppressions.addSuppressionPeer(s, p); }
|
||||
bool isNew(const uint256& s, uint64 p, int& f) { return mSuppressions.addSuppressionPeer(s, p, f); }
|
||||
bool isNewFlag(const uint256& s, int f) { return mSuppressions.setFlag(s, f); }
|
||||
bool running() { return mTxnDB != NULL; }
|
||||
bool getSystemTimeOffset(int& offset) { return mSNTPClient.getOffset(offset); }
|
||||
|
||||
DatabaseCon* getRpcDB() { return mRpcDB; }
|
||||
DatabaseCon* getTxnDB() { return mTxnDB; }
|
||||
DatabaseCon* getLedgerDB() { return mLedgerDB; }
|
||||
DatabaseCon* getWalletDB() { return mWalletDB; }
|
||||
DatabaseCon* getHashNodeDB() { return mHashNodeDB; }
|
||||
DatabaseCon* getNetNodeDB() { return mNetNodeDB; }
|
||||
|
||||
uint256 getNonce256() { return mNonce256; }
|
||||
std::size_t getNonceST() { return mNonceST; }
|
||||
|
||||
void run();
|
||||
void stop();
|
||||
void sweep();
|
||||
};
|
||||
|
||||
extern Application* theApp;
|
||||
|
||||
#endif
|
||||
// vim:ts=4
|
||||
107
src/cpp/ripple/BitcoinUtil.cpp
Normal file
107
src/cpp/ripple/BitcoinUtil.cpp
Normal file
@@ -0,0 +1,107 @@
|
||||
#include "BitcoinUtil.h"
|
||||
#include <cstdarg>
|
||||
#include <openssl/rand.h>
|
||||
#include <ctime>
|
||||
|
||||
#if defined(WIN32) || defined(WIN64)
|
||||
#include <windows.h>
|
||||
#else
|
||||
#include <sys/time.h>
|
||||
#endif
|
||||
|
||||
using namespace std;
|
||||
|
||||
std::string gFormatStr("v1");
|
||||
|
||||
std::string FormatFullVersion()
|
||||
{
|
||||
return(gFormatStr);
|
||||
}
|
||||
|
||||
|
||||
string strprintf(const char* format, ...)
|
||||
{
|
||||
char buffer[50000];
|
||||
char* p = buffer;
|
||||
int limit = sizeof(buffer);
|
||||
int ret;
|
||||
loop
|
||||
{
|
||||
va_list arg_ptr;
|
||||
va_start(arg_ptr, format);
|
||||
ret = _vsnprintf(p, limit, format, arg_ptr);
|
||||
va_end(arg_ptr);
|
||||
if (ret >= 0 && ret < limit)
|
||||
break;
|
||||
if (p != buffer)
|
||||
delete[] p;
|
||||
limit *= 2;
|
||||
p = new char[limit];
|
||||
if (p == NULL)
|
||||
throw std::bad_alloc();
|
||||
}
|
||||
string str(p, p+ret);
|
||||
if (p != buffer)
|
||||
delete[] p;
|
||||
return str;
|
||||
}
|
||||
|
||||
|
||||
inline int64 GetPerformanceCounter()
|
||||
{
|
||||
int64 nCounter = 0;
|
||||
#if defined(WIN32) || defined(WIN64)
|
||||
QueryPerformanceCounter((LARGE_INTEGER*)&nCounter);
|
||||
#else
|
||||
timeval t;
|
||||
gettimeofday(&t, NULL);
|
||||
nCounter = t.tv_sec * 1000000 + t.tv_usec;
|
||||
#endif
|
||||
return nCounter;
|
||||
}
|
||||
|
||||
void RandAddSeed()
|
||||
{
|
||||
// Seed with CPU performance counter
|
||||
int64 nCounter = GetPerformanceCounter();
|
||||
RAND_add(&nCounter, sizeof(nCounter), 1.5);
|
||||
memset(&nCounter, 0, sizeof(nCounter));
|
||||
}
|
||||
//
|
||||
// "Never go to sea with two chronometers; take one or three."
|
||||
// Our three time sources are:
|
||||
// - System clock
|
||||
// - Median of other nodes's clocks
|
||||
// - The user (asking the user to fix the system clock if the first two disagree)
|
||||
//
|
||||
int64 GetTime()
|
||||
{
|
||||
return time(NULL);
|
||||
}
|
||||
|
||||
void RandAddSeedPerfmon()
|
||||
{
|
||||
RandAddSeed();
|
||||
|
||||
// This can take up to 2 seconds, so only do it every 10 minutes
|
||||
static int64 nLastPerfmon;
|
||||
if (GetTime() < nLastPerfmon + 10 * 60)
|
||||
return;
|
||||
nLastPerfmon = GetTime();
|
||||
|
||||
#ifdef WIN32
|
||||
// Don't need this on Linux, OpenSSL automatically uses /dev/urandom
|
||||
// Seed with the entire set of perfmon data
|
||||
unsigned char pdata[250000];
|
||||
memset(pdata, 0, sizeof(pdata));
|
||||
unsigned long nSize = sizeof(pdata);
|
||||
long ret = RegQueryValueExA(HKEY_PERFORMANCE_DATA, "Global", NULL, NULL, pdata, &nSize);
|
||||
RegCloseKey(HKEY_PERFORMANCE_DATA);
|
||||
if (ret == ERROR_SUCCESS)
|
||||
{
|
||||
RAND_add(pdata, nSize, nSize/100.0);
|
||||
memset(pdata, 0, nSize);
|
||||
//printf("%s RandAddSeed() %d bytes\n", DateTimeStrFormat("%x %H:%M", GetTime()).c_str(), nSize);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
106
src/cpp/ripple/BitcoinUtil.h
Normal file
106
src/cpp/ripple/BitcoinUtil.h
Normal file
@@ -0,0 +1,106 @@
|
||||
#ifndef __BITCOIN_UTIL__
|
||||
#define __BITCOIN_UTIL__
|
||||
|
||||
// TODO: these things should all go somewhere
|
||||
|
||||
#include <string>
|
||||
#include "types.h"
|
||||
#include "uint256.h"
|
||||
#include <openssl/ripemd.h>
|
||||
#include <openssl/sha.h>
|
||||
|
||||
std::string strprintf(const char* format, ...);
|
||||
std::string FormatFullVersion();
|
||||
void RandAddSeedPerfmon();
|
||||
|
||||
static const unsigned int MAX_SIZE = 0x02000000;
|
||||
|
||||
#define loop for (;;)
|
||||
#define PAIR(t1, t2) pair<t1, t2>
|
||||
|
||||
#if !defined(WIN32) && !defined(WIN64)
|
||||
#define _vsnprintf(a,b,c,d) vsnprintf(a,b,c,d)
|
||||
#endif
|
||||
|
||||
|
||||
template<typename T1>
|
||||
inline uint256 SHA256Hash(const T1 pbegin, const T1 pend)
|
||||
{
|
||||
static unsigned char pblank[1];
|
||||
uint256 hash1;
|
||||
SHA256((pbegin == pend ? pblank : (unsigned char*)&pbegin[0]), (pend - pbegin) * sizeof(pbegin[0]), (unsigned char*)&hash1);
|
||||
uint256 hash2;
|
||||
SHA256((unsigned char*)&hash1, sizeof(hash1), (unsigned char*)&hash2);
|
||||
return hash2;
|
||||
}
|
||||
|
||||
template<typename T1, typename T2>
|
||||
inline uint256 SHA256Hash(const T1 p1begin, const T1 p1end,
|
||||
const T2 p2begin, const T2 p2end)
|
||||
{
|
||||
static unsigned char pblank[1];
|
||||
uint256 hash1;
|
||||
SHA256_CTX ctx;
|
||||
SHA256_Init(&ctx);
|
||||
SHA256_Update(&ctx, (p1begin == p1end ? pblank : (unsigned char*)&p1begin[0]), (p1end - p1begin) * sizeof(p1begin[0]));
|
||||
SHA256_Update(&ctx, (p2begin == p2end ? pblank : (unsigned char*)&p2begin[0]), (p2end - p2begin) * sizeof(p2begin[0]));
|
||||
SHA256_Final((unsigned char*)&hash1, &ctx);
|
||||
uint256 hash2;
|
||||
SHA256((unsigned char*)&hash1, sizeof(hash1), (unsigned char*)&hash2);
|
||||
return hash2;
|
||||
}
|
||||
|
||||
template<typename T1, typename T2, typename T3>
|
||||
inline uint256 SHA256Hash(const T1 p1begin, const T1 p1end,
|
||||
const T2 p2begin, const T2 p2end,
|
||||
const T3 p3begin, const T3 p3end)
|
||||
{
|
||||
static unsigned char pblank[1];
|
||||
uint256 hash1;
|
||||
SHA256_CTX ctx;
|
||||
SHA256_Init(&ctx);
|
||||
SHA256_Update(&ctx, (p1begin == p1end ? pblank : (unsigned char*)&p1begin[0]), (p1end - p1begin) * sizeof(p1begin[0]));
|
||||
SHA256_Update(&ctx, (p2begin == p2end ? pblank : (unsigned char*)&p2begin[0]), (p2end - p2begin) * sizeof(p2begin[0]));
|
||||
SHA256_Update(&ctx, (p3begin == p3end ? pblank : (unsigned char*)&p3begin[0]), (p3end - p3begin) * sizeof(p3begin[0]));
|
||||
SHA256_Final((unsigned char*)&hash1, &ctx);
|
||||
uint256 hash2;
|
||||
SHA256((unsigned char*)&hash1, sizeof(hash1), (unsigned char*)&hash2);
|
||||
return hash2;
|
||||
}
|
||||
|
||||
inline uint160 Hash160(const std::vector<unsigned char>& vch)
|
||||
{
|
||||
uint256 hash1;
|
||||
SHA256(&vch[0], vch.size(), (unsigned char*)&hash1);
|
||||
uint160 hash2;
|
||||
RIPEMD160((unsigned char*)&hash1, sizeof(hash1), (unsigned char*)&hash2);
|
||||
return hash2;
|
||||
}
|
||||
|
||||
/*
|
||||
#ifdef WIN32
|
||||
// This is used to attempt to keep keying material out of swap
|
||||
// Note that VirtualLock does not provide this as a guarantee on Windows,
|
||||
// but, in practice, memory that has been VirtualLock'd almost never gets written to
|
||||
// the pagefile except in rare circumstances where memory is extremely low.
|
||||
#include <windows.h>
|
||||
#define mlock(p, n) VirtualLock((p), (n));
|
||||
#define munlock(p, n) VirtualUnlock((p), (n));
|
||||
#else
|
||||
#include <sys/mman.h>
|
||||
#include <limits.h>
|
||||
// This comes from limits.h if it's not defined there set a sane default
|
||||
#ifndef PAGESIZE
|
||||
#include <unistd.h>
|
||||
#define PAGESIZE sysconf(_SC_PAGESIZE)
|
||||
#endif
|
||||
#define mlock(a,b) \
|
||||
mlock(((void *)(((size_t)(a)) & (~((PAGESIZE)-1)))),\
|
||||
(((((size_t)(a)) + (b) - 1) | ((PAGESIZE) - 1)) + 1) - (((size_t)(a)) & (~((PAGESIZE) - 1))))
|
||||
#define munlock(a,b) \
|
||||
munlock(((void *)(((size_t)(a)) & (~((PAGESIZE)-1)))),\
|
||||
(((((size_t)(a)) + (b) - 1) | ((PAGESIZE) - 1)) + 1) - (((size_t)(a)) & (~((PAGESIZE) - 1))))
|
||||
#endif
|
||||
*/
|
||||
|
||||
#endif
|
||||
160
src/cpp/ripple/CallRPC.cpp
Normal file
160
src/cpp/ripple/CallRPC.cpp
Normal file
@@ -0,0 +1,160 @@
|
||||
|
||||
#include <iostream>
|
||||
#include <cstdlib>
|
||||
|
||||
#include <boost/asio.hpp>
|
||||
#include <boost/iostreams/concepts.hpp>
|
||||
#include <boost/iostreams/stream.hpp>
|
||||
#include <boost/algorithm/string.hpp>
|
||||
|
||||
#include <openssl/buffer.h>
|
||||
#include <openssl/evp.h>
|
||||
|
||||
#include "../json/value.h"
|
||||
#include "../json/reader.h"
|
||||
|
||||
#include "CallRPC.h"
|
||||
#include "RPC.h"
|
||||
#include "Config.h"
|
||||
#include "BitcoinUtil.h"
|
||||
|
||||
static inline bool isSwitchChar(char c)
|
||||
{
|
||||
#ifdef __WXMSW__
|
||||
return c == '-' || c == '/';
|
||||
#else
|
||||
return c == '-';
|
||||
#endif
|
||||
}
|
||||
|
||||
std::string EncodeBase64(const std::string& s)
|
||||
{ // FIXME: This performs terribly
|
||||
BIO *b64, *bmem;
|
||||
BUF_MEM *bptr;
|
||||
|
||||
b64 = BIO_new(BIO_f_base64());
|
||||
BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL);
|
||||
bmem = BIO_new(BIO_s_mem());
|
||||
b64 = BIO_push(b64, bmem);
|
||||
BIO_write(b64, s.data(), s.size());
|
||||
(void) BIO_flush(b64);
|
||||
BIO_get_mem_ptr(b64, &bptr);
|
||||
|
||||
std::string result(bptr->data, bptr->length);
|
||||
BIO_free_all(b64);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
int commandLineRPC(const std::vector<std::string>& vCmd)
|
||||
{
|
||||
std::string strPrint;
|
||||
int nRet = 0;
|
||||
try
|
||||
{
|
||||
if (vCmd.empty()) return 1;
|
||||
|
||||
std::string strMethod = vCmd[0];
|
||||
|
||||
// Parameters default to strings
|
||||
Json::Value params(Json::arrayValue);
|
||||
for (int i = 1; i != vCmd.size(); i++)
|
||||
params.append(vCmd[i]);
|
||||
|
||||
// Execute
|
||||
Json::Value reply = callRPC(strMethod, params);
|
||||
|
||||
// Parse reply
|
||||
Json::Value result = reply.get("result", Json::Value());
|
||||
Json::Value error = reply.get("error", Json::Value());
|
||||
|
||||
if (result.isString() && (result.asString() == "unknown command"))
|
||||
nRet=1;
|
||||
|
||||
if (!error.isNull())
|
||||
{ // Error
|
||||
strPrint = "error: " + error.toStyledString();
|
||||
int code = error["code"].asInt();
|
||||
nRet = abs(code);
|
||||
}
|
||||
else
|
||||
{ // Result
|
||||
if (result.isNull())
|
||||
strPrint = "";
|
||||
else if (result.isString())
|
||||
strPrint = result.asString();
|
||||
else
|
||||
strPrint = result.toStyledString();
|
||||
}
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
strPrint = std::string("error: ") + e.what();
|
||||
nRet = 87;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
std::cout << "Exception CommandLineRPC()" << std::endl;
|
||||
}
|
||||
|
||||
if (strPrint != "")
|
||||
{
|
||||
std::cout << strPrint << std::endl;
|
||||
}
|
||||
return nRet;
|
||||
}
|
||||
|
||||
|
||||
Json::Value callRPC(const std::string& strMethod, const Json::Value& params)
|
||||
{
|
||||
if (theConfig.RPC_USER.empty() && theConfig.RPC_PASSWORD.empty())
|
||||
throw std::runtime_error("You must set rpcpassword=<password> in the configuration file"
|
||||
"If the file does not exist, create it with owner-readable-only file permissions.");
|
||||
|
||||
// Connect to localhost
|
||||
std::cout << "Connecting to: " << theConfig.RPC_IP << ":" << theConfig.RPC_PORT << std::endl;
|
||||
|
||||
boost::asio::ip::tcp::endpoint
|
||||
endpoint(boost::asio::ip::address::from_string(theConfig.RPC_IP), theConfig.RPC_PORT);
|
||||
boost::asio::ip::tcp::iostream stream;
|
||||
stream.connect(endpoint);
|
||||
if (stream.fail())
|
||||
throw std::runtime_error("couldn't connect to server");
|
||||
|
||||
// HTTP basic authentication
|
||||
std::string strUserPass64 = EncodeBase64(theConfig.RPC_USER + ":" + theConfig.RPC_PASSWORD);
|
||||
std::map<std::string, std::string> mapRequestHeaders;
|
||||
mapRequestHeaders["Authorization"] = std::string("Basic ") + strUserPass64;
|
||||
|
||||
// Send request
|
||||
std::string strRequest = JSONRPCRequest(strMethod, params, Json::Value(1));
|
||||
std::cout << "send request " << strMethod << " : " << strRequest << std::endl;
|
||||
std::string strPost = createHTTPPost(strRequest, mapRequestHeaders);
|
||||
stream << strPost << std::flush;
|
||||
|
||||
// std::cout << "post " << strPost << std::endl;
|
||||
|
||||
// Receive reply
|
||||
std::map<std::string, std::string> mapHeaders;
|
||||
std::string strReply;
|
||||
int nStatus = ReadHTTP(stream, mapHeaders, strReply);
|
||||
if (nStatus == 401)
|
||||
throw std::runtime_error("incorrect rpcuser or rpcpassword (authorization failed)");
|
||||
else if ((nStatus >= 400) && (nStatus != 400) && (nStatus != 404) && (nStatus != 500)) // ?
|
||||
throw std::runtime_error(strprintf("server returned HTTP error %d", nStatus));
|
||||
else if (strReply.empty())
|
||||
throw std::runtime_error("no response from server");
|
||||
|
||||
// Parse reply
|
||||
std::cout << "RPC reply: " << strReply << std::endl;
|
||||
Json::Reader reader;
|
||||
Json::Value valReply;
|
||||
if (!reader.parse(strReply, valReply))
|
||||
throw std::runtime_error("couldn't parse reply from server");
|
||||
if (valReply.isNull())
|
||||
throw std::runtime_error("expected reply to have result, error and id properties");
|
||||
|
||||
return valReply;
|
||||
}
|
||||
|
||||
// vim:ts=4
|
||||
7
src/cpp/ripple/CallRPC.h
Normal file
7
src/cpp/ripple/CallRPC.h
Normal file
@@ -0,0 +1,7 @@
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "../json/value.h"
|
||||
|
||||
extern int commandLineRPC(const std::vector<std::string>& vCmd);
|
||||
extern Json::Value callRPC(const std::string& strMethod, const Json::Value& params);
|
||||
55
src/cpp/ripple/CanonicalTXSet.cpp
Normal file
55
src/cpp/ripple/CanonicalTXSet.cpp
Normal file
@@ -0,0 +1,55 @@
|
||||
|
||||
#include "CanonicalTXSet.h"
|
||||
|
||||
bool CanonicalTXKey::operator<(const CanonicalTXKey& key) const
|
||||
{
|
||||
if (mAccount < key.mAccount) return true;
|
||||
if (mAccount > key.mAccount) return false;
|
||||
if (mSeq < key.mSeq) return true;
|
||||
if (mSeq > key.mSeq) return false;
|
||||
return mTXid < key.mTXid;
|
||||
}
|
||||
|
||||
bool CanonicalTXKey::operator>(const CanonicalTXKey& key) const
|
||||
{
|
||||
if (mAccount > key.mAccount) return true;
|
||||
if (mAccount < key.mAccount) return false;
|
||||
if (mSeq > key.mSeq) return true;
|
||||
if (mSeq < key.mSeq) return false;
|
||||
return mTXid > key.mTXid;
|
||||
}
|
||||
|
||||
bool CanonicalTXKey::operator<=(const CanonicalTXKey& key) const
|
||||
{
|
||||
if (mAccount < key.mAccount) return true;
|
||||
if (mAccount > key.mAccount) return false;
|
||||
if (mSeq < key.mSeq) return true;
|
||||
if (mSeq > key.mSeq) return false;
|
||||
return mTXid <= key.mTXid;
|
||||
}
|
||||
|
||||
bool CanonicalTXKey::operator>=(const CanonicalTXKey& key)const
|
||||
{
|
||||
if (mAccount > key.mAccount) return true;
|
||||
if (mAccount < key.mAccount) return false;
|
||||
if (mSeq > key.mSeq) return true;
|
||||
if (mSeq < key.mSeq) return false;
|
||||
return mTXid >= key.mTXid;
|
||||
}
|
||||
|
||||
void CanonicalTXSet::push_back(SerializedTransaction::ref txn)
|
||||
{
|
||||
uint256 effectiveAccount = mSetHash;
|
||||
effectiveAccount ^= txn->getSourceAccount().getAccountID().to256();
|
||||
mMap.insert(std::make_pair(CanonicalTXKey(effectiveAccount, txn->getSequence(), txn->getTransactionID()), txn));
|
||||
}
|
||||
|
||||
CanonicalTXSet::iterator CanonicalTXSet::erase(const iterator& it)
|
||||
{
|
||||
iterator tmp = it;
|
||||
++tmp;
|
||||
mMap.erase(it);
|
||||
return tmp;
|
||||
}
|
||||
|
||||
|
||||
59
src/cpp/ripple/CanonicalTXSet.h
Normal file
59
src/cpp/ripple/CanonicalTXSet.h
Normal file
@@ -0,0 +1,59 @@
|
||||
#ifndef __CANONICAL_TX_SET_
|
||||
#define __CANONICAL_TX_SET_
|
||||
|
||||
#include <map>
|
||||
|
||||
#include "uint256.h"
|
||||
#include "SerializedTransaction.h"
|
||||
|
||||
class CanonicalTXKey
|
||||
{
|
||||
protected:
|
||||
uint256 mAccount, mTXid;
|
||||
uint32 mSeq;
|
||||
|
||||
public:
|
||||
CanonicalTXKey(const uint256& account, uint32 seq, const uint256& id)
|
||||
: mAccount(account), mTXid(id), mSeq(seq) { ; }
|
||||
|
||||
bool operator<(const CanonicalTXKey&) const;
|
||||
bool operator>(const CanonicalTXKey&) const;
|
||||
bool operator<=(const CanonicalTXKey&) const;
|
||||
bool operator>=(const CanonicalTXKey&) const;
|
||||
|
||||
bool operator==(const CanonicalTXKey& k) const { return mTXid == k.mTXid; }
|
||||
bool operator!=(const CanonicalTXKey& k) const { return mTXid != k.mTXid; }
|
||||
};
|
||||
|
||||
class CanonicalTXSet
|
||||
{
|
||||
public:
|
||||
typedef std::map<CanonicalTXKey, SerializedTransaction::pointer>::iterator iterator;
|
||||
typedef std::map<CanonicalTXKey, SerializedTransaction::pointer>::const_iterator const_iterator;
|
||||
|
||||
protected:
|
||||
uint256 mSetHash;
|
||||
std::map<CanonicalTXKey, SerializedTransaction::pointer> mMap;
|
||||
|
||||
public:
|
||||
CanonicalTXSet(const uint256& lclHash) : mSetHash(lclHash) { ; }
|
||||
|
||||
void push_back(SerializedTransaction::ref txn);
|
||||
|
||||
void reset(const uint256& newLCL)
|
||||
{
|
||||
mSetHash = newLCL;
|
||||
mMap.clear();
|
||||
}
|
||||
|
||||
iterator erase(const iterator& it);
|
||||
|
||||
iterator begin() { return mMap.begin(); }
|
||||
iterator end() { return mMap.end(); }
|
||||
const_iterator begin() const { return mMap.begin(); }
|
||||
const_iterator end() const { return mMap.end(); }
|
||||
size_t size() const { return mMap.size(); }
|
||||
bool empty() const { return mMap.empty(); }
|
||||
};
|
||||
|
||||
#endif
|
||||
304
src/cpp/ripple/Config.cpp
Normal file
304
src/cpp/ripple/Config.cpp
Normal file
@@ -0,0 +1,304 @@
|
||||
#include "Config.h"
|
||||
|
||||
#include "utils.h"
|
||||
|
||||
#include <boost/lexical_cast.hpp>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <algorithm>
|
||||
|
||||
#define SECTION_ACCOUNT_PROBE_MAX "account_probe_max"
|
||||
#define SECTION_DEBUG_LOGFILE "debug_logfile"
|
||||
#define SECTION_FEE_ACCOUNT_CREATE "fee_account_create"
|
||||
#define SECTION_FEE_DEFAULT "fee_default"
|
||||
#define SECTION_FEE_NICKNAME_CREATE "fee_nickname_create"
|
||||
#define SECTION_FEE_OFFER "fee_offer"
|
||||
#define SECTION_FEE_OPERATION "fee_operation"
|
||||
#define SECTION_FULL_HISTORY "full_history"
|
||||
#define SECTION_IPS "ips"
|
||||
#define SECTION_NETWORK_QUORUM "network_quorum"
|
||||
#define SECTION_PEER_CONNECT_LOW_WATER "peer_connect_low_water"
|
||||
#define SECTION_PEER_IP "peer_ip"
|
||||
#define SECTION_PEER_PORT "peer_port"
|
||||
#define SECTION_PEER_SCAN_INTERVAL_MIN "peer_scan_interval_min"
|
||||
#define SECTION_PEER_SSL_CIPHER_LIST "peer_ssl_cipher_list"
|
||||
#define SECTION_PEER_START_MAX "peer_start_max"
|
||||
#define SECTION_RPC_ALLOW_REMOTE "rpc_allow_remote"
|
||||
#define SECTION_RPC_IP "rpc_ip"
|
||||
#define SECTION_RPC_PORT "rpc_port"
|
||||
#define SECTION_SNTP "sntp_servers"
|
||||
#define SECTION_UNL_DEFAULT "unl_default"
|
||||
#define SECTION_VALIDATION_QUORUM "validation_quorum"
|
||||
#define SECTION_VALIDATION_SEED "validation_seed"
|
||||
#define SECTION_WEBSOCKET_PUBLIC_IP "websocket_public_ip"
|
||||
#define SECTION_WEBSOCKET_PUBLIC_PORT "websocket_public_port"
|
||||
#define SECTION_WEBSOCKET_IP "websocket_ip"
|
||||
#define SECTION_WEBSOCKET_PORT "websocket_port"
|
||||
#define SECTION_VALIDATORS "validators"
|
||||
#define SECTION_VALIDATORS_SITE "validators_site"
|
||||
|
||||
// Fees are in XRP.
|
||||
#define DEFAULT_FEE_DEFAULT 10
|
||||
#define DEFAULT_FEE_ACCOUNT_CREATE 1000*SYSTEM_CURRENCY_PARTS
|
||||
#define DEFAULT_FEE_NICKNAME_CREATE 1000
|
||||
#define DEFAULT_FEE_OFFER DEFAULT_FEE_DEFAULT
|
||||
#define DEFAULT_FEE_OPERATION 1
|
||||
|
||||
Config theConfig;
|
||||
|
||||
void Config::setup(const std::string& strConf)
|
||||
{
|
||||
boost::system::error_code ec;
|
||||
|
||||
//
|
||||
// Determine the config and data directories.
|
||||
// If the config file is found in the current working directory, use the current working directory as the config directory and
|
||||
// that with "db" as the data directory.
|
||||
//
|
||||
|
||||
if (!strConf.empty())
|
||||
{
|
||||
// --conf=<path> : everything is relative that file.
|
||||
CONFIG_FILE = strConf;
|
||||
CONFIG_DIR = CONFIG_FILE;
|
||||
CONFIG_DIR.remove_filename();
|
||||
DATA_DIR = CONFIG_DIR / "db";
|
||||
}
|
||||
else
|
||||
{
|
||||
CONFIG_DIR = boost::filesystem::current_path();
|
||||
CONFIG_FILE = CONFIG_DIR / CONFIG_FILE_NAME;
|
||||
DATA_DIR = CONFIG_DIR / "db";
|
||||
|
||||
if (exists(CONFIG_FILE)
|
||||
// Can we figure out XDG dirs?
|
||||
|| (!getenv("HOME") && (!getenv("XDG_CONFIG_HOME") || !getenv("XDG_DATA_HOME"))))
|
||||
{
|
||||
// Current working directory is fine, put dbs in a subdir.
|
||||
nothing();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Construct XDG config and data home.
|
||||
// http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html
|
||||
std::string strHome = strGetEnv("HOME");
|
||||
std::string strXdgConfigHome = strGetEnv("XDG_CONFIG_HOME");
|
||||
std::string strXdgDataHome = strGetEnv("XDG_DATA_HOME");
|
||||
|
||||
if (strXdgConfigHome.empty())
|
||||
{
|
||||
// $XDG_CONFIG_HOME was not set, use default based on $HOME.
|
||||
strXdgConfigHome = str(boost::format("%s/.config") % strHome);
|
||||
}
|
||||
|
||||
if (strXdgDataHome.empty())
|
||||
{
|
||||
// $XDG_DATA_HOME was not set, use default based on $HOME.
|
||||
strXdgDataHome = str(boost::format("%s/.local/share") % strHome);
|
||||
}
|
||||
|
||||
CONFIG_DIR = str(boost::format("%s/" SYSTEM_NAME) % strXdgConfigHome);
|
||||
CONFIG_FILE = CONFIG_DIR / CONFIG_FILE_NAME;
|
||||
DATA_DIR = str(boost::format("%s/" SYSTEM_NAME) % strXdgDataHome);
|
||||
|
||||
boost::filesystem::create_directories(CONFIG_DIR, ec);
|
||||
|
||||
if (ec)
|
||||
throw std::runtime_error(str(boost::format("Can not create %s") % CONFIG_DIR));
|
||||
}
|
||||
}
|
||||
|
||||
boost::filesystem::create_directories(DATA_DIR, ec);
|
||||
|
||||
if (ec)
|
||||
throw std::runtime_error(str(boost::format("Can not create %s") % DATA_DIR));
|
||||
|
||||
// std::cerr << "CONFIG FILE: " << CONFIG_FILE << std::endl;
|
||||
// std::cerr << "CONFIG DIR: " << CONFIG_DIR << std::endl;
|
||||
// std::cerr << "DATA DIR: " << DATA_DIR << std::endl;
|
||||
|
||||
//
|
||||
// Defaults
|
||||
//
|
||||
|
||||
NETWORK_START_TIME = 1319844908;
|
||||
|
||||
PEER_PORT = SYSTEM_PEER_PORT;
|
||||
RPC_PORT = 5001;
|
||||
WEBSOCKET_PORT = SYSTEM_WEBSOCKET_PORT;
|
||||
WEBSOCKET_PUBLIC_PORT = SYSTEM_WEBSOCKET_PUBLIC_PORT;
|
||||
NUMBER_CONNECTIONS = 30;
|
||||
|
||||
// a new ledger every minute
|
||||
LEDGER_SECONDS = 60;
|
||||
LEDGER_CREATOR = false;
|
||||
|
||||
RPC_USER = "admin";
|
||||
RPC_PASSWORD = "pass";
|
||||
RPC_ALLOW_REMOTE = false;
|
||||
|
||||
PEER_SSL_CIPHER_LIST = DEFAULT_PEER_SSL_CIPHER_LIST;
|
||||
PEER_SCAN_INTERVAL_MIN = DEFAULT_PEER_SCAN_INTERVAL_MIN;
|
||||
|
||||
PEER_START_MAX = DEFAULT_PEER_START_MAX;
|
||||
PEER_CONNECT_LOW_WATER = DEFAULT_PEER_CONNECT_LOW_WATER;
|
||||
|
||||
TRANSACTION_FEE_BASE = 1000;
|
||||
|
||||
NETWORK_QUORUM = 0; // Don't need to see other nodes
|
||||
VALIDATION_QUORUM = 1; // Only need one node to vouch
|
||||
|
||||
FEE_ACCOUNT_CREATE = DEFAULT_FEE_ACCOUNT_CREATE;
|
||||
FEE_NICKNAME_CREATE = DEFAULT_FEE_NICKNAME_CREATE;
|
||||
FEE_OFFER = DEFAULT_FEE_OFFER;
|
||||
FEE_DEFAULT = DEFAULT_FEE_DEFAULT;
|
||||
FEE_CONTRACT_OPERATION = DEFAULT_FEE_OPERATION;
|
||||
|
||||
FULL_HISTORY = false;
|
||||
|
||||
ACCOUNT_PROBE_MAX = 10;
|
||||
|
||||
VALIDATORS_SITE = DEFAULT_VALIDATORS_SITE;
|
||||
|
||||
RUN_STANDALONE = false;
|
||||
START_UP = NORMAL;
|
||||
|
||||
load();
|
||||
}
|
||||
|
||||
void Config::load()
|
||||
{
|
||||
std::cout << "Loading: " << CONFIG_FILE << std::endl;
|
||||
|
||||
std::ifstream ifsConfig(CONFIG_FILE.c_str(), std::ios::in);
|
||||
|
||||
if (!ifsConfig)
|
||||
{
|
||||
std::cerr << "Failed to open '" << CONFIG_FILE << "'." << std::endl;
|
||||
}
|
||||
else
|
||||
{
|
||||
std::string strConfigFile;
|
||||
|
||||
strConfigFile.assign((std::istreambuf_iterator<char>(ifsConfig)),
|
||||
std::istreambuf_iterator<char>());
|
||||
|
||||
if (ifsConfig.bad())
|
||||
{
|
||||
std::cerr << "Failed to read '" << CONFIG_FILE << "'." << std::endl;
|
||||
}
|
||||
else
|
||||
{
|
||||
section secConfig = ParseSection(strConfigFile, true);
|
||||
std::string strTemp;
|
||||
|
||||
// XXX Leak
|
||||
section::mapped_type* smtTmp;
|
||||
|
||||
smtTmp = sectionEntries(secConfig, SECTION_VALIDATORS);
|
||||
if (smtTmp)
|
||||
{
|
||||
VALIDATORS = *smtTmp;
|
||||
// sectionEntriesPrint(&VALIDATORS, SECTION_VALIDATORS);
|
||||
}
|
||||
|
||||
smtTmp = sectionEntries(secConfig, SECTION_IPS);
|
||||
if (smtTmp)
|
||||
{
|
||||
IPS = *smtTmp;
|
||||
// sectionEntriesPrint(&IPS, SECTION_IPS);
|
||||
}
|
||||
|
||||
smtTmp = sectionEntries(secConfig, SECTION_SNTP);
|
||||
if (smtTmp)
|
||||
{
|
||||
SNTP_SERVERS = *smtTmp;
|
||||
}
|
||||
|
||||
(void) sectionSingleB(secConfig, SECTION_VALIDATORS_SITE, VALIDATORS_SITE);
|
||||
|
||||
(void) sectionSingleB(secConfig, SECTION_PEER_IP, PEER_IP);
|
||||
|
||||
if (sectionSingleB(secConfig, SECTION_PEER_PORT, strTemp))
|
||||
PEER_PORT = boost::lexical_cast<int>(strTemp);
|
||||
|
||||
(void) sectionSingleB(secConfig, SECTION_RPC_IP, RPC_IP);
|
||||
|
||||
if (sectionSingleB(secConfig, SECTION_RPC_PORT, strTemp))
|
||||
RPC_PORT = boost::lexical_cast<int>(strTemp);
|
||||
|
||||
if (sectionSingleB(secConfig, "ledger_creator" , strTemp))
|
||||
LEDGER_CREATOR = boost::lexical_cast<bool>(strTemp);
|
||||
|
||||
if (sectionSingleB(secConfig, SECTION_RPC_ALLOW_REMOTE, strTemp))
|
||||
RPC_ALLOW_REMOTE = boost::lexical_cast<bool>(strTemp);
|
||||
|
||||
(void) sectionSingleB(secConfig, SECTION_WEBSOCKET_IP, WEBSOCKET_IP);
|
||||
|
||||
if (sectionSingleB(secConfig, SECTION_WEBSOCKET_PORT, strTemp))
|
||||
WEBSOCKET_PORT = boost::lexical_cast<int>(strTemp);
|
||||
|
||||
(void) sectionSingleB(secConfig, SECTION_WEBSOCKET_PUBLIC_IP, WEBSOCKET_PUBLIC_IP);
|
||||
|
||||
if (sectionSingleB(secConfig, SECTION_WEBSOCKET_PUBLIC_PORT, strTemp))
|
||||
WEBSOCKET_PUBLIC_PORT = boost::lexical_cast<int>(strTemp);
|
||||
|
||||
if (sectionSingleB(secConfig, SECTION_VALIDATION_SEED, strTemp))
|
||||
{
|
||||
VALIDATION_SEED.setSeedGeneric(strTemp);
|
||||
if (VALIDATION_SEED.isValid())
|
||||
{
|
||||
VALIDATION_PUB = RippleAddress::createNodePublic(VALIDATION_SEED);
|
||||
VALIDATION_PRIV = RippleAddress::createNodePrivate(VALIDATION_SEED);
|
||||
}
|
||||
}
|
||||
|
||||
(void) sectionSingleB(secConfig, SECTION_PEER_SSL_CIPHER_LIST, PEER_SSL_CIPHER_LIST);
|
||||
|
||||
if (sectionSingleB(secConfig, SECTION_PEER_SCAN_INTERVAL_MIN, strTemp))
|
||||
// Minimum for min is 60 seconds.
|
||||
PEER_SCAN_INTERVAL_MIN = std::max(60, boost::lexical_cast<int>(strTemp));
|
||||
|
||||
if (sectionSingleB(secConfig, SECTION_PEER_START_MAX, strTemp))
|
||||
PEER_START_MAX = std::max(1, boost::lexical_cast<int>(strTemp));
|
||||
|
||||
if (sectionSingleB(secConfig, SECTION_PEER_CONNECT_LOW_WATER, strTemp))
|
||||
PEER_CONNECT_LOW_WATER = std::max(1, boost::lexical_cast<int>(strTemp));
|
||||
|
||||
if (sectionSingleB(secConfig, SECTION_NETWORK_QUORUM, strTemp))
|
||||
NETWORK_QUORUM = std::max(0, boost::lexical_cast<int>(strTemp));
|
||||
|
||||
if (sectionSingleB(secConfig, SECTION_VALIDATION_QUORUM, strTemp))
|
||||
VALIDATION_QUORUM = std::max(0, boost::lexical_cast<int>(strTemp));
|
||||
|
||||
if (sectionSingleB(secConfig, SECTION_FEE_ACCOUNT_CREATE, strTemp))
|
||||
FEE_ACCOUNT_CREATE = boost::lexical_cast<int>(strTemp);
|
||||
|
||||
if (sectionSingleB(secConfig, SECTION_FEE_NICKNAME_CREATE, strTemp))
|
||||
FEE_NICKNAME_CREATE = boost::lexical_cast<int>(strTemp);
|
||||
|
||||
if (sectionSingleB(secConfig, SECTION_FEE_OFFER, strTemp))
|
||||
FEE_OFFER = boost::lexical_cast<int>(strTemp);
|
||||
|
||||
if (sectionSingleB(secConfig, SECTION_FEE_DEFAULT, strTemp))
|
||||
FEE_DEFAULT = boost::lexical_cast<int>(strTemp);
|
||||
|
||||
if (sectionSingleB(secConfig, SECTION_FEE_OPERATION, strTemp))
|
||||
FEE_CONTRACT_OPERATION = boost::lexical_cast<int>(strTemp);
|
||||
|
||||
if (sectionSingleB(secConfig, SECTION_FULL_HISTORY, strTemp))
|
||||
FULL_HISTORY = boost::lexical_cast<bool>(strTemp);
|
||||
|
||||
if (sectionSingleB(secConfig, SECTION_ACCOUNT_PROBE_MAX, strTemp))
|
||||
ACCOUNT_PROBE_MAX = boost::lexical_cast<int>(strTemp);
|
||||
|
||||
if (sectionSingleB(secConfig, SECTION_UNL_DEFAULT, strTemp))
|
||||
UNL_DEFAULT = strTemp;
|
||||
|
||||
if (sectionSingleB(secConfig, SECTION_DEBUG_LOGFILE, strTemp))
|
||||
DEBUG_LOGFILE = strTemp;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// vim:ts=4
|
||||
122
src/cpp/ripple/Config.h
Normal file
122
src/cpp/ripple/Config.h
Normal file
@@ -0,0 +1,122 @@
|
||||
#ifndef __CONFIG__
|
||||
#define __CONFIG__
|
||||
|
||||
#include "types.h"
|
||||
#include "RippleAddress.h"
|
||||
#include "ParseSection.h"
|
||||
#include "SerializedTypes.h"
|
||||
|
||||
#include <string>
|
||||
#include <boost/filesystem.hpp>
|
||||
|
||||
#define SYSTEM_NAME "ripple"
|
||||
#define SYSTEM_CURRENCY_CODE "XRP"
|
||||
#define SYSTEM_CURRENCY_PRECISION 6
|
||||
#define SYSTEM_CURRENCY_CODE_RIPPLE "XRR"
|
||||
|
||||
#define SYSTEM_CURRENCY_GIFT 1000ull
|
||||
#define SYSTEM_CURRENCY_USERS 100000000ull
|
||||
#define SYSTEM_CURRENCY_PARTS 1000000ull // 10^SYSTEM_CURRENCY_PRECISION
|
||||
#define SYSTEM_CURRENCY_START (SYSTEM_CURRENCY_GIFT*SYSTEM_CURRENCY_USERS*SYSTEM_CURRENCY_PARTS)
|
||||
|
||||
#define CONFIG_FILE_NAME SYSTEM_NAME "d.cfg" // rippled.cfg
|
||||
|
||||
#define DEFAULT_VALIDATORS_SITE "redstem.com"
|
||||
#define VALIDATORS_FILE_NAME "validators.txt"
|
||||
|
||||
const int SYSTEM_PEER_PORT = 6561;
|
||||
const int SYSTEM_WEBSOCKET_PORT = 6562;
|
||||
const int SYSTEM_WEBSOCKET_PUBLIC_PORT = 6563; // XXX Going away.
|
||||
|
||||
// Allow anonymous DH.
|
||||
#define DEFAULT_PEER_SSL_CIPHER_LIST "ALL:!LOW:!EXP:!MD5:@STRENGTH"
|
||||
|
||||
// Normal, recommend 1 hour.
|
||||
// #define DEFAULT_PEER_SCAN_INTERVAL_MIN (60*60)
|
||||
// Testing, recommend 1 minute.
|
||||
#define DEFAULT_PEER_SCAN_INTERVAL_MIN (60)
|
||||
|
||||
// Maximum number of peers to try to connect to as client at once.
|
||||
#define DEFAULT_PEER_START_MAX 5
|
||||
|
||||
// Might connect with fewer for testing.
|
||||
#define DEFAULT_PEER_CONNECT_LOW_WATER 4
|
||||
|
||||
class Config
|
||||
{
|
||||
public:
|
||||
// Configuration parameters
|
||||
boost::filesystem::path CONFIG_FILE;
|
||||
boost::filesystem::path CONFIG_DIR;
|
||||
boost::filesystem::path DATA_DIR;
|
||||
boost::filesystem::path DEBUG_LOGFILE;
|
||||
boost::filesystem::path UNL_DEFAULT;
|
||||
|
||||
std::string VALIDATORS_SITE; // Where to find validators.txt on the Internet.
|
||||
std::vector<std::string> VALIDATORS; // Validators from rippled.cfg.
|
||||
std::vector<std::string> IPS; // Peer IPs from rippled.cfg.
|
||||
std::vector<std::string> SNTP_SERVERS; // SNTP servers from rippled.cfg.
|
||||
|
||||
enum StartUpType { FRESH, NORMAL, LOAD, NETWORK };
|
||||
StartUpType START_UP;
|
||||
|
||||
// Network parameters
|
||||
int NETWORK_START_TIME; // The Unix time we start ledger 0.
|
||||
int TRANSACTION_FEE_BASE;
|
||||
int LEDGER_SECONDS;
|
||||
int LEDGER_PROPOSAL_DELAY_SECONDS;
|
||||
int LEDGER_AVALANCHE_SECONDS;
|
||||
bool LEDGER_CREATOR; // should be false unless we are starting a new ledger
|
||||
bool RUN_STANDALONE;
|
||||
|
||||
// Note: The following parameters do not relate to the UNL or trust at all
|
||||
unsigned int NETWORK_QUORUM; // Minimum number of nodes to consider the network present
|
||||
int VALIDATION_QUORUM; // Minimum validations to consider ledger authoritative
|
||||
|
||||
// Peer networking parameters
|
||||
std::string PEER_IP;
|
||||
int PEER_PORT;
|
||||
int NUMBER_CONNECTIONS;
|
||||
std::string PEER_SSL_CIPHER_LIST;
|
||||
int PEER_SCAN_INTERVAL_MIN;
|
||||
int PEER_START_MAX;
|
||||
unsigned int PEER_CONNECT_LOW_WATER;
|
||||
|
||||
// Websocket networking parameters
|
||||
std::string WEBSOCKET_PUBLIC_IP; // XXX Going away. Merge with the inbound peer connction.
|
||||
int WEBSOCKET_PUBLIC_PORT;
|
||||
|
||||
std::string WEBSOCKET_IP;
|
||||
int WEBSOCKET_PORT;
|
||||
|
||||
// RPC parameters
|
||||
std::string RPC_IP;
|
||||
int RPC_PORT;
|
||||
std::string RPC_USER;
|
||||
std::string RPC_PASSWORD;
|
||||
bool RPC_ALLOW_REMOTE;
|
||||
|
||||
// Validation
|
||||
RippleAddress VALIDATION_SEED, VALIDATION_PUB, VALIDATION_PRIV;
|
||||
|
||||
// Fees
|
||||
uint64 FEE_DEFAULT; // Default fee.
|
||||
uint64 FEE_ACCOUNT_CREATE; // Fee to create an account.
|
||||
uint64 FEE_NICKNAME_CREATE; // Fee to create a nickname.
|
||||
uint64 FEE_OFFER; // Rate per day.
|
||||
int FEE_CONTRACT_OPERATION; // fee for each contract operation
|
||||
|
||||
// Node storage configuration
|
||||
bool FULL_HISTORY;
|
||||
|
||||
// Client behavior
|
||||
int ACCOUNT_PROBE_MAX; // How far to scan for accounts.
|
||||
|
||||
void setup(const std::string& strConf);
|
||||
void load();
|
||||
};
|
||||
|
||||
extern Config theConfig;
|
||||
#endif
|
||||
|
||||
// vim:ts=4
|
||||
764
src/cpp/ripple/ConnectionPool.cpp
Normal file
764
src/cpp/ripple/ConnectionPool.cpp
Normal file
@@ -0,0 +1,764 @@
|
||||
|
||||
#include "ConnectionPool.h"
|
||||
|
||||
#include <boost/asio.hpp>
|
||||
#include <boost/bind.hpp>
|
||||
#include <boost/foreach.hpp>
|
||||
#include <boost/format.hpp>
|
||||
#include <boost/algorithm/string.hpp>
|
||||
#include <algorithm>
|
||||
|
||||
#include "Config.h"
|
||||
#include "Peer.h"
|
||||
#include "Application.h"
|
||||
#include "utils.h"
|
||||
#include "Log.h"
|
||||
|
||||
SETUP_LOG();
|
||||
|
||||
// How often to enforce policies.
|
||||
#define POLICY_INTERVAL_SECONDS 5
|
||||
|
||||
void splitIpPort(const std::string& strIpPort, std::string& strIp, int& iPort)
|
||||
{
|
||||
std::vector<std::string> vIpPort;
|
||||
boost::split(vIpPort, strIpPort, boost::is_any_of(" "));
|
||||
|
||||
strIp = vIpPort[0];
|
||||
iPort = boost::lexical_cast<int>(vIpPort[1]);
|
||||
}
|
||||
|
||||
ConnectionPool::ConnectionPool(boost::asio::io_service& io_service) :
|
||||
mLastPeer(0),
|
||||
mCtx(boost::asio::ssl::context::sslv23),
|
||||
mScanTimer(io_service),
|
||||
mPolicyTimer(io_service)
|
||||
{
|
||||
mCtx.set_options(
|
||||
boost::asio::ssl::context::default_workarounds
|
||||
| boost::asio::ssl::context::no_sslv2
|
||||
| boost::asio::ssl::context::single_dh_use);
|
||||
|
||||
if (1 != SSL_CTX_set_cipher_list(mCtx.native_handle(), theConfig.PEER_SSL_CIPHER_LIST.c_str()))
|
||||
std::runtime_error("Error setting cipher list (no valid ciphers).");
|
||||
}
|
||||
|
||||
void ConnectionPool::start()
|
||||
{
|
||||
if (theConfig.RUN_STANDALONE)
|
||||
return;
|
||||
|
||||
// Start running policy.
|
||||
policyEnforce();
|
||||
|
||||
// Start scanning.
|
||||
scanRefresh();
|
||||
}
|
||||
|
||||
bool ConnectionPool::getTopNAddrs(int n,std::vector<std::string>& addrs)
|
||||
{
|
||||
// XXX Filter out other local addresses (like ipv6)
|
||||
Database* db = theApp->getWalletDB()->getDB();
|
||||
ScopedLock sl(theApp->getWalletDB()->getDBLock());
|
||||
|
||||
SQL_FOREACH(db, str(boost::format("SELECT IpPort FROM PeerIps LIMIT %d") % n) )
|
||||
{
|
||||
std::string str;
|
||||
|
||||
db->getStr(0,str);
|
||||
|
||||
addrs.push_back(str);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ConnectionPool::savePeer(const std::string& strIp, int iPort, char code)
|
||||
{
|
||||
bool bNew = false;
|
||||
|
||||
Database* db = theApp->getWalletDB()->getDB();
|
||||
|
||||
std::string ipPort = sqlEscape(str(boost::format("%s %d") % strIp % iPort));
|
||||
|
||||
ScopedLock sl(theApp->getWalletDB()->getDBLock());
|
||||
std::string sql = str(boost::format("SELECT COUNT(*) FROM PeerIps WHERE IpPort=%s;") % ipPort);
|
||||
if (db->executeSQL(sql) && db->startIterRows())
|
||||
{
|
||||
if (!db->getInt(0))
|
||||
{
|
||||
db->executeSQL(str(boost::format("INSERT INTO PeerIps (IpPort,Score,Source) values (%s,0,'%c');") % ipPort % code));
|
||||
bNew = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// We already had this peer.
|
||||
// We will eventually verify its address if it is possible.
|
||||
// YYY If it is vsInbound, then we might make verification immediate so we can connect back sooner if the connection
|
||||
// is lost.
|
||||
nothing();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cout << "Error saving Peer" << std::endl;
|
||||
}
|
||||
|
||||
if (bNew)
|
||||
scanRefresh();
|
||||
|
||||
return bNew;
|
||||
}
|
||||
|
||||
Peer::pointer ConnectionPool::getPeerById(const uint64& id)
|
||||
{
|
||||
boost::mutex::scoped_lock sl(mPeerLock);
|
||||
const boost::unordered_map<uint64, Peer::pointer>::iterator& it = mPeerIdMap.find(id);
|
||||
if (it == mPeerIdMap.end())
|
||||
return Peer::pointer();
|
||||
return it->second;
|
||||
}
|
||||
|
||||
bool ConnectionPool::hasPeer(const uint64& id)
|
||||
{
|
||||
boost::mutex::scoped_lock sl(mPeerLock);
|
||||
return mPeerIdMap.find(id) != mPeerIdMap.end();
|
||||
}
|
||||
|
||||
// An available peer is one we had no trouble connect to last time and that we are not currently knowingly connected or connecting
|
||||
// too.
|
||||
//
|
||||
// <-- true, if a peer is available to connect to
|
||||
bool ConnectionPool::peerAvailable(std::string& strIp, int& iPort)
|
||||
{
|
||||
Database* db = theApp->getWalletDB()->getDB();
|
||||
std::vector<std::string> vstrIpPort;
|
||||
|
||||
// Convert mIpMap (list of open connections) to a vector of "<ip> <port>".
|
||||
{
|
||||
boost::mutex::scoped_lock sl(mPeerLock);
|
||||
|
||||
vstrIpPort.reserve(mIpMap.size());
|
||||
|
||||
BOOST_FOREACH(pipPeer ipPeer, mIpMap)
|
||||
{
|
||||
const std::string& strIp = ipPeer.first.first;
|
||||
int iPort = ipPeer.first.second;
|
||||
|
||||
vstrIpPort.push_back(sqlEscape(str(boost::format("%s %d") % strIp % iPort)));
|
||||
}
|
||||
}
|
||||
|
||||
// Get the first IpPort entry which is not in vector and which is not scheduled for scanning.
|
||||
std::string strIpPort;
|
||||
|
||||
{
|
||||
ScopedLock sl(theApp->getWalletDB()->getDBLock());
|
||||
|
||||
if (db->executeSQL(str(boost::format("SELECT IpPort FROM PeerIps WHERE ScanNext IS NULL AND IpPort NOT IN (%s) LIMIT 1;")
|
||||
% strJoin(vstrIpPort.begin(), vstrIpPort.end(), ",")))
|
||||
&& db->startIterRows())
|
||||
{
|
||||
strIpPort = db->getStrBinary("IpPort");
|
||||
}
|
||||
}
|
||||
|
||||
bool bAvailable = !strIpPort.empty();
|
||||
|
||||
if (bAvailable)
|
||||
splitIpPort(strIpPort, strIp, iPort);
|
||||
|
||||
return bAvailable;
|
||||
}
|
||||
|
||||
// Make sure we have at least low water connections.
|
||||
void ConnectionPool::policyLowWater()
|
||||
{
|
||||
std::string strIp;
|
||||
int iPort;
|
||||
|
||||
// Find an entry to connect to.
|
||||
if (mConnectedMap.size() > theConfig.PEER_CONNECT_LOW_WATER)
|
||||
{
|
||||
// Above low water mark, don't need more connections.
|
||||
cLog(lsTRACE) << "Pool: Low water: sufficient connections: " << mConnectedMap.size() << "/" << theConfig.PEER_CONNECT_LOW_WATER;
|
||||
|
||||
nothing();
|
||||
}
|
||||
#if 0
|
||||
else if (miConnectStarting == theConfig.PEER_START_MAX)
|
||||
{
|
||||
// Too many connections starting to start another.
|
||||
nothing();
|
||||
}
|
||||
#endif
|
||||
else if (!peerAvailable(strIp, iPort))
|
||||
{
|
||||
// No more connections available to start.
|
||||
cLog(lsTRACE) << "Pool: Low water: no peers available.";
|
||||
|
||||
// XXX Might ask peers for more ips.
|
||||
nothing();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Try to start connection.
|
||||
cLog(lsTRACE) << "Pool: Low water: start connection.";
|
||||
|
||||
if (!peerConnect(strIp, iPort))
|
||||
{
|
||||
cLog(lsINFO) << "Pool: Low water: already connected.";
|
||||
}
|
||||
|
||||
// Check if we need more.
|
||||
policyLowWater();
|
||||
}
|
||||
}
|
||||
|
||||
void ConnectionPool::policyEnforce()
|
||||
{
|
||||
// Cancel any in progress timer.
|
||||
(void) mPolicyTimer.cancel();
|
||||
|
||||
// Enforce policies.
|
||||
policyLowWater();
|
||||
|
||||
// Schedule next enforcement.
|
||||
mPolicyTimer.expires_at(boost::posix_time::second_clock::universal_time()+boost::posix_time::seconds(POLICY_INTERVAL_SECONDS));
|
||||
mPolicyTimer.async_wait(boost::bind(&ConnectionPool::policyHandler, this, _1));
|
||||
}
|
||||
|
||||
void ConnectionPool::policyHandler(const boost::system::error_code& ecResult)
|
||||
{
|
||||
if (ecResult == boost::asio::error::operation_aborted)
|
||||
{
|
||||
nothing();
|
||||
}
|
||||
else if (!ecResult)
|
||||
{
|
||||
policyEnforce();
|
||||
}
|
||||
else
|
||||
{
|
||||
throw std::runtime_error("Internal error: unexpected deadline error.");
|
||||
}
|
||||
}
|
||||
|
||||
// YYY: Should probably do this in the background.
|
||||
// YYY: Might end up sending to disconnected peer?
|
||||
int ConnectionPool::relayMessage(Peer* fromPeer, const PackedMessage::pointer& msg)
|
||||
{
|
||||
int sentTo = 0;
|
||||
boost::mutex::scoped_lock sl(mPeerLock);
|
||||
|
||||
BOOST_FOREACH(naPeer pair, mConnectedMap)
|
||||
{
|
||||
Peer::ref peer = pair.second;
|
||||
if (!peer)
|
||||
std::cerr << "CP::RM null peer in list" << std::endl;
|
||||
else if ((!fromPeer || !(peer.get() == fromPeer)) && peer->isConnected())
|
||||
{
|
||||
++sentTo;
|
||||
peer->sendPacket(msg);
|
||||
}
|
||||
}
|
||||
|
||||
return sentTo;
|
||||
}
|
||||
|
||||
void ConnectionPool::relayMessageBut(const std::set<uint64>& fromPeers, const PackedMessage::pointer& msg)
|
||||
{ // Relay message to all but the specified peers
|
||||
boost::mutex::scoped_lock sl(mPeerLock);
|
||||
|
||||
BOOST_FOREACH(naPeer pair, mConnectedMap)
|
||||
{
|
||||
Peer::ref peer = pair.second;
|
||||
if (peer->isConnected() && (fromPeers.count(peer->getPeerId()) == 0))
|
||||
peer->sendPacket(msg);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void ConnectionPool::relayMessageTo(const std::set<uint64>& fromPeers, const PackedMessage::pointer& msg)
|
||||
{ // Relay message to the specified peers
|
||||
boost::mutex::scoped_lock sl(mPeerLock);
|
||||
|
||||
BOOST_FOREACH(const uint64& peerID, fromPeers)
|
||||
{
|
||||
const boost::unordered_map<uint64, Peer::pointer>::iterator& it = mPeerIdMap.find(peerID);
|
||||
if ((it != mPeerIdMap.end()) && it->second->isConnected())
|
||||
it->second->sendPacket(msg);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Schedule a connection via scanning.
|
||||
//
|
||||
// Add or modify into PeerIps as a manual entry for immediate scanning.
|
||||
// Requires sane IP and port.
|
||||
void ConnectionPool::connectTo(const std::string& strIp, int iPort)
|
||||
{
|
||||
if (theConfig.RUN_STANDALONE)
|
||||
return;
|
||||
{
|
||||
Database* db = theApp->getWalletDB()->getDB();
|
||||
ScopedLock sl(theApp->getWalletDB()->getDBLock());
|
||||
|
||||
db->executeSQL(str(boost::format("REPLACE INTO PeerIps (IpPort,Score,Source,ScanNext) values (%s,%d,'%c',0);")
|
||||
% sqlEscape(str(boost::format("%s %d") % strIp % iPort))
|
||||
% theApp->getUNL().iSourceScore(UniqueNodeList::vsManual)
|
||||
% char(UniqueNodeList::vsManual)));
|
||||
}
|
||||
|
||||
scanRefresh();
|
||||
}
|
||||
|
||||
// Start a connection, if not already known connected or connecting.
|
||||
//
|
||||
// <-- true, if already connected.
|
||||
Peer::pointer ConnectionPool::peerConnect(const std::string& strIp, int iPort)
|
||||
{
|
||||
ipPort pipPeer = make_pair(strIp, iPort);
|
||||
Peer::pointer ppResult;
|
||||
|
||||
boost::unordered_map<ipPort, Peer::pointer>::iterator it;
|
||||
|
||||
{
|
||||
boost::mutex::scoped_lock sl(mPeerLock);
|
||||
|
||||
if ((it = mIpMap.find(pipPeer)) == mIpMap.end())
|
||||
{
|
||||
Peer::pointer ppNew(Peer::create(theApp->getIOService(), mCtx, ++mLastPeer));
|
||||
|
||||
// Did not find it. Not already connecting or connected.
|
||||
ppNew->connect(strIp, iPort);
|
||||
|
||||
mIpMap[pipPeer] = ppNew;
|
||||
|
||||
ppResult = ppNew;
|
||||
// ++miConnectStarting;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Found it. Already connected.
|
||||
|
||||
nothing();
|
||||
}
|
||||
}
|
||||
|
||||
if (ppResult)
|
||||
{
|
||||
//cLog(lsINFO) << "Pool: Connecting: " << ADDRESS_SHARED(ppResult) << ": " << strIp << " " << iPort;
|
||||
}
|
||||
else
|
||||
{
|
||||
//cLog(lsINFO) << "Pool: Already connected: " << strIp << " " << iPort;
|
||||
}
|
||||
|
||||
return ppResult;
|
||||
}
|
||||
|
||||
// Returns information on verified peers.
|
||||
Json::Value ConnectionPool::getPeersJson()
|
||||
{
|
||||
Json::Value ret(Json::arrayValue);
|
||||
std::vector<Peer::pointer> vppPeers = getPeerVector();
|
||||
|
||||
BOOST_FOREACH(Peer::pointer peer, vppPeers)
|
||||
{
|
||||
ret.append(peer->getJson());
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int ConnectionPool::getPeerCount()
|
||||
{
|
||||
boost::mutex::scoped_lock sl(mPeerLock);
|
||||
|
||||
return mConnectedMap.size();
|
||||
}
|
||||
|
||||
std::vector<Peer::pointer> ConnectionPool::getPeerVector()
|
||||
{
|
||||
std::vector<Peer::pointer> ret;
|
||||
|
||||
boost::mutex::scoped_lock sl(mPeerLock);
|
||||
|
||||
ret.reserve(mConnectedMap.size());
|
||||
|
||||
BOOST_FOREACH(naPeer pair, mConnectedMap)
|
||||
{
|
||||
assert(!!pair.second);
|
||||
ret.push_back(pair.second);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
uint64 ConnectionPool::assignPeerId()
|
||||
{
|
||||
boost::mutex::scoped_lock sl(mPeerLock);
|
||||
return ++mLastPeer;
|
||||
}
|
||||
|
||||
// Now know peer's node public key. Determine if we want to stay connected.
|
||||
// <-- bNew: false = redundant
|
||||
bool ConnectionPool::peerConnected(Peer::ref peer, const RippleAddress& naPeer,
|
||||
const std::string& strIP, int iPort)
|
||||
{
|
||||
bool bNew = false;
|
||||
|
||||
assert(!!peer);
|
||||
|
||||
if (naPeer == theApp->getWallet().getNodePublic())
|
||||
{
|
||||
cLog(lsINFO) << "Pool: Connected: self: " << ADDRESS_SHARED(peer) << ": " << naPeer.humanNodePublic() << " " << strIP << " " << iPort;
|
||||
}
|
||||
else
|
||||
{
|
||||
boost::mutex::scoped_lock sl(mPeerLock);
|
||||
const boost::unordered_map<RippleAddress, Peer::pointer>::iterator& itCm = mConnectedMap.find(naPeer);
|
||||
|
||||
if (itCm == mConnectedMap.end())
|
||||
{
|
||||
// New connection.
|
||||
//cLog(lsINFO) << "Pool: Connected: new: " << ADDRESS_SHARED(peer) << ": " << naPeer.humanNodePublic() << " " << strIP << " " << iPort;
|
||||
|
||||
mConnectedMap[naPeer] = peer;
|
||||
bNew = true;
|
||||
|
||||
assert(peer->getPeerId() != 0);
|
||||
mPeerIdMap.insert(std::make_pair(peer->getPeerId(), peer));
|
||||
}
|
||||
// Found in map, already connected.
|
||||
else if (!strIP.empty())
|
||||
{
|
||||
// Was an outbound connection, we know IP and port.
|
||||
// Note in previous connection how to reconnect.
|
||||
if (itCm->second->getIP().empty())
|
||||
{
|
||||
// Old peer did not know it's IP.
|
||||
//cLog(lsINFO) << "Pool: Connected: redundant: outbound: " << ADDRESS_SHARED(peer) << " discovered: " << ADDRESS_SHARED(itCm->second) << ": " << strIP << " " << iPort;
|
||||
|
||||
itCm->second->setIpPort(strIP, iPort);
|
||||
|
||||
// Add old connection to identified connection list.
|
||||
mIpMap[make_pair(strIP, iPort)] = itCm->second;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Old peer knew its IP. Do nothing.
|
||||
//cLog(lsINFO) << "Pool: Connected: redundant: outbound: rediscovered: " << ADDRESS_SHARED(peer) << " " << strIP << " " << iPort;
|
||||
|
||||
nothing();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//cLog(lsINFO) << "Pool: Connected: redundant: inbound: " << ADDRESS_SHARED(peer) << " " << strIP << " " << iPort;
|
||||
|
||||
nothing();
|
||||
}
|
||||
}
|
||||
|
||||
return bNew;
|
||||
}
|
||||
|
||||
// We maintain a map of public key to peer for connected and verified peers. Maintain it.
|
||||
void ConnectionPool::peerDisconnected(Peer::ref peer, const RippleAddress& naPeer)
|
||||
{
|
||||
boost::mutex::scoped_lock sl(mPeerLock);
|
||||
|
||||
if (naPeer.isValid())
|
||||
{
|
||||
const boost::unordered_map<RippleAddress, Peer::pointer>::iterator& itCm = mConnectedMap.find(naPeer);
|
||||
|
||||
if (itCm == mConnectedMap.end())
|
||||
{
|
||||
// Did not find it. Not already connecting or connected.
|
||||
cLog(lsWARNING) << "Pool: disconnected: Internal Error: mConnectedMap was inconsistent.";
|
||||
// XXX Maybe bad error, considering we have racing connections, may not so bad.
|
||||
}
|
||||
else if (itCm->second != peer)
|
||||
{
|
||||
cLog(lsWARNING) << "Pool: disconected: non canonical entry";
|
||||
|
||||
nothing();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Found it. Delete it.
|
||||
mConnectedMap.erase(itCm);
|
||||
|
||||
//cLog(lsINFO) << "Pool: disconnected: " << naPeer.humanNodePublic() << " " << peer->getIP() << " " << peer->getPort();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//cLog(lsINFO) << "Pool: disconnected: anonymous: " << peer->getIP() << " " << peer->getPort();
|
||||
}
|
||||
|
||||
assert(peer->getPeerId() != 0);
|
||||
mPeerIdMap.erase(peer->getPeerId());
|
||||
}
|
||||
|
||||
// Schedule for immediate scanning, if not already scheduled.
|
||||
//
|
||||
// <-- true, scanRefresh needed.
|
||||
bool ConnectionPool::peerScanSet(const std::string& strIp, int iPort)
|
||||
{
|
||||
std::string strIpPort = str(boost::format("%s %d") % strIp % iPort);
|
||||
bool bScanDirty = false;
|
||||
|
||||
ScopedLock sl(theApp->getWalletDB()->getDBLock());
|
||||
Database* db = theApp->getWalletDB()->getDB();
|
||||
|
||||
if (db->executeSQL(str(boost::format("SELECT ScanNext FROM PeerIps WHERE IpPort=%s;")
|
||||
% sqlEscape(strIpPort)))
|
||||
&& db->startIterRows())
|
||||
{
|
||||
if (db->getNull("ScanNext"))
|
||||
{
|
||||
// Non-scanning connection terminated. Schedule for scanning.
|
||||
int iInterval = theConfig.PEER_SCAN_INTERVAL_MIN;
|
||||
boost::posix_time::ptime tpNow = boost::posix_time::second_clock::universal_time();
|
||||
boost::posix_time::ptime tpNext = tpNow + boost::posix_time::seconds(iInterval);
|
||||
|
||||
//cLog(lsINFO) << str(boost::format("Pool: Scan: schedule create: %s %s (next %s, delay=%d)")
|
||||
// % mScanIp % mScanPort % tpNext % (tpNext-tpNow).total_seconds());
|
||||
|
||||
db->executeSQL(str(boost::format("UPDATE PeerIps SET ScanNext=%d,ScanInterval=%d WHERE IpPort=%s;")
|
||||
% iToSeconds(tpNext)
|
||||
% iInterval
|
||||
% db->escape(strIpPort)));
|
||||
|
||||
bScanDirty = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Scan connection terminated, already scheduled for retry.
|
||||
// boost::posix_time::ptime tpNow = boost::posix_time::second_clock::universal_time();
|
||||
// boost::posix_time::ptime tpNext = ptFromSeconds(db->getInt("ScanNext"));
|
||||
|
||||
//cLog(lsINFO) << str(boost::format("Pool: Scan: schedule exists: %s %s (next %s, delay=%d)")
|
||||
// % mScanIp % mScanPort % tpNext % (tpNext-tpNow).total_seconds());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//cLog(lsWARNING) << "Pool: Scan: peer wasn't in PeerIps: " << strIp << " " << iPort;
|
||||
}
|
||||
|
||||
return bScanDirty;
|
||||
}
|
||||
|
||||
// --> strIp: not empty
|
||||
void ConnectionPool::peerClosed(Peer::ref peer, const std::string& strIp, int iPort)
|
||||
{
|
||||
ipPort ipPeer = make_pair(strIp, iPort);
|
||||
bool bScanRefresh = false;
|
||||
|
||||
// If the connection was our scan, we are no longer scanning.
|
||||
if (mScanning && mScanning == peer)
|
||||
{
|
||||
//cLog(lsINFO) << "Pool: Scan: scan fail: " << strIp << " " << iPort;
|
||||
|
||||
mScanning.reset(); // No longer scanning.
|
||||
bScanRefresh = true; // Look for more to scan.
|
||||
}
|
||||
|
||||
// Determine if closed peer was redundant.
|
||||
bool bRedundant = true;
|
||||
{
|
||||
boost::mutex::scoped_lock sl(mPeerLock);
|
||||
const boost::unordered_map<ipPort, Peer::pointer>::iterator& itIp = mIpMap.find(ipPeer);
|
||||
|
||||
if (itIp == mIpMap.end())
|
||||
{
|
||||
// Did not find it. Not already connecting or connected.
|
||||
cLog(lsWARNING) << "Pool: Closed: UNEXPECTED: " << ADDRESS_SHARED(peer) << ": " << strIp << " " << iPort;
|
||||
// XXX Internal error.
|
||||
}
|
||||
else if (mIpMap[ipPeer] == peer)
|
||||
{
|
||||
// We were the identified connection.
|
||||
//cLog(lsINFO) << "Pool: Closed: identified: " << ADDRESS_SHARED(peer) << ": " << strIp << " " << iPort;
|
||||
|
||||
// Delete our entry.
|
||||
mIpMap.erase(itIp);
|
||||
|
||||
bRedundant = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Found it. But, we were redundant.
|
||||
//cLog(lsINFO) << "Pool: Closed: redundant: " << ADDRESS_SHARED(peer) << ": " << strIp << " " << iPort;
|
||||
}
|
||||
}
|
||||
|
||||
if (!bRedundant)
|
||||
{
|
||||
// If closed was not redundant schedule if not already scheduled.
|
||||
bScanRefresh = peerScanSet(ipPeer.first, ipPeer.second) || bScanRefresh;
|
||||
}
|
||||
|
||||
if (bScanRefresh)
|
||||
scanRefresh();
|
||||
}
|
||||
|
||||
void ConnectionPool::peerVerified(Peer::ref peer)
|
||||
{
|
||||
if (mScanning && mScanning == peer)
|
||||
{
|
||||
// Scan completed successfully.
|
||||
std::string strIp = peer->getIP();
|
||||
int iPort = peer->getPort();
|
||||
|
||||
std::string strIpPort = str(boost::format("%s %d") % strIp % iPort);
|
||||
|
||||
//cLog(lsINFO) << str(boost::format("Pool: Scan: connected: %s %s %s (scanned)") % ADDRESS_SHARED(peer) % strIp % iPort);
|
||||
|
||||
if (peer->getNodePublic() == theApp->getWallet().getNodePublic())
|
||||
{
|
||||
// Talking to ourself. We will just back off. This lets us maybe advertise our outside address.
|
||||
|
||||
nothing(); // Do nothing, leave scheduled scanning.
|
||||
}
|
||||
else
|
||||
{
|
||||
// Talking with a different peer.
|
||||
ScopedLock sl(theApp->getWalletDB()->getDBLock());
|
||||
Database *db=theApp->getWalletDB()->getDB();
|
||||
|
||||
db->executeSQL(str(boost::format("UPDATE PeerIps SET ScanNext=NULL,ScanInterval=0 WHERE IpPort=%s;")
|
||||
% db->escape(strIpPort)));
|
||||
// XXX Check error.
|
||||
}
|
||||
|
||||
mScanning.reset();
|
||||
|
||||
scanRefresh(); // Continue scanning.
|
||||
}
|
||||
}
|
||||
|
||||
void ConnectionPool::scanHandler(const boost::system::error_code& ecResult)
|
||||
{
|
||||
if (ecResult == boost::asio::error::operation_aborted)
|
||||
{
|
||||
nothing();
|
||||
}
|
||||
else if (!ecResult)
|
||||
{
|
||||
scanRefresh();
|
||||
}
|
||||
else
|
||||
{
|
||||
throw std::runtime_error("Internal error: unexpected deadline error.");
|
||||
}
|
||||
}
|
||||
|
||||
// Scan ips as per db entries.
|
||||
void ConnectionPool::scanRefresh()
|
||||
{
|
||||
if (mScanning)
|
||||
{
|
||||
// Currently scanning, will scan again after completion.
|
||||
cLog(lsTRACE) << "Pool: Scan: already scanning";
|
||||
|
||||
nothing();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Discover if there are entries that need scanning.
|
||||
boost::posix_time::ptime tpNext;
|
||||
boost::posix_time::ptime tpNow;
|
||||
std::string strIpPort;
|
||||
int iInterval;
|
||||
|
||||
{
|
||||
ScopedLock sl(theApp->getWalletDB()->getDBLock());
|
||||
Database* db = theApp->getWalletDB()->getDB();
|
||||
|
||||
if (db->executeSQL("SELECT * FROM PeerIps INDEXED BY PeerScanIndex WHERE ScanNext NOT NULL ORDER BY ScanNext LIMIT 1;")
|
||||
&& db->startIterRows())
|
||||
{
|
||||
// Have an entry to scan.
|
||||
int iNext = db->getInt("ScanNext");
|
||||
|
||||
tpNext = ptFromSeconds(iNext);
|
||||
tpNow = boost::posix_time::second_clock::universal_time();
|
||||
|
||||
db->getStr("IpPort", strIpPort);
|
||||
iInterval = db->getInt("ScanInterval");
|
||||
}
|
||||
else
|
||||
{
|
||||
// No entries to scan.
|
||||
tpNow = boost::posix_time::ptime(boost::posix_time::not_a_date_time);
|
||||
}
|
||||
}
|
||||
|
||||
if (tpNow.is_not_a_date_time())
|
||||
{
|
||||
//cLog(lsINFO) << "Pool: Scan: stop.";
|
||||
|
||||
(void) mScanTimer.cancel();
|
||||
}
|
||||
else if (tpNext <= tpNow)
|
||||
{
|
||||
// Scan it.
|
||||
splitIpPort(strIpPort, mScanIp, mScanPort);
|
||||
|
||||
(void) mScanTimer.cancel();
|
||||
|
||||
iInterval = std::max(iInterval, theConfig.PEER_SCAN_INTERVAL_MIN);
|
||||
|
||||
tpNext = tpNow + boost::posix_time::seconds(iInterval);
|
||||
|
||||
//cLog(lsINFO) << str(boost::format("Pool: Scan: Now: %s %s (next %s, delay=%d)")
|
||||
// % mScanIp % mScanPort % tpNext % (tpNext-tpNow).total_seconds());
|
||||
|
||||
iInterval *= 2;
|
||||
|
||||
{
|
||||
ScopedLock sl(theApp->getWalletDB()->getDBLock());
|
||||
Database *db=theApp->getWalletDB()->getDB();
|
||||
|
||||
db->executeSQL(str(boost::format("UPDATE PeerIps SET ScanNext=%d,ScanInterval=%d WHERE IpPort=%s;")
|
||||
% iToSeconds(tpNext)
|
||||
% iInterval
|
||||
% db->escape(strIpPort)));
|
||||
// XXX Check error.
|
||||
}
|
||||
|
||||
mScanning = peerConnect(mScanIp, mScanPort);
|
||||
if (!mScanning)
|
||||
{
|
||||
// Already connected. Try again.
|
||||
scanRefresh();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//cLog(lsINFO) << str(boost::format("Pool: Scan: Next: %s (next %s, delay=%d)")
|
||||
// % strIpPort % tpNext % (tpNext-tpNow).total_seconds());
|
||||
|
||||
mScanTimer.expires_at(tpNext);
|
||||
mScanTimer.async_wait(boost::bind(&ConnectionPool::scanHandler, this, _1));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if 0
|
||||
bool ConnectionPool::isMessageKnown(PackedMessage::pointer msg)
|
||||
{
|
||||
for(unsigned int n=0; n<mBroadcastMessages.size(); n++)
|
||||
{
|
||||
if(msg==mBroadcastMessages[n].first) return(false);
|
||||
}
|
||||
return(false);
|
||||
}
|
||||
#endif
|
||||
|
||||
// vim:ts=4
|
||||
120
src/cpp/ripple/ConnectionPool.h
Normal file
120
src/cpp/ripple/ConnectionPool.h
Normal file
@@ -0,0 +1,120 @@
|
||||
#ifndef __CONNECTION_POOL__
|
||||
#define __CONNECTION_POOL__
|
||||
|
||||
#include <set>
|
||||
|
||||
#include <boost/asio/ssl.hpp>
|
||||
#include <boost/thread/mutex.hpp>
|
||||
|
||||
#include "Peer.h"
|
||||
#include "PackedMessage.h"
|
||||
#include "types.h"
|
||||
|
||||
//
|
||||
// Access to the Ripple network.
|
||||
//
|
||||
class ConnectionPool
|
||||
{
|
||||
private:
|
||||
boost::mutex mPeerLock;
|
||||
uint64 mLastPeer;
|
||||
|
||||
typedef std::pair<RippleAddress, Peer::pointer> naPeer;
|
||||
typedef std::pair<ipPort, Peer::pointer> pipPeer;
|
||||
|
||||
// Peers we are connecting with and non-thin peers we are connected to.
|
||||
// Only peers we know the connection ip for are listed.
|
||||
// We know the ip and port for:
|
||||
// - All outbound connections
|
||||
// - Some inbound connections (which we figured out).
|
||||
boost::unordered_map<ipPort, Peer::pointer> mIpMap;
|
||||
|
||||
// Non-thin peers which we are connected to.
|
||||
// Peers we have the public key for.
|
||||
boost::unordered_map<RippleAddress, Peer::pointer> mConnectedMap;
|
||||
|
||||
// Connections with have a 64-bit identifier
|
||||
boost::unordered_map<uint64, Peer::pointer> mPeerIdMap;
|
||||
|
||||
boost::asio::ssl::context mCtx;
|
||||
|
||||
Peer::pointer mScanning;
|
||||
boost::asio::deadline_timer mScanTimer;
|
||||
std::string mScanIp;
|
||||
int mScanPort;
|
||||
|
||||
void scanHandler(const boost::system::error_code& ecResult);
|
||||
|
||||
boost::asio::deadline_timer mPolicyTimer;
|
||||
|
||||
void policyHandler(const boost::system::error_code& ecResult);
|
||||
|
||||
// Peers we are establishing a connection with as a client.
|
||||
// int miConnectStarting;
|
||||
|
||||
bool peerAvailable(std::string& strIp, int& iPort);
|
||||
bool peerScanSet(const std::string& strIp, int iPort);
|
||||
|
||||
Peer::pointer peerConnect(const std::string& strIp, int iPort);
|
||||
|
||||
public:
|
||||
ConnectionPool(boost::asio::io_service& io_service);
|
||||
|
||||
// Begin enforcing connection policy.
|
||||
void start();
|
||||
|
||||
// Send message to network.
|
||||
int relayMessage(Peer* fromPeer, const PackedMessage::pointer& msg);
|
||||
void relayMessageTo(const std::set<uint64>& fromPeers, const PackedMessage::pointer& msg);
|
||||
void relayMessageBut(const std::set<uint64>& fromPeers, const PackedMessage::pointer& msg);
|
||||
|
||||
// Manual connection request.
|
||||
// Queue for immediate scanning.
|
||||
void connectTo(const std::string& strIp, int iPort);
|
||||
|
||||
//
|
||||
// Peer connectivity notification.
|
||||
//
|
||||
bool getTopNAddrs(int n,std::vector<std::string>& addrs);
|
||||
bool savePeer(const std::string& strIp, int iPort, char code);
|
||||
|
||||
// We know peers node public key.
|
||||
// <-- bool: false=reject
|
||||
bool peerConnected(Peer::ref peer, const RippleAddress& naPeer, const std::string& strIP, int iPort);
|
||||
|
||||
// No longer connected.
|
||||
void peerDisconnected(Peer::ref peer, const RippleAddress& naPeer);
|
||||
|
||||
// As client accepted.
|
||||
void peerVerified(Peer::ref peer);
|
||||
|
||||
// As client failed connect and be accepted.
|
||||
void peerClosed(Peer::ref peer, const std::string& strIp, int iPort);
|
||||
|
||||
int getPeerCount();
|
||||
Json::Value getPeersJson();
|
||||
std::vector<Peer::pointer> getPeerVector();
|
||||
|
||||
// Peer 64-bit ID function
|
||||
uint64 assignPeerId();
|
||||
Peer::pointer getPeerById(const uint64& id);
|
||||
bool hasPeer(const uint64& id);
|
||||
|
||||
//
|
||||
// Scanning
|
||||
//
|
||||
|
||||
void scanRefresh();
|
||||
|
||||
//
|
||||
// Connection policy
|
||||
//
|
||||
void policyLowWater();
|
||||
void policyEnforce();
|
||||
|
||||
};
|
||||
|
||||
extern void splitIpPort(const std::string& strIpPort, std::string& strIp, int& iPort);
|
||||
|
||||
#endif
|
||||
// vim:ts=4
|
||||
35
src/cpp/ripple/Contract.cpp
Normal file
35
src/cpp/ripple/Contract.cpp
Normal file
@@ -0,0 +1,35 @@
|
||||
#include "Contract.h"
|
||||
#include "Interpreter.h"
|
||||
|
||||
using namespace Script;
|
||||
/*
|
||||
JED: V III
|
||||
*/
|
||||
|
||||
Contract::Contract()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
void Contract::executeCreate()
|
||||
{
|
||||
|
||||
}
|
||||
void Contract::executeRemove()
|
||||
{
|
||||
|
||||
}
|
||||
void Contract::executeFund()
|
||||
{
|
||||
|
||||
}
|
||||
void Contract::executeAccept()
|
||||
{
|
||||
//std::vector<char> code;
|
||||
|
||||
//Interpreter interpreter;
|
||||
//interpreter.interpret(this,code);
|
||||
}
|
||||
|
||||
|
||||
30
src/cpp/ripple/Contract.h
Normal file
30
src/cpp/ripple/Contract.h
Normal file
@@ -0,0 +1,30 @@
|
||||
#ifndef __CONTRACT__
|
||||
#define __CONTRACT__
|
||||
|
||||
#include "SerializedLedger.h"
|
||||
#include <boost/shared_ptr.hpp>
|
||||
#include "ScriptData.h"
|
||||
/*
|
||||
Encapsulates the SLE for a Contract
|
||||
*/
|
||||
|
||||
class Contract
|
||||
{
|
||||
public:
|
||||
Contract();
|
||||
|
||||
uint160& getIssuer();
|
||||
uint160& getOwner();
|
||||
STAmount& getRippleEscrow();
|
||||
uint32 getEscrow();
|
||||
uint32 getBond();
|
||||
|
||||
Script::Data getData(int index);
|
||||
|
||||
void executeCreate();
|
||||
void executeRemove();
|
||||
void executeFund();
|
||||
void executeAccept();
|
||||
};
|
||||
|
||||
#endif
|
||||
288
src/cpp/ripple/DBInit.cpp
Normal file
288
src/cpp/ripple/DBInit.cpp
Normal file
@@ -0,0 +1,288 @@
|
||||
#include "utils.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
// Transaction database holds transactions and public keys
|
||||
const char *TxnDBInit[] = {
|
||||
"BEGIN TRANSACTION;",
|
||||
|
||||
"CREATE TABLE Transactions ( \
|
||||
TransID CHARACTER(64) PRIMARY KEY, \
|
||||
TransType CHARACTER(24), \
|
||||
FromAcct CHARACTER(35), \
|
||||
FromSeq BIGINT UNSIGNED, \
|
||||
LedgerSeq BIGINT UNSIGNED, \
|
||||
Status CHARACTER(1), \
|
||||
RawTxn BLOB \
|
||||
TxnMeta BLOB \
|
||||
);",
|
||||
"CREATE TABLE PubKeys ( \
|
||||
ID CHARACTER(35) PRIMARY KEY, \
|
||||
PubKey BLOB \
|
||||
);",
|
||||
"CREATE TABLE AccountTransactions ( \
|
||||
TransID CHARACTER(64), \
|
||||
Account CHARACTER(64), \
|
||||
LedgerSeq BIGINT UNSIGNED \
|
||||
);",
|
||||
"CREATE INDEX AcctTxindex ON \
|
||||
AccountTransactions(Account, LedgerSeq, TransID);",
|
||||
"CREATE INDEX AcctLgrIndex ON \
|
||||
AccountTransactions(LedgerSeq, Account, TransID);",
|
||||
|
||||
"END TRANSACTION;"
|
||||
};
|
||||
|
||||
int TxnDBCount = NUMBER(TxnDBInit);
|
||||
|
||||
// Ledger database holds ledgers and ledger confirmations
|
||||
const char *LedgerDBInit[] = {
|
||||
"BEGIN TRANSACTION;",
|
||||
|
||||
"CREATE TABLE Ledgers ( \
|
||||
LedgerHash CHARACTER(64) PRIMARY KEY, \
|
||||
LedgerSeq BIGINT UNSIGNED, \
|
||||
PrevHash CHARACTER(64), \
|
||||
TotalCoins BIGINT UNSIGNED, \
|
||||
ClosingTime BIGINT UNSIGNED, \
|
||||
PrevClosingTime BIGINT UNSIGNED, \
|
||||
CloseTimeRes BIGINT UNSIGNED, \
|
||||
CloseFlags BIGINT UNSIGNED, \
|
||||
AccountSetHash CHARACTER(64), \
|
||||
TransSetHash CHARACTER(64) \
|
||||
);",
|
||||
"CREATE INDEX SeqLedger ON Ledgers(LedgerSeq);",
|
||||
|
||||
"CREATE TABLE LedgerValidations ( \
|
||||
LedgerHash CHARACTER(64), \
|
||||
NodePubKey CHARACTER(56), \
|
||||
Flags BIGINT UNSIGNED, \
|
||||
SignTime BIGINT UNSIGNED, \
|
||||
Signature BLOB \
|
||||
);",
|
||||
"CREATE INDEX ValidationByHash ON \
|
||||
LedgerValidations(LedgerHash);",
|
||||
|
||||
"END TRANSACTION;"
|
||||
};
|
||||
|
||||
int LedgerDBCount = NUMBER(LedgerDBInit);
|
||||
|
||||
// RPC database holds persistent data for RPC clients.
|
||||
const char *RpcDBInit[] = {
|
||||
|
||||
// Local persistence of the RPC client
|
||||
"CREATE TABLE RPCData ( \
|
||||
Key TEXT PRIMARY Key, \
|
||||
Value TEXT \
|
||||
);",
|
||||
};
|
||||
|
||||
int RpcDBCount = NUMBER(RpcDBInit);
|
||||
|
||||
// Wallet database holds local accounts and trusted nodes
|
||||
const char *WalletDBInit[] = {
|
||||
// Node identity must be persisted for CAS routing and responsibilities.
|
||||
"BEGIN TRANSACTION;",
|
||||
|
||||
"CREATE TABLE NodeIdentity ( \
|
||||
PublicKey CHARACTER(53), \
|
||||
PrivateKey CHARACTER(52), \
|
||||
Dh512 TEXT, \
|
||||
Dh1024 TEXT \
|
||||
);",
|
||||
|
||||
// Miscellaneous persistent information
|
||||
// Integer: 1 : Used to simplify SQL.
|
||||
// ScoreUpdated: when scores was last updated.
|
||||
// FetchUpdated: when last fetch succeeded.
|
||||
"CREATE TABLE Misc ( \
|
||||
Magic INTEGER UNIQUE NOT NULL, \
|
||||
ScoreUpdated DATETIME, \
|
||||
FetchUpdated DATETIME \
|
||||
);",
|
||||
|
||||
// Scoring and other information for domains.
|
||||
//
|
||||
// Domain:
|
||||
// Domain source for https.
|
||||
// PublicKey:
|
||||
// Set if ever succeeded.
|
||||
// XXX Use NULL in place of ""
|
||||
// Source:
|
||||
// 'M' = Manually added. : 1500
|
||||
// 'V' = validators.txt : 1000
|
||||
// 'W' = Web browsing. : 200
|
||||
// 'R' = Referral : 0
|
||||
// Next:
|
||||
// Time of next fetch attempt.
|
||||
// Scan:
|
||||
// Time of last fetch attempt.
|
||||
// Fetch:
|
||||
// Time of last successful fetch.
|
||||
// Sha256:
|
||||
// Checksum of last fetch.
|
||||
// Comment:
|
||||
// User supplied comment.
|
||||
// Table of Domains user has asked to trust.
|
||||
"CREATE TABLE SeedDomains ( \
|
||||
Domain TEXT PRIMARY KEY NOT NULL, \
|
||||
PublicKey CHARACTER(53), \
|
||||
Source CHARACTER(1) NOT NULL, \
|
||||
Next DATETIME, \
|
||||
Scan DATETIME, \
|
||||
Fetch DATETIME, \
|
||||
Sha256 CHARACTER[64], \
|
||||
Comment TEXT \
|
||||
);",
|
||||
|
||||
// Allow us to easily find the next SeedDomain to fetch.
|
||||
"CREATE INDEX SeedDomainNext ON SeedDomains (Next);",
|
||||
|
||||
// Table of PublicKeys user has asked to trust.
|
||||
// Fetches are made to the CAS. This gets the ripple.txt so even validators without a web server can publish a ripple.txt.
|
||||
// Source:
|
||||
// 'M' = Manually added. : 1500
|
||||
// 'V' = validators.txt : 1000
|
||||
// 'W' = Web browsing. : 200
|
||||
// 'R' = Referral : 0
|
||||
// Next:
|
||||
// Time of next fetch attempt.
|
||||
// Scan:
|
||||
// Time of last fetch attempt.
|
||||
// Fetch:
|
||||
// Time of last successful fetch.
|
||||
// Sha256:
|
||||
// Checksum of last fetch.
|
||||
// Comment:
|
||||
// User supplied comment.
|
||||
"CREATE TABLE SeedNodes ( \
|
||||
PublicKey CHARACTER(53) PRIMARY KEY NOT NULL, \
|
||||
Source CHARACTER(1) NOT NULL, \
|
||||
Next DATETIME, \
|
||||
Scan DATETIME, \
|
||||
Fetch DATETIME, \
|
||||
Sha256 CHARACTER[64], \
|
||||
Comment TEXT \
|
||||
);",
|
||||
|
||||
// Allow us to easily find the next SeedNode to fetch.
|
||||
"CREATE INDEX SeedNodeNext ON SeedNodes (Next);",
|
||||
|
||||
// Nodes we trust to not grossly collude against us. Derived from SeedDomains, SeedNodes, and ValidatorReferrals.
|
||||
//
|
||||
// Score:
|
||||
// Computed trust score. Higher is better.
|
||||
// Seen:
|
||||
// Last validation received.
|
||||
"CREATE TABLE TrustedNodes ( \
|
||||
PublicKey CHARACTER(53) PRIMARY KEY NOT NULL, \
|
||||
Score INTEGER DEFAULT 0 NOT NULL, \
|
||||
Seen DATETIME, \
|
||||
Comment TEXT \
|
||||
);",
|
||||
|
||||
// List of referrals.
|
||||
// - There may be multiple sources for a Validator. The last source is used.
|
||||
// Validator:
|
||||
// Public key of referrer.
|
||||
// Entry:
|
||||
// Entry index in [validators] table.
|
||||
// Referral:
|
||||
// This is the form provided by the ripple.txt:
|
||||
// - Public key for CAS based referral.
|
||||
// - Domain for domain based referral.
|
||||
// XXX Do garbage collection when validators have no references.
|
||||
"CREATE TABLE ValidatorReferrals ( \
|
||||
Validator CHARACTER(53) NOT NULL, \
|
||||
Entry INTEGER NOT NULL, \
|
||||
Referral TEXT NOT NULL, \
|
||||
PRIMARY KEY (Validator,Entry) \
|
||||
);",
|
||||
|
||||
// List of referrals from ripple.txt files.
|
||||
// Validator:
|
||||
// Public key of referree.
|
||||
// Entry:
|
||||
// Entry index in [validators] table.
|
||||
// IP:
|
||||
// IP of referred.
|
||||
// Port:
|
||||
// -1 = Default
|
||||
// XXX Do garbage collection when ips have no references.
|
||||
"CREATE TABLE IpReferrals ( \
|
||||
Validator CHARACTER(53) NOT NULL, \
|
||||
Entry INTEGER NOT NULL, \
|
||||
IP TEXT NOT NULL, \
|
||||
Port INTEGER NOT NULL DEFAULT -1, \
|
||||
PRIMARY KEY (Validator,Entry) \
|
||||
);",
|
||||
|
||||
// Table of IPs to contact the network.
|
||||
// IP:
|
||||
// IP address to contact.
|
||||
// Port:
|
||||
// Port to contact.
|
||||
// -1 = Default
|
||||
// Score:
|
||||
// Computed trust score. Higher is better.
|
||||
// Source:
|
||||
// 'V' = Validation file
|
||||
// 'M' = Manually added.
|
||||
// 'I' = Inbound connection.
|
||||
// 'T' = Told by other peer
|
||||
// 'O' = Other.
|
||||
// ScanNext:
|
||||
// When to next scan. Null=not scanning.
|
||||
// ScanInterval:
|
||||
// Delay between scans.
|
||||
"CREATE TABLE PeerIps ( \
|
||||
IpPort TEXT NOT NULL PRIMARY KEY, \
|
||||
Score INTEGER NOT NULL DEFAULT 0, \
|
||||
Source CHARACTER(1) NOT NULL, \
|
||||
ScanNext DATETIME DEFAULT 0, \
|
||||
ScanInterval INTEGER NOT NULL DEFAULT 0 \
|
||||
);",
|
||||
|
||||
"CREATE INDEX PeerScanIndex ON \
|
||||
PeerIps(ScanNext);",
|
||||
|
||||
"END TRANSACTION;"
|
||||
};
|
||||
|
||||
int WalletDBCount = NUMBER(WalletDBInit);
|
||||
|
||||
// Hash node database holds nodes indexed by hash
|
||||
const char *HashNodeDBInit[] = {
|
||||
"BEGIN TRANSACTION;",
|
||||
|
||||
"CREATE TABLE CommittedObjects ( \
|
||||
Hash CHARACTER(64) PRIMARY KEY, \
|
||||
ObjType CHAR(1) NOT NULL, \
|
||||
LedgerIndex BIGINT UNSIGNED, \
|
||||
Object BLOB \
|
||||
);",
|
||||
|
||||
"CREATE INDEX ObjectLocate ON \
|
||||
CommittedObjects(LedgerIndex, ObjType);",
|
||||
|
||||
"END TRANSACTION;"
|
||||
};
|
||||
|
||||
int HashNodeDBCount = NUMBER(HashNodeDBInit);
|
||||
|
||||
// Net node database holds nodes seen on the network
|
||||
// XXX Not really used needs replacement.
|
||||
const char *NetNodeDBInit[] = {
|
||||
"CREATE TABLE KnownNodes ( \
|
||||
Hanko CHARACTER(35) PRIMARY KEY, \
|
||||
LastSeen TEXT, \
|
||||
HaveContactInfo CHARACTER(1), \
|
||||
ContactObject BLOB \
|
||||
);"
|
||||
};
|
||||
|
||||
|
||||
int NetNodeDBCount = NUMBER(NetNodeDBInit);
|
||||
|
||||
// vim:ts=4
|
||||
340
src/cpp/ripple/DeterministicKeys.cpp
Normal file
340
src/cpp/ripple/DeterministicKeys.cpp
Normal file
@@ -0,0 +1,340 @@
|
||||
|
||||
#include <openssl/ec.h>
|
||||
#include <openssl/bn.h>
|
||||
#include <openssl/ecdsa.h>
|
||||
#include <openssl/pem.h>
|
||||
#include <openssl/err.h>
|
||||
|
||||
// #define EC_DEBUG
|
||||
|
||||
// Functions to add CKey support for deterministic EC keys
|
||||
|
||||
#include <boost/test/unit_test.hpp>
|
||||
|
||||
#include "Serializer.h"
|
||||
#include "Log.h"
|
||||
|
||||
// <-- seed
|
||||
uint128 CKey::PassPhraseToKey(const std::string& passPhrase)
|
||||
{
|
||||
Serializer s;
|
||||
|
||||
s.addRaw(passPhrase);
|
||||
uint256 hash256 = s.getSHA512Half();
|
||||
uint128 ret(hash256);
|
||||
|
||||
s.secureErase();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
// --> seed
|
||||
// <-- private root generator + public root generator
|
||||
EC_KEY* CKey::GenerateRootDeterministicKey(const uint128& seed)
|
||||
{
|
||||
BN_CTX* ctx=BN_CTX_new();
|
||||
if(!ctx) return NULL;
|
||||
|
||||
EC_KEY* pkey=EC_KEY_new_by_curve_name(NID_secp256k1);
|
||||
if(!pkey)
|
||||
{
|
||||
BN_CTX_free(ctx);
|
||||
return NULL;
|
||||
}
|
||||
EC_KEY_set_conv_form(pkey, POINT_CONVERSION_COMPRESSED);
|
||||
|
||||
BIGNUM* order=BN_new();
|
||||
if(!order)
|
||||
{
|
||||
BN_CTX_free(ctx);
|
||||
EC_KEY_free(pkey);
|
||||
return NULL;
|
||||
}
|
||||
if(!EC_GROUP_get_order(EC_KEY_get0_group(pkey), order, ctx))
|
||||
{
|
||||
assert(false);
|
||||
BN_free(order);
|
||||
EC_KEY_free(pkey);
|
||||
BN_CTX_free(ctx);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
BIGNUM *privKey=NULL;
|
||||
int seq=0;
|
||||
do
|
||||
{ // private key must be non-zero and less than the curve's order
|
||||
Serializer s((128+32)/8);
|
||||
s.add128(seed);
|
||||
s.add32(seq++);
|
||||
uint256 root=s.getSHA512Half();
|
||||
s.secureErase();
|
||||
privKey=BN_bin2bn((const unsigned char *) &root, sizeof(root), privKey);
|
||||
if(privKey==NULL)
|
||||
{
|
||||
EC_KEY_free(pkey);
|
||||
BN_free(order);
|
||||
BN_CTX_free(ctx);
|
||||
}
|
||||
root.zero();
|
||||
} while(BN_is_zero(privKey) || (BN_cmp(privKey, order)>=0));
|
||||
|
||||
BN_free(order);
|
||||
|
||||
if(!EC_KEY_set_private_key(pkey, privKey))
|
||||
{ // set the random point as the private key
|
||||
assert(false);
|
||||
EC_KEY_free(pkey);
|
||||
BN_clear_free(privKey);
|
||||
BN_CTX_free(ctx);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
EC_POINT *pubKey=EC_POINT_new(EC_KEY_get0_group(pkey));
|
||||
if(!EC_POINT_mul(EC_KEY_get0_group(pkey), pubKey, privKey, NULL, NULL, ctx))
|
||||
{ // compute the corresponding public key point
|
||||
assert(false);
|
||||
BN_clear_free(privKey);
|
||||
EC_POINT_free(pubKey);
|
||||
EC_KEY_free(pkey);
|
||||
BN_CTX_free(ctx);
|
||||
return NULL;
|
||||
}
|
||||
BN_clear_free(privKey);
|
||||
if(!EC_KEY_set_public_key(pkey, pubKey))
|
||||
{
|
||||
assert(false);
|
||||
EC_POINT_free(pubKey);
|
||||
EC_KEY_free(pkey);
|
||||
BN_CTX_free(ctx);
|
||||
return NULL;
|
||||
}
|
||||
EC_POINT_free(pubKey);
|
||||
|
||||
BN_CTX_free(ctx);
|
||||
|
||||
#ifdef EC_DEBUG
|
||||
assert(EC_KEY_check_key(pkey)==1); // CAUTION: This check is *very* expensive
|
||||
#endif
|
||||
return pkey;
|
||||
}
|
||||
|
||||
// Take ripple address.
|
||||
// --> root public generator (consumes)
|
||||
// <-- root public generator in EC format
|
||||
EC_KEY* CKey::GenerateRootPubKey(BIGNUM* pubGenerator)
|
||||
{
|
||||
if (pubGenerator == NULL)
|
||||
{
|
||||
assert(false);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
EC_KEY* pkey = EC_KEY_new_by_curve_name(NID_secp256k1);
|
||||
if (!pkey)
|
||||
{
|
||||
BN_free(pubGenerator);
|
||||
return NULL;
|
||||
}
|
||||
EC_KEY_set_conv_form(pkey, POINT_CONVERSION_COMPRESSED);
|
||||
|
||||
EC_POINT* pubPoint = EC_POINT_bn2point(EC_KEY_get0_group(pkey), pubGenerator, NULL, NULL);
|
||||
BN_free(pubGenerator);
|
||||
if(!pubPoint)
|
||||
{
|
||||
assert(false);
|
||||
EC_KEY_free(pkey);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if(!EC_KEY_set_public_key(pkey, pubPoint))
|
||||
{
|
||||
assert(false);
|
||||
EC_POINT_free(pubPoint);
|
||||
EC_KEY_free(pkey);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return pkey;
|
||||
}
|
||||
|
||||
// --> public generator
|
||||
static BIGNUM* makeHash(const RippleAddress& pubGen, int seq, BIGNUM* order)
|
||||
{
|
||||
int subSeq=0;
|
||||
BIGNUM* ret=NULL;
|
||||
do
|
||||
{
|
||||
Serializer s((33*8+32+32)/8);
|
||||
s.addRaw(pubGen.getGenerator());
|
||||
s.add32(seq);
|
||||
s.add32(subSeq++);
|
||||
uint256 root=s.getSHA512Half();
|
||||
s.secureErase();
|
||||
ret=BN_bin2bn((const unsigned char *) &root, sizeof(root), ret);
|
||||
if(!ret) return NULL;
|
||||
} while (BN_is_zero(ret) || (BN_cmp(ret, order)>=0));
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
// --> public generator
|
||||
EC_KEY* CKey::GeneratePublicDeterministicKey(const RippleAddress& pubGen, int seq)
|
||||
{ // publicKey(n) = rootPublicKey EC_POINT_+ Hash(pubHash|seq)*point
|
||||
EC_KEY* rootKey = CKey::GenerateRootPubKey(pubGen.getGeneratorBN());
|
||||
const EC_POINT* rootPubKey = EC_KEY_get0_public_key(rootKey);
|
||||
BN_CTX* ctx = BN_CTX_new();
|
||||
EC_KEY* pkey = EC_KEY_new_by_curve_name(NID_secp256k1);
|
||||
EC_POINT* newPoint = 0;
|
||||
BIGNUM* order = 0;
|
||||
BIGNUM* hash = 0;
|
||||
bool success = true;
|
||||
|
||||
if (!ctx || !pkey) success = false;
|
||||
|
||||
if (success)
|
||||
EC_KEY_set_conv_form(pkey, POINT_CONVERSION_COMPRESSED);
|
||||
|
||||
if (success) {
|
||||
newPoint = EC_POINT_new(EC_KEY_get0_group(pkey));
|
||||
if(!newPoint) success = false;
|
||||
}
|
||||
|
||||
if (success) {
|
||||
order = BN_new();
|
||||
|
||||
if(!order || !EC_GROUP_get_order(EC_KEY_get0_group(pkey), order, ctx))
|
||||
success = false;
|
||||
}
|
||||
|
||||
// Calculate the private additional key.
|
||||
if (success) {
|
||||
hash = makeHash(pubGen, seq, order);
|
||||
if(!hash) success = false;
|
||||
}
|
||||
|
||||
if (success) {
|
||||
// Calculate the corresponding public key.
|
||||
EC_POINT_mul(EC_KEY_get0_group(pkey), newPoint, hash, NULL, NULL, ctx);
|
||||
|
||||
// Add the master public key and set.
|
||||
EC_POINT_add(EC_KEY_get0_group(pkey), newPoint, newPoint, rootPubKey, ctx);
|
||||
EC_KEY_set_public_key(pkey, newPoint);
|
||||
}
|
||||
|
||||
if (order) BN_free(order);
|
||||
if (hash) BN_free(hash);
|
||||
if (newPoint) EC_POINT_free(newPoint);
|
||||
if (ctx) BN_CTX_free(ctx);
|
||||
if (rootKey) EC_KEY_free(rootKey);
|
||||
if (pkey && !success) EC_KEY_free(pkey);
|
||||
|
||||
return success ? pkey : NULL;
|
||||
}
|
||||
|
||||
EC_KEY* CKey::GeneratePrivateDeterministicKey(const RippleAddress& pubGen, const uint256& u, int seq)
|
||||
{
|
||||
CBigNum bn(u);
|
||||
return GeneratePrivateDeterministicKey(pubGen, static_cast<BIGNUM*>(&bn), seq);
|
||||
}
|
||||
|
||||
// --> root private key
|
||||
EC_KEY* CKey::GeneratePrivateDeterministicKey(const RippleAddress& pubGen, const BIGNUM* rootPrivKey, int seq)
|
||||
{ // privateKey(n) = (rootPrivateKey + Hash(pubHash|seq)) % order
|
||||
BN_CTX* ctx=BN_CTX_new();
|
||||
if(ctx==NULL) return NULL;
|
||||
|
||||
EC_KEY* pkey=EC_KEY_new_by_curve_name(NID_secp256k1);
|
||||
if(pkey==NULL)
|
||||
{
|
||||
BN_CTX_free(ctx);
|
||||
return NULL;
|
||||
}
|
||||
EC_KEY_set_conv_form(pkey, POINT_CONVERSION_COMPRESSED);
|
||||
|
||||
BIGNUM* order=BN_new();
|
||||
if(order==NULL)
|
||||
{
|
||||
BN_CTX_free(ctx);
|
||||
EC_KEY_free(pkey);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if(!EC_GROUP_get_order(EC_KEY_get0_group(pkey), order, ctx))
|
||||
{
|
||||
BN_free(order);
|
||||
BN_CTX_free(ctx);
|
||||
EC_KEY_free(pkey);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// calculate the private additional key
|
||||
BIGNUM* privKey=makeHash(pubGen, seq, order);
|
||||
if(privKey==NULL)
|
||||
{
|
||||
BN_free(order);
|
||||
BN_CTX_free(ctx);
|
||||
EC_KEY_free(pkey);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// calculate the final private key
|
||||
BN_mod_add(privKey, privKey, rootPrivKey, order, ctx);
|
||||
BN_free(order);
|
||||
EC_KEY_set_private_key(pkey, privKey);
|
||||
|
||||
// compute the corresponding public key
|
||||
EC_POINT* pubKey=EC_POINT_new(EC_KEY_get0_group(pkey));
|
||||
if(!pubKey)
|
||||
{
|
||||
BN_clear_free(privKey);
|
||||
BN_CTX_free(ctx);
|
||||
EC_KEY_free(pkey);
|
||||
return NULL;
|
||||
}
|
||||
if(EC_POINT_mul(EC_KEY_get0_group(pkey), pubKey, privKey, NULL, NULL, ctx)==0)
|
||||
{
|
||||
BN_clear_free(privKey);
|
||||
BN_CTX_free(ctx);
|
||||
EC_KEY_free(pkey);
|
||||
return NULL;
|
||||
}
|
||||
BN_clear_free(privKey);
|
||||
EC_KEY_set_public_key(pkey, pubKey);
|
||||
|
||||
EC_POINT_free(pubKey);
|
||||
BN_CTX_free(ctx);
|
||||
|
||||
return pkey;
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE(DeterministicKeys_test)
|
||||
|
||||
BOOST_AUTO_TEST_CASE(DeterminsticKeys_test1)
|
||||
{
|
||||
Log(lsDEBUG) << "Beginning deterministic key test";
|
||||
|
||||
uint128 seed1, seed2;
|
||||
seed1.SetHex("71ED064155FFADFA38782C5E0158CB26");
|
||||
seed2.SetHex("CF0C3BE4485961858C4198515AE5B965");
|
||||
CKey root1(seed1), root2(seed2);
|
||||
|
||||
uint256 priv1, priv2;
|
||||
root1.GetPrivateKeyU(priv1);
|
||||
root2.GetPrivateKeyU(priv2);
|
||||
|
||||
if (priv1.GetHex() != "7CFBA64F771E93E817E15039215430B53F7401C34931D111EAB3510B22DBB0D8")
|
||||
BOOST_FAIL("Incorrect private key for generator");
|
||||
if (priv2.GetHex() != "98BC2EACB26EB021D1A6293C044D88BA2F0B6729A2772DEEBF2E21A263C1740B")
|
||||
BOOST_FAIL("Incorrect private key for generator");
|
||||
|
||||
RippleAddress nSeed;
|
||||
nSeed.setSeed(seed1);
|
||||
if (nSeed.humanSeed() != "shHM53KPZ87Gwdqarm1bAmPeXg8Tn")
|
||||
BOOST_FAIL("Incorrect human seed");
|
||||
if (nSeed.humanSeed1751() != "MAD BODY ACE MINT OKAY HUB WHAT DATA SACK FLAT DANA MATH")
|
||||
BOOST_FAIL("Incorrect 1751 seed");
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END();
|
||||
|
||||
// vim:ts=4
|
||||
303
src/cpp/ripple/ECIES.cpp
Normal file
303
src/cpp/ripple/ECIES.cpp
Normal file
@@ -0,0 +1,303 @@
|
||||
#include <openssl/ec.h>
|
||||
#include <openssl/bn.h>
|
||||
#include <openssl/ecdsa.h>
|
||||
#include <openssl/pem.h>
|
||||
#include <openssl/hmac.h>
|
||||
#include <openssl/rand.h>
|
||||
|
||||
#include <vector>
|
||||
#include <cassert>
|
||||
|
||||
#include "key.h"
|
||||
|
||||
// ECIES uses elliptic curve keys to send an encrypted message.
|
||||
|
||||
// A shared secret is generated from one public key and one private key.
|
||||
// The same key results regardless of which key is public and which private.
|
||||
|
||||
// Anonymous messages can be sent by generating an ephemeral public/private
|
||||
// key pair, using that private key with the recipient's public key to
|
||||
// encrypt and publishing the ephemeral public key. Non-anonymous messages
|
||||
// can be sent by using your own private key with the recipient's public key.
|
||||
|
||||
// A random IV is used to encrypt the message and an HMAC is used to ensure
|
||||
// message integrity. If you need timestamps or need to tell the recipient
|
||||
// which key to use (his, yours, or ephemeral) you must add that data.
|
||||
// (Obviously, key information can't go in the encrypted portion anyway.)
|
||||
|
||||
// Our ciphertext is all encrypted except the IV. The encrypted data decodes as follows:
|
||||
// 1) IV (unencrypted)
|
||||
// 2) Encrypted: HMAC of original plaintext
|
||||
// 3) Encrypted: Original plaintext
|
||||
// 4) Encrypted: Rest of block/padding
|
||||
|
||||
// ECIES operations throw on any error such as a corrupt message or incorrect
|
||||
// key. They *must* be called in try/catch blocks.
|
||||
|
||||
// Algorithmic choices:
|
||||
#define ECIES_KEY_HASH SHA512 // Hash used to expand shared secret
|
||||
#define ECIES_KEY_LENGTH (512/8) // Size of expanded shared secret
|
||||
#define ECIES_MIN_SEC (128/8) // The minimum equivalent security
|
||||
#define ECIES_ENC_ALGO EVP_aes_256_cbc() // Encryption algorithm
|
||||
#define ECIES_ENC_KEY_TYPE uint256 // Type used to hold shared secret
|
||||
#define ECIES_ENC_KEY_SIZE (256/8) // Encryption key size
|
||||
#define ECIES_ENC_BLK_SIZE (128/8) // Encryption block size
|
||||
#define ECIES_ENC_IV_TYPE uint128 // Type used to hold IV
|
||||
#define ECIES_HMAC_ALGO EVP_sha256() // HMAC algorithm
|
||||
#define ECIES_HMAC_KEY_TYPE uint256 // Type used to hold HMAC key
|
||||
#define ECIES_HMAC_KEY_SIZE (256/8) // Size of HMAC key
|
||||
#define ECIES_HMAC_TYPE uint256 // Type used to hold HMAC value
|
||||
#define ECIES_HMAC_SIZE (256/8) // Size of HMAC value
|
||||
|
||||
void CKey::getECIESSecret(CKey& otherKey, ECIES_ENC_KEY_TYPE& enc_key, ECIES_HMAC_KEY_TYPE& hmac_key)
|
||||
{ // Retrieve a secret generated from an EC key pair. At least one private key must be known.
|
||||
if (!pkey || !otherKey.pkey)
|
||||
throw std::runtime_error("missing key");
|
||||
|
||||
EC_KEY *pubkey, *privkey;
|
||||
if (EC_KEY_get0_private_key(pkey))
|
||||
{
|
||||
privkey = pkey;
|
||||
pubkey = otherKey.pkey;
|
||||
}
|
||||
else if (EC_KEY_get0_private_key(otherKey.pkey))
|
||||
{
|
||||
privkey = otherKey.pkey;
|
||||
pubkey = pkey;
|
||||
}
|
||||
else throw std::runtime_error("no private key");
|
||||
|
||||
unsigned char rawbuf[512];
|
||||
int buflen = ECDH_compute_key(rawbuf, 512, EC_KEY_get0_public_key(pubkey), privkey, NULL);
|
||||
if (buflen < ECIES_MIN_SEC)
|
||||
throw std::runtime_error("ecdh key failed");
|
||||
|
||||
unsigned char hbuf[ECIES_KEY_LENGTH];
|
||||
ECIES_KEY_HASH(rawbuf, buflen, hbuf);
|
||||
memset(rawbuf, 0, ECIES_HMAC_KEY_SIZE);
|
||||
|
||||
assert((ECIES_ENC_KEY_SIZE + ECIES_HMAC_KEY_SIZE) >= ECIES_KEY_LENGTH);
|
||||
memcpy(enc_key.begin(), hbuf, ECIES_ENC_KEY_SIZE);
|
||||
memcpy(hmac_key.begin(), hbuf + ECIES_ENC_KEY_SIZE, ECIES_HMAC_KEY_SIZE);
|
||||
memset(hbuf, 0, ECIES_KEY_LENGTH);
|
||||
}
|
||||
|
||||
static ECIES_HMAC_TYPE makeHMAC(const ECIES_HMAC_KEY_TYPE& secret, const std::vector<unsigned char>& data)
|
||||
{
|
||||
HMAC_CTX ctx;
|
||||
HMAC_CTX_init(&ctx);
|
||||
|
||||
if (HMAC_Init_ex(&ctx, secret.begin(), ECIES_HMAC_KEY_SIZE, ECIES_HMAC_ALGO, NULL) != 1)
|
||||
{
|
||||
HMAC_CTX_cleanup(&ctx);
|
||||
throw std::runtime_error("init hmac");
|
||||
}
|
||||
|
||||
if (HMAC_Update(&ctx, &(data.front()), data.size()) != 1)
|
||||
{
|
||||
HMAC_CTX_cleanup(&ctx);
|
||||
throw std::runtime_error("update hmac");
|
||||
}
|
||||
|
||||
ECIES_HMAC_TYPE ret;
|
||||
unsigned int ml = ECIES_HMAC_SIZE;
|
||||
if (HMAC_Final(&ctx, ret.begin(), &ml) != 1)
|
||||
{
|
||||
HMAC_CTX_cleanup(&ctx);
|
||||
throw std::runtime_error("finalize hmac");
|
||||
}
|
||||
assert(ml == ECIES_HMAC_SIZE);
|
||||
HMAC_CTX_cleanup(&ctx);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::vector<unsigned char> CKey::encryptECIES(CKey& otherKey, const std::vector<unsigned char>& plaintext)
|
||||
{
|
||||
|
||||
ECIES_ENC_IV_TYPE iv;
|
||||
if (RAND_bytes(static_cast<unsigned char *>(iv.begin()), ECIES_ENC_BLK_SIZE) != 1)
|
||||
throw std::runtime_error("insufficient entropy");
|
||||
|
||||
ECIES_ENC_KEY_TYPE secret;
|
||||
ECIES_HMAC_KEY_TYPE hmacKey;
|
||||
|
||||
getECIESSecret(otherKey, secret, hmacKey);
|
||||
ECIES_HMAC_TYPE hmac = makeHMAC(hmacKey, plaintext);
|
||||
hmacKey.zero();
|
||||
|
||||
EVP_CIPHER_CTX ctx;
|
||||
EVP_CIPHER_CTX_init(&ctx);
|
||||
|
||||
if (EVP_EncryptInit_ex(&ctx, ECIES_ENC_ALGO, NULL, secret.begin(), iv.begin()) != 1)
|
||||
{
|
||||
EVP_CIPHER_CTX_cleanup(&ctx);
|
||||
secret.zero();
|
||||
throw std::runtime_error("init cipher ctx");
|
||||
}
|
||||
secret.zero();
|
||||
|
||||
std::vector<unsigned char> out(plaintext.size() + ECIES_HMAC_SIZE + ECIES_ENC_KEY_SIZE + ECIES_ENC_BLK_SIZE, 0);
|
||||
int len = 0, bytesWritten;
|
||||
|
||||
// output IV
|
||||
memcpy(&(out.front()), iv.begin(), ECIES_ENC_BLK_SIZE);
|
||||
len = ECIES_ENC_BLK_SIZE;
|
||||
|
||||
// Encrypt/output HMAC
|
||||
bytesWritten = out.capacity() - len;
|
||||
assert(bytesWritten>0);
|
||||
if (EVP_EncryptUpdate(&ctx, &(out.front()) + len, &bytesWritten, hmac.begin(), ECIES_HMAC_SIZE) < 0)
|
||||
{
|
||||
EVP_CIPHER_CTX_cleanup(&ctx);
|
||||
throw std::runtime_error("");
|
||||
}
|
||||
len += bytesWritten;
|
||||
|
||||
// encrypt/output plaintext
|
||||
bytesWritten = out.capacity() - len;
|
||||
assert(bytesWritten>0);
|
||||
if (EVP_EncryptUpdate(&ctx, &(out.front()) + len, &bytesWritten, &(plaintext.front()), plaintext.size()) < 0)
|
||||
{
|
||||
EVP_CIPHER_CTX_cleanup(&ctx);
|
||||
throw std::runtime_error("");
|
||||
}
|
||||
len += bytesWritten;
|
||||
|
||||
// finalize
|
||||
bytesWritten = out.capacity() - len;
|
||||
if (EVP_EncryptFinal_ex(&ctx, &(out.front()) + len, &bytesWritten) < 0)
|
||||
{
|
||||
EVP_CIPHER_CTX_cleanup(&ctx);
|
||||
throw std::runtime_error("encryption error");
|
||||
}
|
||||
len += bytesWritten;
|
||||
|
||||
// Output contains: IV, encrypted HMAC, encrypted data, encrypted padding
|
||||
assert(len <= (plaintext.size() + ECIES_HMAC_SIZE + (2 * ECIES_ENC_BLK_SIZE)));
|
||||
assert(len >= (plaintext.size() + ECIES_HMAC_SIZE + ECIES_ENC_BLK_SIZE)); // IV, HMAC, data
|
||||
out.resize(len);
|
||||
EVP_CIPHER_CTX_cleanup(&ctx);
|
||||
return out;
|
||||
}
|
||||
|
||||
std::vector<unsigned char> CKey::decryptECIES(CKey& otherKey, const std::vector<unsigned char>& ciphertext)
|
||||
{
|
||||
// minimum ciphertext = IV + HMAC + 1 block
|
||||
if (ciphertext.size() < ((2 * ECIES_ENC_BLK_SIZE) + ECIES_HMAC_SIZE) )
|
||||
throw std::runtime_error("ciphertext too short");
|
||||
|
||||
// extract IV
|
||||
ECIES_ENC_IV_TYPE iv;
|
||||
memcpy(iv.begin(), &(ciphertext.front()), ECIES_ENC_BLK_SIZE);
|
||||
|
||||
// begin decrypting
|
||||
EVP_CIPHER_CTX ctx;
|
||||
EVP_CIPHER_CTX_init(&ctx);
|
||||
|
||||
ECIES_ENC_KEY_TYPE secret;
|
||||
ECIES_HMAC_KEY_TYPE hmacKey;
|
||||
getECIESSecret(otherKey, secret, hmacKey);
|
||||
|
||||
if (EVP_DecryptInit_ex(&ctx, ECIES_ENC_ALGO, NULL, secret.begin(), iv.begin()) != 1)
|
||||
{
|
||||
secret.zero();
|
||||
hmacKey.zero();
|
||||
EVP_CIPHER_CTX_cleanup(&ctx);
|
||||
throw std::runtime_error("unable to init cipher");
|
||||
}
|
||||
|
||||
// decrypt mac
|
||||
ECIES_HMAC_TYPE hmac;
|
||||
int outlen = ECIES_HMAC_SIZE;
|
||||
if ( (EVP_DecryptUpdate(&ctx, hmac.begin(), &outlen,
|
||||
&(ciphertext.front()) + ECIES_ENC_BLK_SIZE, ECIES_HMAC_SIZE + 1) != 1) || (outlen != ECIES_HMAC_SIZE) )
|
||||
{
|
||||
secret.zero();
|
||||
hmacKey.zero();
|
||||
EVP_CIPHER_CTX_cleanup(&ctx);
|
||||
throw std::runtime_error("unable to extract hmac");
|
||||
}
|
||||
|
||||
// decrypt plaintext (after IV and encrypted mac)
|
||||
std::vector<unsigned char> plaintext(ciphertext.size() - ECIES_HMAC_SIZE - ECIES_ENC_BLK_SIZE);
|
||||
outlen = plaintext.size();
|
||||
if (EVP_DecryptUpdate(&ctx, &(plaintext.front()), &outlen,
|
||||
&(ciphertext.front()) + ECIES_ENC_BLK_SIZE + ECIES_HMAC_SIZE + 1,
|
||||
ciphertext.size() - ECIES_ENC_BLK_SIZE - ECIES_HMAC_SIZE - 1) != 1)
|
||||
{
|
||||
secret.zero();
|
||||
hmacKey.zero();
|
||||
EVP_CIPHER_CTX_cleanup(&ctx);
|
||||
throw std::runtime_error("unable to extract plaintext");
|
||||
}
|
||||
|
||||
// decrypt padding
|
||||
int flen = 0;
|
||||
if (EVP_DecryptFinal(&ctx, &(plaintext.front()) + outlen, &flen) != 1)
|
||||
{
|
||||
secret.zero();
|
||||
hmacKey.zero();
|
||||
EVP_CIPHER_CTX_cleanup(&ctx);
|
||||
throw std::runtime_error("plaintext had bad padding");
|
||||
}
|
||||
plaintext.resize(flen + outlen);
|
||||
|
||||
// verify integrity
|
||||
if (hmac != makeHMAC(hmacKey, plaintext))
|
||||
{
|
||||
secret.zero();
|
||||
hmacKey.zero();
|
||||
EVP_CIPHER_CTX_cleanup(&ctx);
|
||||
throw std::runtime_error("plaintext had bad hmac");
|
||||
}
|
||||
secret.zero();
|
||||
hmacKey.zero();
|
||||
|
||||
EVP_CIPHER_CTX_cleanup(&ctx);
|
||||
return plaintext;
|
||||
}
|
||||
|
||||
bool checkECIES(void)
|
||||
{
|
||||
CKey senderPriv, recipientPriv, senderPub, recipientPub;
|
||||
|
||||
for (int i = 0; i < 30000; ++i)
|
||||
{
|
||||
if ((i % 100) == 0)
|
||||
{ // generate new keys every 100 times
|
||||
// std::cerr << "new keys" << std::endl;
|
||||
senderPriv.MakeNewKey();
|
||||
recipientPriv.MakeNewKey();
|
||||
|
||||
if (!senderPub.SetPubKey(senderPriv.GetPubKey()))
|
||||
throw std::runtime_error("key error");
|
||||
if (!recipientPub.SetPubKey(recipientPriv.GetPubKey()))
|
||||
throw std::runtime_error("key error");
|
||||
}
|
||||
|
||||
// generate message
|
||||
std::vector<unsigned char> message(4096);
|
||||
int msglen = i%3000;
|
||||
|
||||
if (RAND_bytes(static_cast<unsigned char *>(&message.front()), msglen) != 1)
|
||||
throw std::runtime_error("insufficient entropy");
|
||||
message.resize(msglen);
|
||||
|
||||
// encrypt message with sender's private key and recipient's public key
|
||||
std::vector<unsigned char> ciphertext = senderPriv.encryptECIES(recipientPub, message);
|
||||
|
||||
// decrypt message with recipient's private key and sender's public key
|
||||
std::vector<unsigned char> decrypt = recipientPriv.decryptECIES(senderPub, ciphertext);
|
||||
|
||||
if (decrypt != message)
|
||||
{
|
||||
assert(false);
|
||||
return false;
|
||||
}
|
||||
// std::cerr << "Msg(" << msglen << ") ok " << ciphertext.size() << std::endl;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// vim:ts=4
|
||||
143
src/cpp/ripple/FieldNames.cpp
Normal file
143
src/cpp/ripple/FieldNames.cpp
Normal file
@@ -0,0 +1,143 @@
|
||||
|
||||
#include "FieldNames.h"
|
||||
|
||||
#include <map>
|
||||
|
||||
#include <boost/thread/mutex.hpp>
|
||||
#include <boost/lexical_cast.hpp>
|
||||
#include <boost/foreach.hpp>
|
||||
|
||||
#include "utils.h"
|
||||
|
||||
// These must stay at the top of this file
|
||||
std::map<int, SField::ptr> SField::codeToField;
|
||||
boost::mutex SField::mapMutex;
|
||||
|
||||
SField sfInvalid(-1), sfGeneric(0);
|
||||
SField sfLedgerEntry(STI_LEDGERENTRY, 1, "LedgerEntry");
|
||||
SField sfTransaction(STI_TRANSACTION, 1, "Transaction");
|
||||
SField sfValidation(STI_VALIDATION, 1, "Validation");
|
||||
SField sfHash(STI_HASH256, 257, "hash");
|
||||
SField sfIndex(STI_HASH256, 258, "index");
|
||||
|
||||
#define FIELD(name, type, index) SField sf##name(FIELD_CODE(STI_##type, index), STI_##type, index, #name);
|
||||
#define TYPE(name, type, index)
|
||||
#include "SerializeProto.h"
|
||||
#undef FIELD
|
||||
#undef TYPE
|
||||
|
||||
static int initFields()
|
||||
{
|
||||
sfTxnSignature.notSigningField(); sfTxnSignatures.notSigningField();
|
||||
sfSignature.notSigningField();
|
||||
|
||||
sfHighQualityIn.setMeta(SFM_CHANGE); sfHighQualityOut.setMeta(SFM_CHANGE);
|
||||
sfLowQualityIn.setMeta(SFM_CHANGE); sfLowQualityOut.setMeta(SFM_CHANGE);
|
||||
|
||||
sfLowLimit.setMeta(SFM_ALWAYS); sfHighLimit.setMeta(SFM_ALWAYS);
|
||||
sfTakerPays.setMeta(SFM_ALWAYS); sfTakerGets.setMeta(SFM_ALWAYS);
|
||||
sfQualityIn.setMeta(SFM_ALWAYS); sfQualityOut.setMeta(SFM_ALWAYS);
|
||||
|
||||
sfBalance.setMeta(SFM_ALWAYS);
|
||||
|
||||
sfPublicKey.setMeta(SFM_CHANGE); sfMessageKey.setMeta(SFM_CHANGE);
|
||||
sfSigningPubKey.setMeta(SFM_CHANGE); sfAuthorizedKey.setMeta(SFM_CHANGE);
|
||||
sfSigningAccounts.setMeta(SFM_CHANGE);
|
||||
|
||||
sfWalletLocator.setMeta(SFM_ALWAYS);
|
||||
sfWalletSize.setMeta(SFM_ALWAYS);
|
||||
sfNickname.setMeta(SFM_CHANGE);
|
||||
sfAmount.setMeta(SFM_ALWAYS);
|
||||
sfDomain.setMeta(SFM_CHANGE);
|
||||
sfOwner.setMeta(SFM_ALWAYS);
|
||||
|
||||
return 0;
|
||||
}
|
||||
static const int f = initFields();
|
||||
|
||||
|
||||
SField::SField(SerializedTypeID tid, int fv) : fieldCode(FIELD_CODE(tid, fv)), fieldType(tid), fieldValue(fv)
|
||||
{ // call with the map mutex
|
||||
fieldName = lexical_cast_i(tid) + "/" + lexical_cast_i(fv);
|
||||
codeToField[fieldCode] = this;
|
||||
}
|
||||
|
||||
SField::ref SField::getField(int code)
|
||||
{
|
||||
int type = code >> 16;
|
||||
int field = code % 0xffff;
|
||||
|
||||
if ((type <= 0) || (field <= 0))
|
||||
return sfInvalid;
|
||||
|
||||
boost::mutex::scoped_lock sl(mapMutex);
|
||||
|
||||
std::map<int, SField::ptr>::iterator it = codeToField.find(code);
|
||||
if (it != codeToField.end())
|
||||
return *(it->second);
|
||||
|
||||
if (field > 255) // don't dynamically extend types that have no binary encoding
|
||||
return sfInvalid;
|
||||
|
||||
switch (type)
|
||||
{ // types we are willing to dynamically extend
|
||||
|
||||
#define FIELD(name, type, index)
|
||||
#define TYPE(name, type, index) case STI_##type:
|
||||
#include "SerializeProto.h"
|
||||
#undef FIELD
|
||||
#undef TYPE
|
||||
|
||||
break;
|
||||
default:
|
||||
return sfInvalid;
|
||||
}
|
||||
|
||||
return *(new SField(static_cast<SerializedTypeID>(type), field));
|
||||
}
|
||||
|
||||
int SField::compare(SField::ref f1, SField::ref f2)
|
||||
{ // -1 = f1 comes before f2, 0 = illegal combination, 1 = f1 comes after f2
|
||||
if ((f1.fieldCode <= 0) || (f2.fieldCode <= 0))
|
||||
return 0;
|
||||
|
||||
if (f1.fieldCode < f2.fieldCode)
|
||||
return -1;
|
||||
|
||||
if (f2.fieldCode < f1.fieldCode)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::string SField::getName() const
|
||||
{
|
||||
if (!fieldName.empty())
|
||||
return fieldName;
|
||||
if (fieldValue == 0)
|
||||
return "";
|
||||
return boost::lexical_cast<std::string>(static_cast<int>(fieldType)) + "/" +
|
||||
boost::lexical_cast<std::string>(fieldValue);
|
||||
}
|
||||
|
||||
SField::ref SField::getField(const std::string& fieldName)
|
||||
{ // OPTIMIZEME me with a map. CHECKME this is case sensitive
|
||||
boost::mutex::scoped_lock sl(mapMutex);
|
||||
typedef std::pair<const int, SField::ptr> int_sfref_pair;
|
||||
BOOST_FOREACH(const int_sfref_pair& fieldPair, codeToField)
|
||||
{
|
||||
if (fieldPair.second->fieldName == fieldName)
|
||||
return *(fieldPair.second);
|
||||
}
|
||||
return sfInvalid;
|
||||
}
|
||||
|
||||
SField::~SField()
|
||||
{
|
||||
boost::mutex::scoped_lock sl(mapMutex);
|
||||
std::map<int, ptr>::iterator it = codeToField.find(fieldCode);
|
||||
if ((it != codeToField.end()) && (it->second == this))
|
||||
codeToField.erase(it);
|
||||
}
|
||||
|
||||
// vim:ts=4
|
||||
122
src/cpp/ripple/FieldNames.h
Normal file
122
src/cpp/ripple/FieldNames.h
Normal file
@@ -0,0 +1,122 @@
|
||||
#ifndef __FIELDNAMES__
|
||||
#define __FIELDNAMES__
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <boost/thread/mutex.hpp>
|
||||
|
||||
#define FIELD_CODE(type, index) ((static_cast<int>(type) << 16) | index)
|
||||
|
||||
enum SerializedTypeID
|
||||
{
|
||||
// special types
|
||||
STI_UNKNOWN = -2,
|
||||
STI_DONE = -1,
|
||||
STI_NOTPRESENT = 0,
|
||||
|
||||
#define TYPE(name, field, value) STI_##field = value,
|
||||
#define FIELD(name, field, value)
|
||||
#include "SerializeProto.h"
|
||||
#undef TYPE
|
||||
#undef FIELD
|
||||
|
||||
// high level types
|
||||
STI_TRANSACTION = 10001,
|
||||
STI_LEDGERENTRY = 10002,
|
||||
STI_VALIDATION = 10003,
|
||||
};
|
||||
|
||||
enum SOE_Flags
|
||||
{
|
||||
SOE_INVALID = -1,
|
||||
SOE_REQUIRED = 0, // required
|
||||
SOE_OPTIONAL = 1, // optional
|
||||
};
|
||||
|
||||
enum SF_Meta
|
||||
{
|
||||
SFM_NEVER = 0,
|
||||
SFM_CHANGE = 1,
|
||||
SFM_DELETE = 2,
|
||||
SFM_ALWAYS = 3
|
||||
};
|
||||
|
||||
class SField
|
||||
{
|
||||
public:
|
||||
typedef const SField& ref;
|
||||
typedef SField const * ptr;
|
||||
|
||||
protected:
|
||||
static std::map<int, ptr> codeToField;
|
||||
static boost::mutex mapMutex;
|
||||
|
||||
SField(SerializedTypeID id, int val);
|
||||
|
||||
public:
|
||||
|
||||
const int fieldCode; // (type<<16)|index
|
||||
const SerializedTypeID fieldType; // STI_*
|
||||
const int fieldValue; // Code number for protocol
|
||||
std::string fieldName;
|
||||
SF_Meta fieldMeta;
|
||||
bool signingField;
|
||||
|
||||
SField(int fc, SerializedTypeID tid, int fv, const char* fn) :
|
||||
fieldCode(fc), fieldType(tid), fieldValue(fv), fieldName(fn), fieldMeta(SFM_NEVER), signingField(true)
|
||||
{
|
||||
boost::mutex::scoped_lock sl(mapMutex);
|
||||
codeToField[fieldCode] = this;
|
||||
}
|
||||
|
||||
SField(SerializedTypeID tid, int fv, const char *fn) :
|
||||
fieldCode(FIELD_CODE(tid, fv)), fieldType(tid), fieldValue(fv), fieldName(fn),
|
||||
fieldMeta(SFM_NEVER), signingField(true)
|
||||
{
|
||||
boost::mutex::scoped_lock sl(mapMutex);
|
||||
codeToField[fieldCode] = this;
|
||||
}
|
||||
|
||||
SField(int fc) : fieldCode(fc), fieldType(STI_UNKNOWN), fieldValue(0) { ; }
|
||||
|
||||
~SField();
|
||||
|
||||
static SField::ref getField(int fieldCode);
|
||||
static SField::ref getField(const std::string& fieldName);
|
||||
static SField::ref getField(int type, int value) { return getField(FIELD_CODE(type, value)); }
|
||||
static SField::ref getField(SerializedTypeID type, int value) { return getField(FIELD_CODE(type, value)); }
|
||||
|
||||
std::string getName() const;
|
||||
bool hasName() const { return !fieldName.empty(); }
|
||||
|
||||
bool isGeneric() const { return fieldCode == 0; }
|
||||
bool isInvalid() const { return fieldCode == -1; }
|
||||
bool isKnown() const { return fieldType != STI_UNKNOWN; }
|
||||
bool isBinary() const { return fieldValue < 256; }
|
||||
bool isDiscardable() const { return fieldValue > 256; }
|
||||
|
||||
SF_Meta getMeta() const { return fieldMeta; }
|
||||
bool shouldMetaDel() const { return (fieldMeta == SFM_DELETE) || (fieldMeta == SFM_ALWAYS); }
|
||||
bool shouldMetaMod() const { return (fieldMeta == SFM_CHANGE) || (fieldMeta == SFM_ALWAYS); }
|
||||
void setMeta(SF_Meta m) { fieldMeta = m; }
|
||||
bool isSigningField() const { return signingField; }
|
||||
void notSigningField() { signingField = false; }
|
||||
|
||||
bool shouldInclude(bool withSigningField) const
|
||||
{ return (fieldValue < 256) && (withSigningField || signingField); }
|
||||
|
||||
bool operator==(const SField& f) const { return fieldCode == f.fieldCode; }
|
||||
bool operator!=(const SField& f) const { return fieldCode != f.fieldCode; }
|
||||
|
||||
static int compare(SField::ref f1, SField::ref f2);
|
||||
};
|
||||
|
||||
extern SField sfInvalid, sfGeneric, sfLedgerEntry, sfTransaction, sfValidation;
|
||||
|
||||
#define FIELD(name, type, index) extern SField sf##name;
|
||||
#define TYPE(name, type, index)
|
||||
#include "SerializeProto.h"
|
||||
#undef FIELD
|
||||
#undef TYPE
|
||||
|
||||
#endif
|
||||
102
src/cpp/ripple/HTTPRequest.cpp
Normal file
102
src/cpp/ripple/HTTPRequest.cpp
Normal file
@@ -0,0 +1,102 @@
|
||||
#include "HTTPRequest.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <boost/algorithm/string.hpp>
|
||||
#include <boost/lexical_cast.hpp>
|
||||
|
||||
#include "Log.h"
|
||||
SETUP_LOG();
|
||||
|
||||
void HTTPRequest::reset()
|
||||
{
|
||||
vHeaders.clear();
|
||||
sRequestBody.clear();
|
||||
sAuthorization.clear();
|
||||
iDataSize = 0;
|
||||
bShouldClose = true;
|
||||
eState = await_request;
|
||||
}
|
||||
|
||||
HTTPRequestAction HTTPRequest::requestDone(bool forceClose)
|
||||
{
|
||||
if (forceClose || bShouldClose)
|
||||
return haCLOSE_CONN;
|
||||
reset();
|
||||
return haREAD_LINE;
|
||||
}
|
||||
|
||||
std::string HTTPRequest::getReplyHeaders(bool forceClose)
|
||||
{
|
||||
if (forceClose || bShouldClose)
|
||||
return "Connection: close\r\n";
|
||||
else
|
||||
return "Connection: Keep-Alive\r\n";
|
||||
}
|
||||
|
||||
HTTPRequestAction HTTPRequest::consume(boost::asio::streambuf& buf)
|
||||
{
|
||||
std::string line;
|
||||
std::istream is(&buf);
|
||||
std::getline(is, line);
|
||||
boost::trim(line);
|
||||
|
||||
// cLog(lsTRACE) << "HTTPRequest line: " << line;
|
||||
|
||||
if (eState == await_request)
|
||||
{ // VERB URL PROTO
|
||||
if (line.empty())
|
||||
return haREAD_LINE;
|
||||
sRequest = line;
|
||||
bShouldClose = sRequest.find("HTTP/1.1") == std::string::npos;
|
||||
|
||||
eState = await_header;
|
||||
return haREAD_LINE;
|
||||
}
|
||||
|
||||
if (eState == await_header)
|
||||
{ // HEADER_NAME: HEADER_BODY
|
||||
if (line.empty()) // empty line or bare \r
|
||||
{
|
||||
if (iDataSize == 0)
|
||||
{ // no body
|
||||
eState = do_request;
|
||||
return haDO_REQUEST;
|
||||
}
|
||||
eState = getting_body;
|
||||
return haREAD_RAW;
|
||||
}
|
||||
vHeaders.push_back(line);
|
||||
|
||||
size_t colon = line.find(':');
|
||||
if (colon != std::string::npos)
|
||||
{
|
||||
std::string headerName = line.substr(0, colon);
|
||||
boost::trim(headerName);
|
||||
boost::to_lower(headerName);
|
||||
|
||||
std::string headerValue = line.substr(colon+1);
|
||||
boost::trim(headerValue);
|
||||
|
||||
if (headerName == "connection")
|
||||
{
|
||||
boost::to_lower(headerValue);
|
||||
if ((headerValue == "keep-alive") || (headerValue == "keepalive"))
|
||||
bShouldClose = false;
|
||||
if (headerValue == "close")
|
||||
bShouldClose = true;
|
||||
}
|
||||
|
||||
if (headerName == "content-length")
|
||||
iDataSize = boost::lexical_cast<int>(headerValue);
|
||||
|
||||
if (headerName == "authorization")
|
||||
sAuthorization = headerValue;
|
||||
|
||||
}
|
||||
|
||||
return haREAD_LINE;
|
||||
}
|
||||
|
||||
assert(false);
|
||||
return haERROR;
|
||||
}
|
||||
61
src/cpp/ripple/HTTPRequest.h
Normal file
61
src/cpp/ripple/HTTPRequest.h
Normal file
@@ -0,0 +1,61 @@
|
||||
#ifndef HTTPREQUEST__HPP
|
||||
#define HTTPREQUEST__HPP
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <boost/asio/streambuf.hpp>
|
||||
|
||||
enum HTTPRequestAction
|
||||
{ // What the application code needs to do
|
||||
haERROR = 0,
|
||||
haREAD_LINE = 1,
|
||||
haREAD_RAW = 2,
|
||||
haDO_REQUEST = 3,
|
||||
haCLOSE_CONN = 4
|
||||
};
|
||||
|
||||
class HTTPRequest
|
||||
{ // an HTTP request in progress
|
||||
protected:
|
||||
|
||||
enum state
|
||||
{
|
||||
await_request, // We are waiting for the request line
|
||||
await_header, // We are waiting for request headers
|
||||
getting_body, // We are waiting for the body
|
||||
do_request, // We are waiting for the request to complete
|
||||
};
|
||||
|
||||
state eState;
|
||||
std::string sRequest; // VERB URL PROTO
|
||||
std::string sRequestBody;
|
||||
std::string sAuthorization;
|
||||
|
||||
std::vector<std::string> vHeaders;
|
||||
|
||||
int iDataSize;
|
||||
bool bShouldClose;
|
||||
|
||||
public:
|
||||
|
||||
HTTPRequest() : eState(await_request), iDataSize(0), bShouldClose(true) { ; }
|
||||
void reset();
|
||||
|
||||
std::string& peekBody() { return sRequestBody; }
|
||||
std::string getBody() { return sRequestBody; }
|
||||
std::string& peekRequest() { return sRequest; }
|
||||
std::string getRequest() { return sRequest; }
|
||||
std::string& peekAuth() { return sAuthorization; }
|
||||
std::string getAuth() { return sAuthorization; }
|
||||
|
||||
std::vector<std::string>& peekHeaders() { return vHeaders; }
|
||||
std::string getReplyHeaders(bool forceClose);
|
||||
|
||||
HTTPRequestAction consume(boost::asio::streambuf&);
|
||||
HTTPRequestAction requestDone(bool forceClose); // call after reply is sent
|
||||
|
||||
int getDataSize() { return iDataSize; }
|
||||
};
|
||||
|
||||
#endif
|
||||
32
src/cpp/ripple/HashPrefixes.h
Normal file
32
src/cpp/ripple/HashPrefixes.h
Normal file
@@ -0,0 +1,32 @@
|
||||
#ifndef __HASHPREFIXES__
|
||||
#define __HASHPREFIXES__
|
||||
|
||||
#include "types.h"
|
||||
|
||||
// TXN - Hash of transaction plus signature to give transaction ID
|
||||
const uint32 sHP_TransactionID = 0x54584E00;
|
||||
|
||||
// STX - Hash of inner transaction to sign
|
||||
const uint32 sHP_TransactionSign = 0x53545800;
|
||||
|
||||
// TND - Hash of transaction plus metadata
|
||||
const uint32 sHP_TransactionNode = 0x534E4400;
|
||||
|
||||
// MLN - Hash of account state
|
||||
const uint32 sHP_LeafNode = 0x4D4C4E00;
|
||||
|
||||
// MIN - Hash of inner node in tree
|
||||
const uint32 sHP_InnerNode = 0x4D494E00;
|
||||
|
||||
// LGR - Hash of ledger master data for signing
|
||||
const uint32 sHP_Ledger = 0x4C575200;
|
||||
|
||||
// VAL - Hash of validation for signing
|
||||
const uint32 sHP_Validation = 0x56414C00;
|
||||
|
||||
// PRP - Hash of proposal for signing
|
||||
const uint32 sHP_Proposal = 0x50525000;
|
||||
|
||||
#endif
|
||||
|
||||
// vim:ts=4
|
||||
175
src/cpp/ripple/HashedObject.cpp
Normal file
175
src/cpp/ripple/HashedObject.cpp
Normal file
@@ -0,0 +1,175 @@
|
||||
|
||||
#include "HashedObject.h"
|
||||
|
||||
#include <boost/lexical_cast.hpp>
|
||||
#include <boost/foreach.hpp>
|
||||
|
||||
#include "Serializer.h"
|
||||
#include "Application.h"
|
||||
#include "Log.h"
|
||||
|
||||
SETUP_LOG();
|
||||
DECLARE_INSTANCE(HashedObject);
|
||||
|
||||
HashedObjectStore::HashedObjectStore(int cacheSize, int cacheAge) :
|
||||
mCache("HashedObjectStore", cacheSize, cacheAge), mWritePending(false)
|
||||
{
|
||||
mWriteSet.reserve(128);
|
||||
}
|
||||
|
||||
|
||||
bool HashedObjectStore::store(HashedObjectType type, uint32 index,
|
||||
const std::vector<unsigned char>& data, const uint256& hash)
|
||||
{ // return: false = already in cache, true = added to cache
|
||||
if (!theApp->getHashNodeDB())
|
||||
{
|
||||
cLog(lsTRACE) << "HOS: no db";
|
||||
return true;
|
||||
}
|
||||
if (mCache.touch(hash))
|
||||
{
|
||||
cLog(lsTRACE) << "HOS: " << hash << " store: incache";
|
||||
return false;
|
||||
}
|
||||
assert(hash == Serializer::getSHA512Half(data));
|
||||
|
||||
HashedObject::pointer object = boost::make_shared<HashedObject>(type, index, data, hash);
|
||||
if (!mCache.canonicalize(hash, object))
|
||||
{
|
||||
// cLog(lsTRACE) << "Queuing write for " << hash;
|
||||
boost::mutex::scoped_lock sl(mWriteMutex);
|
||||
mWriteSet.push_back(object);
|
||||
if (!mWritePending)
|
||||
{
|
||||
mWritePending = true;
|
||||
boost::thread t(boost::bind(&HashedObjectStore::bulkWrite, this));
|
||||
t.detach();
|
||||
}
|
||||
}
|
||||
// else
|
||||
// cLog(lsTRACE) << "HOS: already had " << hash;
|
||||
return true;
|
||||
}
|
||||
|
||||
void HashedObjectStore::waitWrite()
|
||||
{
|
||||
boost::unique_lock<boost::mutex> sl(mWriteMutex);
|
||||
while (mWritePending)
|
||||
mWriteCondition.wait(sl);
|
||||
}
|
||||
|
||||
void HashedObjectStore::bulkWrite()
|
||||
{
|
||||
while (1)
|
||||
{
|
||||
std::vector< boost::shared_ptr<HashedObject> > set;
|
||||
set.reserve(128);
|
||||
|
||||
{
|
||||
boost::unique_lock<boost::mutex> sl(mWriteMutex);
|
||||
mWriteSet.swap(set);
|
||||
assert(mWriteSet.empty());
|
||||
if (set.empty())
|
||||
{
|
||||
mWritePending = false;
|
||||
mWriteCondition.notify_all();
|
||||
return;
|
||||
}
|
||||
}
|
||||
// cLog(lsTRACE) << "HOS: writing " << set.size();
|
||||
|
||||
static boost::format fExists("SELECT ObjType FROM CommittedObjects WHERE Hash = '%s';");
|
||||
static boost::format
|
||||
fAdd("INSERT INTO CommittedObjects (Hash,ObjType,LedgerIndex,Object) VALUES ('%s','%c','%u',%s);");
|
||||
|
||||
Database* db = theApp->getHashNodeDB()->getDB();
|
||||
ScopedLock sl = theApp->getHashNodeDB()->getDBLock();
|
||||
|
||||
db->executeSQL("BEGIN TRANSACTION;");
|
||||
|
||||
BOOST_FOREACH(const boost::shared_ptr<HashedObject>& it, set)
|
||||
{
|
||||
if (!SQL_EXISTS(db, boost::str(fExists % it->getHash().GetHex())))
|
||||
{
|
||||
char type;
|
||||
switch(it->getType())
|
||||
{
|
||||
case hotLEDGER: type = 'L'; break;
|
||||
case hotTRANSACTION: type = 'T'; break;
|
||||
case hotACCOUNT_NODE: type = 'A'; break;
|
||||
case hotTRANSACTION_NODE: type = 'N'; break;
|
||||
default: type = 'U';
|
||||
}
|
||||
std::string rawData;
|
||||
db->escape(&(it->getData().front()), it->getData().size(), rawData);
|
||||
db->executeSQL(boost::str(fAdd % it->getHash().GetHex() % type % it->getIndex() % rawData ));
|
||||
}
|
||||
}
|
||||
|
||||
db->executeSQL("END TRANSACTION;");
|
||||
}
|
||||
}
|
||||
|
||||
HashedObject::pointer HashedObjectStore::retrieve(const uint256& hash)
|
||||
{
|
||||
HashedObject::pointer obj;
|
||||
{
|
||||
ScopedLock sl(theApp->getHashNodeDB()->getDBLock());
|
||||
obj = mCache.fetch(hash);
|
||||
if (obj)
|
||||
{
|
||||
cLog(lsTRACE) << "HOS: " << hash << " fetch: incache";
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
|
||||
if (!theApp || !theApp->getHashNodeDB())
|
||||
return HashedObject::pointer();
|
||||
std::string sql = "SELECT * FROM CommittedObjects WHERE Hash='";
|
||||
sql.append(hash.GetHex());
|
||||
sql.append("';");
|
||||
|
||||
std::vector<unsigned char> data;
|
||||
{
|
||||
ScopedLock sl(theApp->getHashNodeDB()->getDBLock());
|
||||
Database* db = theApp->getHashNodeDB()->getDB();
|
||||
|
||||
if (!db->executeSQL(sql) || !db->startIterRows())
|
||||
{
|
||||
// cLog(lsTRACE) << "HOS: " << hash << " fetch: not in db";
|
||||
return HashedObject::pointer();
|
||||
}
|
||||
|
||||
std::string type;
|
||||
db->getStr("ObjType", type);
|
||||
if (type.size() == 0) return HashedObject::pointer();
|
||||
|
||||
uint32 index = db->getBigInt("LedgerIndex");
|
||||
|
||||
int size = db->getBinary("Object", NULL, 0);
|
||||
data.resize(size);
|
||||
db->getBinary("Object", &(data.front()), size);
|
||||
db->endIterRows();
|
||||
|
||||
assert(Serializer::getSHA512Half(data) == hash);
|
||||
|
||||
HashedObjectType htype = hotUNKNOWN;
|
||||
switch (type[0])
|
||||
{
|
||||
case 'L': htype = hotLEDGER; break;
|
||||
case 'T': htype = hotTRANSACTION; break;
|
||||
case 'A': htype = hotACCOUNT_NODE; break;
|
||||
case 'N': htype = hotTRANSACTION_NODE; break;
|
||||
default:
|
||||
cLog(lsERROR) << "Invalid hashed object";
|
||||
return HashedObject::pointer();
|
||||
}
|
||||
|
||||
obj = boost::make_shared<HashedObject>(htype, index, data, hash);
|
||||
mCache.canonicalize(hash, obj);
|
||||
}
|
||||
cLog(lsTRACE) << "HOS: " << hash << " fetch: in db";
|
||||
return obj;
|
||||
}
|
||||
|
||||
// vim:ts=4
|
||||
70
src/cpp/ripple/HashedObject.h
Normal file
70
src/cpp/ripple/HashedObject.h
Normal file
@@ -0,0 +1,70 @@
|
||||
#ifndef __HASHEDOBJECT__
|
||||
#define __HASHEDOBJECT__
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include <boost/thread/mutex.hpp>
|
||||
#include <boost/thread/condition_variable.hpp>
|
||||
|
||||
#include "types.h"
|
||||
#include "uint256.h"
|
||||
#include "ScopedLock.h"
|
||||
#include "TaggedCache.h"
|
||||
#include "InstanceCounter.h"
|
||||
|
||||
DEFINE_INSTANCE(HashedObject);
|
||||
|
||||
enum HashedObjectType
|
||||
{
|
||||
hotUNKNOWN = 0,
|
||||
hotLEDGER = 1,
|
||||
hotTRANSACTION = 2,
|
||||
hotACCOUNT_NODE = 3,
|
||||
hotTRANSACTION_NODE = 4
|
||||
};
|
||||
|
||||
class HashedObject : private IS_INSTANCE(HashedObject)
|
||||
{
|
||||
public:
|
||||
typedef boost::shared_ptr<HashedObject> pointer;
|
||||
|
||||
HashedObjectType mType;
|
||||
uint256 mHash;
|
||||
uint32 mLedgerIndex;
|
||||
std::vector<unsigned char> mData;
|
||||
|
||||
HashedObject(HashedObjectType type, uint32 index, const std::vector<unsigned char>& data, const uint256& hash) :
|
||||
mType(type), mHash(hash), mLedgerIndex(index), mData(data) { ; }
|
||||
|
||||
const std::vector<unsigned char>& getData() const { return mData; }
|
||||
const uint256& getHash() const { return mHash; }
|
||||
HashedObjectType getType() const { return mType; }
|
||||
uint32 getIndex() const { return mLedgerIndex; }
|
||||
};
|
||||
|
||||
class HashedObjectStore
|
||||
{
|
||||
protected:
|
||||
TaggedCache<uint256, HashedObject> mCache;
|
||||
|
||||
boost::mutex mWriteMutex;
|
||||
boost::condition_variable mWriteCondition;
|
||||
|
||||
std::vector< boost::shared_ptr<HashedObject> > mWriteSet;
|
||||
bool mWritePending;
|
||||
|
||||
public:
|
||||
|
||||
HashedObjectStore(int cacheSize, int cacheAge);
|
||||
|
||||
bool store(HashedObjectType type, uint32 index, const std::vector<unsigned char>& data,
|
||||
const uint256& hash);
|
||||
|
||||
HashedObject::pointer retrieve(const uint256& hash);
|
||||
|
||||
void bulkWrite();
|
||||
void waitWrite();
|
||||
void sweep() { mCache.sweep(); }
|
||||
};
|
||||
|
||||
#endif
|
||||
383
src/cpp/ripple/HttpsClient.cpp
Normal file
383
src/cpp/ripple/HttpsClient.cpp
Normal file
@@ -0,0 +1,383 @@
|
||||
//
|
||||
// Fetch a web page via https.
|
||||
//
|
||||
|
||||
#include "HttpsClient.h"
|
||||
#include "utils.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <boost/bind.hpp>
|
||||
#include <boost/date_time/posix_time/posix_time.hpp>
|
||||
#include <boost/regex.hpp>
|
||||
#include <boost/smart_ptr/shared_ptr.hpp>
|
||||
#include <boost/system/error_code.hpp>
|
||||
|
||||
using namespace boost::system;
|
||||
using namespace boost::asio;
|
||||
|
||||
HttpsClient::HttpsClient(
|
||||
boost::asio::io_service& io_service,
|
||||
const unsigned short port,
|
||||
const std::string& strPath,
|
||||
std::size_t responseMax
|
||||
) :
|
||||
mCtx(boost::asio::ssl::context::sslv23),
|
||||
mResolver(io_service),
|
||||
mSocketSsl(io_service, mCtx),
|
||||
mResponse(responseMax),
|
||||
mStrPath(strPath),
|
||||
mPort(port),
|
||||
mDeadline(io_service)
|
||||
{
|
||||
}
|
||||
|
||||
void HttpsClient::httpsGet(
|
||||
std::deque<std::string> deqSites,
|
||||
boost::posix_time::time_duration timeout,
|
||||
boost::function<void(const boost::system::error_code& ecResult, std::string& strData)> complete) {
|
||||
|
||||
mDeqSites = deqSites;
|
||||
mComplete = complete;
|
||||
mTimeout = timeout;
|
||||
|
||||
httpsNext();
|
||||
}
|
||||
|
||||
void HttpsClient::httpsNext()
|
||||
{
|
||||
// std::cerr << "Fetch: " << mDeqSites[0] << std::endl;
|
||||
boost::shared_ptr<boost::asio::ip::tcp::resolver::query> query(new boost::asio::ip::tcp::resolver::query(mDeqSites[0], boost::lexical_cast<std::string>(mPort),
|
||||
ip::resolver_query_base::numeric_service|ip::resolver_query_base::numeric_service));
|
||||
mQuery = query;
|
||||
|
||||
mCtx.set_default_verify_paths(mShutdown);
|
||||
if (mShutdown)
|
||||
{
|
||||
std::cerr << "set_default_verify_paths: " << mShutdown.message() << std::endl;
|
||||
}
|
||||
|
||||
if (!mShutdown)
|
||||
{
|
||||
mDeadline.expires_from_now(mTimeout, mShutdown);
|
||||
|
||||
// std::cerr << "expires_from_now: " << mShutdown.message() << std::endl;
|
||||
}
|
||||
|
||||
if (!mShutdown)
|
||||
{
|
||||
mDeadline.async_wait(
|
||||
boost::bind(
|
||||
&HttpsClient::ShandleDeadline,
|
||||
shared_from_this(),
|
||||
boost::asio::placeholders::error));
|
||||
}
|
||||
|
||||
if (!mShutdown)
|
||||
{
|
||||
// std::cerr << "Resolving: " << mDeqSites[0] << std::endl;
|
||||
|
||||
mResolver.async_resolve(*mQuery,
|
||||
boost::bind(
|
||||
&HttpsClient::ShandleResolve,
|
||||
shared_from_this(),
|
||||
boost::asio::placeholders::error,
|
||||
boost::asio::placeholders::iterator));
|
||||
}
|
||||
|
||||
if (mShutdown)
|
||||
invokeComplete(mShutdown);
|
||||
}
|
||||
|
||||
void HttpsClient::handleDeadline(const boost::system::error_code& ecResult)
|
||||
{
|
||||
if (ecResult == boost::asio::error::operation_aborted)
|
||||
{
|
||||
// Timer canceled because deadline no longer needed.
|
||||
// std::cerr << "Deadline cancelled." << std::endl;
|
||||
|
||||
nothing(); // Aborter is done.
|
||||
}
|
||||
else if (ecResult)
|
||||
{
|
||||
std::cerr << "Deadline error: " << mDeqSites[0] << ": " << ecResult.message() << std::endl;
|
||||
|
||||
// Can't do anything sound.
|
||||
abort();
|
||||
}
|
||||
else
|
||||
{
|
||||
boost::system::error_code ec_shutdown;
|
||||
|
||||
std::cerr << "Deadline arrived." << std::endl;
|
||||
|
||||
// Mark us as shutting down.
|
||||
// XXX Use our own error code.
|
||||
mShutdown = boost::system::error_code(errc::bad_address, system_category());
|
||||
|
||||
// Cancel any resolving.
|
||||
mResolver.cancel();
|
||||
|
||||
// Stop the transaction.
|
||||
mSocketSsl.shutdown(ec_shutdown);
|
||||
|
||||
if (ec_shutdown)
|
||||
{
|
||||
std::cerr << "Shutdown error: " << mDeqSites[0] << ": " << ec_shutdown.message() << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void HttpsClient::handleResolve(
|
||||
const boost::system::error_code& ecResult,
|
||||
boost::asio::ip::tcp::resolver::iterator itrEndpoint
|
||||
)
|
||||
{
|
||||
if (!mShutdown)
|
||||
mShutdown = ecResult;
|
||||
|
||||
if (mShutdown)
|
||||
{
|
||||
// std::cerr << "Resolve error: " << mDeqSites[0] << ": " << mShutdown.message() << std::endl;
|
||||
|
||||
invokeComplete(mShutdown);
|
||||
}
|
||||
else
|
||||
{
|
||||
// std::cerr << "Resolve complete." << std::endl;
|
||||
|
||||
boost::asio::async_connect(
|
||||
mSocketSsl.lowest_layer(),
|
||||
itrEndpoint,
|
||||
boost::bind(
|
||||
&HttpsClient::ShandleConnect,
|
||||
shared_from_this(),
|
||||
boost::asio::placeholders::error));
|
||||
}
|
||||
}
|
||||
|
||||
void HttpsClient::handleConnect(const boost::system::error_code& ecResult)
|
||||
{
|
||||
if (!mShutdown)
|
||||
mShutdown = ecResult;
|
||||
|
||||
if (mShutdown)
|
||||
{
|
||||
std::cerr << "Connect error: " << mShutdown.message() << std::endl;
|
||||
}
|
||||
|
||||
if (!mShutdown)
|
||||
{
|
||||
// std::cerr << "Connected." << std::endl;
|
||||
|
||||
mSocketSsl.set_verify_mode(boost::asio::ssl::verify_peer);
|
||||
|
||||
// XXX Verify semantics of RFC 2818 are what we want.
|
||||
mSocketSsl.set_verify_callback(boost::asio::ssl::rfc2818_verification(mDeqSites[0]), mShutdown);
|
||||
|
||||
if (mShutdown)
|
||||
{
|
||||
std::cerr << "set_verify_callback: " << mDeqSites[0] << ": " << mShutdown.message() << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
if (!mShutdown)
|
||||
{
|
||||
mSocketSsl.async_handshake(boost::asio::ssl::stream<boost::asio::ip::tcp::socket>::client,
|
||||
boost::bind(&HttpsClient::ShandleRequest,
|
||||
shared_from_this(),
|
||||
boost::asio::placeholders::error));
|
||||
}
|
||||
else
|
||||
{
|
||||
invokeComplete(mShutdown);
|
||||
}
|
||||
}
|
||||
|
||||
void HttpsClient::handleRequest(const boost::system::error_code& ecResult)
|
||||
{
|
||||
if (!mShutdown)
|
||||
mShutdown = ecResult;
|
||||
|
||||
if (mShutdown)
|
||||
{
|
||||
std::cerr << "Handshake error:" << mShutdown.message() << std::endl;
|
||||
|
||||
invokeComplete(mShutdown);
|
||||
}
|
||||
else
|
||||
{
|
||||
// std::cerr << "SSL session started." << std::endl;
|
||||
|
||||
std::ostream osRequest(&mRequest);
|
||||
|
||||
osRequest <<
|
||||
"GET " << mStrPath << " HTTP/1.0\r\n"
|
||||
"Host: " << mDeqSites[0] << "\r\n"
|
||||
"Accept: */*\r\n" // YYY Do we need this line?
|
||||
"Connection: close\r\n\r\n";
|
||||
|
||||
boost::asio::async_write(
|
||||
mSocketSsl,
|
||||
mRequest,
|
||||
boost::bind(&HttpsClient::ShandleWrite,
|
||||
shared_from_this(),
|
||||
boost::asio::placeholders::error));
|
||||
}
|
||||
}
|
||||
|
||||
void HttpsClient::handleWrite(const boost::system::error_code& ecResult)
|
||||
{
|
||||
if (!mShutdown)
|
||||
mShutdown = ecResult;
|
||||
|
||||
if (mShutdown)
|
||||
{
|
||||
std::cerr << "Write error: " << mShutdown.message() << std::endl;
|
||||
|
||||
invokeComplete(mShutdown);
|
||||
}
|
||||
else
|
||||
{
|
||||
// std::cerr << "Wrote." << std::endl;
|
||||
|
||||
boost::asio::async_read(
|
||||
mSocketSsl,
|
||||
mResponse,
|
||||
boost::asio::transfer_all(),
|
||||
boost::bind(&HttpsClient::ShandleData,
|
||||
shared_from_this(),
|
||||
boost::asio::placeholders::error));
|
||||
}
|
||||
}
|
||||
|
||||
void HttpsClient::handleData(const boost::system::error_code& ecResult)
|
||||
{
|
||||
if (!mShutdown)
|
||||
mShutdown = ecResult;
|
||||
|
||||
if (mShutdown && mShutdown != boost::asio::error::eof)
|
||||
{
|
||||
std::cerr << "Read error: " << mShutdown.message() << std::endl;
|
||||
|
||||
invokeComplete(mShutdown);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (mShutdown)
|
||||
{
|
||||
// std::cerr << "Complete." << std::endl;
|
||||
nothing();
|
||||
}
|
||||
else
|
||||
{
|
||||
// XXX According to boost example code, this is what we should expect for success.
|
||||
std::cerr << "Complete, no eof." << std::endl;
|
||||
}
|
||||
|
||||
parseData();
|
||||
}
|
||||
}
|
||||
|
||||
// Call cancel the deadline timer and invoke the completion routine.
|
||||
void HttpsClient::invokeComplete(const boost::system::error_code& ecResult, std::string strData)
|
||||
{
|
||||
boost::system::error_code ecCancel;
|
||||
|
||||
(void) mDeadline.cancel(ecCancel);
|
||||
|
||||
if (ecCancel)
|
||||
{
|
||||
std::cerr << "Deadline cancel error: " << ecCancel.message() << std::endl;
|
||||
}
|
||||
|
||||
mDeqSites.pop_front();
|
||||
|
||||
if (mDeqSites.empty())
|
||||
{
|
||||
mComplete(ecResult ? ecResult : ecCancel, strData);
|
||||
}
|
||||
else
|
||||
{
|
||||
httpsNext();
|
||||
}
|
||||
}
|
||||
|
||||
void HttpsClient::parseData()
|
||||
{
|
||||
std::string strData((std::istreambuf_iterator<char>(&mResponse)), std::istreambuf_iterator<char>());
|
||||
|
||||
static boost::regex reStatus("\\`HTTP/1\\S+ (\\d{3}) .*\\'"); // HTTP/1.1 200 OK
|
||||
static boost::regex reBody("\\`(?:.*?\\r\\n\\r\\n){1}(.*)\\'");
|
||||
|
||||
boost::smatch smMatch;
|
||||
|
||||
bool bMatch = boost::regex_match(strData, smMatch, reStatus) // Match status code.
|
||||
&& !smMatch[1].compare("200")
|
||||
&& boost::regex_match(strData, smMatch, reBody); // Match body.
|
||||
|
||||
// std::cerr << "Data:" << strData << std::endl;
|
||||
// std::cerr << "Match: " << bMatch << std::endl;
|
||||
// std::cerr << "Body:" << smMatch[1] << std::endl;
|
||||
|
||||
if (bMatch)
|
||||
{
|
||||
boost::system::error_code noErr;
|
||||
|
||||
invokeComplete(noErr, smMatch[1]);
|
||||
}
|
||||
else
|
||||
{
|
||||
// XXX Use our own error code.
|
||||
invokeComplete(boost::system::error_code(errc::bad_address, system_category()));
|
||||
}
|
||||
}
|
||||
|
||||
void HttpsClient::httpsGet(
|
||||
boost::asio::io_service& io_service,
|
||||
std::deque<std::string> deqSites,
|
||||
const unsigned short port,
|
||||
const std::string& strPath,
|
||||
std::size_t responseMax,
|
||||
boost::posix_time::time_duration timeout,
|
||||
boost::function<void(const boost::system::error_code& ecResult, std::string& strData)> complete) {
|
||||
|
||||
boost::shared_ptr<HttpsClient> client(new HttpsClient(io_service, port, strPath, responseMax));
|
||||
|
||||
client->httpsGet(deqSites, timeout, complete);
|
||||
}
|
||||
|
||||
void HttpsClient::httpsGet(
|
||||
boost::asio::io_service& io_service,
|
||||
std::string strSite,
|
||||
const unsigned short port,
|
||||
const std::string& strPath,
|
||||
std::size_t responseMax,
|
||||
boost::posix_time::time_duration timeout,
|
||||
boost::function<void(const boost::system::error_code& ecResult, std::string& strData)> complete) {
|
||||
|
||||
std::deque<std::string> deqSites(1, strSite);
|
||||
|
||||
boost::shared_ptr<HttpsClient> client(new HttpsClient(io_service, port, strPath, responseMax));
|
||||
|
||||
client->httpsGet(deqSites, timeout, complete);
|
||||
}
|
||||
|
||||
bool HttpsClient::httpsParseUrl(const std::string& strUrl, std::string& strDomain, std::string& strPath)
|
||||
{
|
||||
static boost::regex reUrl("(?i)\\`\\s*https://([^/]+)(/.*)\\s*\\'"); // https://DOMAINPATH
|
||||
|
||||
boost::smatch smMatch;
|
||||
|
||||
bool bMatch = boost::regex_match(strUrl, smMatch, reUrl); // Match status code.
|
||||
|
||||
if (bMatch)
|
||||
{
|
||||
strDomain = smMatch[1];
|
||||
strPath = smMatch[2];
|
||||
}
|
||||
// std::cerr << strUrl << " : " << bMatch << " : '" << strDomain << "' : '" << strPath << "'" << std::endl;
|
||||
|
||||
return bMatch;
|
||||
}
|
||||
|
||||
// vim:ts=4
|
||||
106
src/cpp/ripple/HttpsClient.h
Normal file
106
src/cpp/ripple/HttpsClient.h
Normal file
@@ -0,0 +1,106 @@
|
||||
#ifndef _HTTPS_CLIENT_
|
||||
#define _HTTPS_CLIENT_
|
||||
|
||||
#include <deque>
|
||||
#include <string>
|
||||
|
||||
#include <boost/asio.hpp>
|
||||
#include <boost/asio/ssl.hpp>
|
||||
#include <boost/date_time/posix_time/posix_time_types.hpp>
|
||||
#include <boost/enable_shared_from_this.hpp>
|
||||
#include <boost/function.hpp>
|
||||
#include <boost/shared_ptr.hpp>
|
||||
|
||||
|
||||
//
|
||||
// Async https client.
|
||||
//
|
||||
|
||||
class HttpsClient : public boost::enable_shared_from_this<HttpsClient>
|
||||
{
|
||||
private:
|
||||
typedef boost::shared_ptr<HttpsClient> pointer;
|
||||
|
||||
boost::asio::ssl::context mCtx;
|
||||
boost::asio::ip::tcp::resolver mResolver;
|
||||
boost::shared_ptr<boost::asio::ip::tcp::resolver::query> mQuery;
|
||||
boost::asio::ssl::stream<boost::asio::ip::tcp::socket> mSocketSsl;
|
||||
boost::asio::streambuf mRequest;
|
||||
boost::asio::streambuf mResponse;
|
||||
const std::string mStrPath;
|
||||
const unsigned short mPort;
|
||||
boost::function<void(const boost::system::error_code& ecResult, std::string& strData)> mComplete;
|
||||
|
||||
boost::asio::deadline_timer mDeadline;
|
||||
|
||||
// If not success, we are shutting down.
|
||||
boost::system::error_code mShutdown;
|
||||
|
||||
std::deque<std::string> mDeqSites;
|
||||
boost::posix_time::time_duration mTimeout;
|
||||
|
||||
void handleDeadline(const boost::system::error_code& ecResult);
|
||||
static void ShandleDeadline(pointer This, const boost::system::error_code& ecResult)
|
||||
{ This->handleDeadline(ecResult); }
|
||||
|
||||
void handleResolve(const boost::system::error_code& ecResult, boost::asio::ip::tcp::resolver::iterator endpoint_iterator);
|
||||
static void ShandleResolve(pointer This, const boost::system::error_code& ecResult, boost::asio::ip::tcp::resolver::iterator endpoint_iterator)
|
||||
{ This->handleResolve(ecResult, endpoint_iterator); }
|
||||
|
||||
void handleConnect(const boost::system::error_code& ecResult);
|
||||
static void ShandleConnect(pointer This, const boost::system::error_code& ecResult)
|
||||
{ This->handleConnect(ecResult); }
|
||||
|
||||
void handleRequest(const boost::system::error_code& ecResult);
|
||||
static void ShandleRequest(pointer This, const boost::system::error_code& ecResult)
|
||||
{ This->handleRequest(ecResult); }
|
||||
|
||||
void handleWrite(const boost::system::error_code& ecResult);
|
||||
static void ShandleWrite(pointer This, const boost::system::error_code& ecResult)
|
||||
{ This->handleWrite(ecResult); }
|
||||
|
||||
void handleData(const boost::system::error_code& ecResult);
|
||||
static void ShandleData(pointer This, const boost::system::error_code& ecResult)
|
||||
{ This->handleData(ecResult); }
|
||||
|
||||
void parseData();
|
||||
void httpsNext();
|
||||
|
||||
void invokeComplete(const boost::system::error_code& ecResult, std::string strData = "");
|
||||
|
||||
public:
|
||||
|
||||
HttpsClient(
|
||||
boost::asio::io_service& io_service,
|
||||
const unsigned short port,
|
||||
const std::string& strPath,
|
||||
std::size_t responseMax
|
||||
);
|
||||
|
||||
void httpsGet(
|
||||
std::deque<std::string> deqSites,
|
||||
boost::posix_time::time_duration timeout,
|
||||
boost::function<void(const boost::system::error_code& ecResult, std::string& strData)> complete);
|
||||
|
||||
static void httpsGet(
|
||||
boost::asio::io_service& io_service,
|
||||
std::deque<std::string> deqSites,
|
||||
const unsigned short port,
|
||||
const std::string& strPath,
|
||||
std::size_t responseMax,
|
||||
boost::posix_time::time_duration timeout,
|
||||
boost::function<void(const boost::system::error_code& ecResult, std::string& strData)> complete);
|
||||
|
||||
static void httpsGet(
|
||||
boost::asio::io_service& io_service,
|
||||
std::string strSite,
|
||||
const unsigned short port,
|
||||
const std::string& strPath,
|
||||
std::size_t responseMax,
|
||||
boost::posix_time::time_duration timeout,
|
||||
boost::function<void(const boost::system::error_code& ecResult, std::string& strData)> complete);
|
||||
|
||||
static bool httpsParseUrl(const std::string& strUrl, std::string& strDomain, std::string& strPath);
|
||||
};
|
||||
#endif
|
||||
// vim:ts=4
|
||||
15
src/cpp/ripple/InstanceCounter.cpp
Normal file
15
src/cpp/ripple/InstanceCounter.cpp
Normal file
@@ -0,0 +1,15 @@
|
||||
#include "InstanceCounter.h"
|
||||
|
||||
InstanceType* InstanceType::sHeadInstance = NULL;
|
||||
|
||||
std::vector<InstanceType::InstanceCount> InstanceType::getInstanceCounts(int min)
|
||||
{
|
||||
std::vector<InstanceCount> ret;
|
||||
for (InstanceType* i = sHeadInstance; i != NULL; i = i->mNextInstance)
|
||||
{
|
||||
int c = i->getCount();
|
||||
if (c >= min)
|
||||
ret.push_back(InstanceCount(i->getName(), c));
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
80
src/cpp/ripple/InstanceCounter.h
Normal file
80
src/cpp/ripple/InstanceCounter.h
Normal file
@@ -0,0 +1,80 @@
|
||||
#ifndef INSTANCE_COUNTER__H
|
||||
#define INSTANCE_COUNTER__H
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <boost/thread/mutex.hpp>
|
||||
|
||||
#define DEFINE_INSTANCE(x) \
|
||||
extern InstanceType IT_##x; \
|
||||
class Instance_##x : private Instance \
|
||||
{ \
|
||||
protected: \
|
||||
Instance_##x() : Instance(IT_##x) { ; } \
|
||||
Instance_##x(const Instance_##x &) : \
|
||||
Instance(IT_##x) { ; } \
|
||||
Instance_##x& operator=(const Instance_##x&) \
|
||||
{ return *this; } \
|
||||
}
|
||||
|
||||
#define DECLARE_INSTANCE(x) \
|
||||
InstanceType IT_##x(#x);
|
||||
|
||||
#define IS_INSTANCE(x) Instance_##x
|
||||
|
||||
class InstanceType
|
||||
{
|
||||
protected:
|
||||
int mInstances;
|
||||
std::string mName;
|
||||
boost::mutex mLock;
|
||||
|
||||
InstanceType* mNextInstance;
|
||||
static InstanceType* sHeadInstance;
|
||||
|
||||
public:
|
||||
typedef std::pair<std::string, int> InstanceCount;
|
||||
|
||||
InstanceType(const char *n) : mInstances(0), mName(n)
|
||||
{
|
||||
mNextInstance = sHeadInstance;
|
||||
sHeadInstance = this;
|
||||
}
|
||||
|
||||
void addInstance()
|
||||
{
|
||||
mLock.lock();
|
||||
++mInstances;
|
||||
mLock.unlock();
|
||||
}
|
||||
void decInstance()
|
||||
{
|
||||
mLock.lock();
|
||||
--mInstances;
|
||||
mLock.unlock();
|
||||
}
|
||||
int getCount()
|
||||
{
|
||||
boost::mutex::scoped_lock(mLock);
|
||||
return mInstances;
|
||||
}
|
||||
const std::string& getName()
|
||||
{
|
||||
return mName;
|
||||
}
|
||||
|
||||
static std::vector<InstanceCount> getInstanceCounts(int min = 1);
|
||||
};
|
||||
|
||||
class Instance
|
||||
{
|
||||
protected:
|
||||
InstanceType& mType;
|
||||
|
||||
public:
|
||||
Instance(InstanceType& t) : mType(t) { mType.addInstance(); }
|
||||
~Instance() { mType.decInstance(); }
|
||||
};
|
||||
|
||||
#endif
|
||||
202
src/cpp/ripple/Interpreter.cpp
Normal file
202
src/cpp/ripple/Interpreter.cpp
Normal file
@@ -0,0 +1,202 @@
|
||||
#include "Interpreter.h"
|
||||
#include "Operation.h"
|
||||
#include "Config.h"
|
||||
|
||||
/*
|
||||
We also need to charge for each op
|
||||
|
||||
*/
|
||||
|
||||
namespace Script {
|
||||
|
||||
Interpreter::Interpreter()
|
||||
{
|
||||
mContract=NULL;
|
||||
mCode=NULL;
|
||||
mInstructionPointer=0;
|
||||
mTotalFee=0;
|
||||
|
||||
mInBlock=false;
|
||||
mBlockSuccess=true;
|
||||
mBlockJump=0;
|
||||
|
||||
mFunctionTable.resize(NUM_OF_OPS);
|
||||
|
||||
mFunctionTable[INT_OP]=new IntOp();
|
||||
mFunctionTable[FLOAT_OP]=new FloatOp();
|
||||
mFunctionTable[UINT160_OP]=new Uint160Op();
|
||||
mFunctionTable[BOOL_OP]=new Uint160Op();
|
||||
mFunctionTable[PATH_OP]=new Uint160Op();
|
||||
|
||||
mFunctionTable[ADD_OP]=new AddOp();
|
||||
mFunctionTable[SUB_OP]=new SubOp();
|
||||
mFunctionTable[MUL_OP]=new MulOp();
|
||||
mFunctionTable[DIV_OP]=new DivOp();
|
||||
mFunctionTable[MOD_OP]=new ModOp();
|
||||
mFunctionTable[GTR_OP]=new GtrOp();
|
||||
mFunctionTable[LESS_OP]=new LessOp();
|
||||
mFunctionTable[EQUAL_OP]=new SubOp();
|
||||
mFunctionTable[NOT_EQUAL_OP]=new SubOp();
|
||||
mFunctionTable[AND_OP]=new SubOp();
|
||||
mFunctionTable[OR_OP]=new SubOp();
|
||||
mFunctionTable[NOT_OP]=new SubOp();
|
||||
mFunctionTable[JUMP_OP]=new SubOp();
|
||||
mFunctionTable[JUMPIF_OP]=new SubOp();
|
||||
mFunctionTable[STOP_OP]=new SubOp();
|
||||
mFunctionTable[CANCEL_OP]=new SubOp();
|
||||
mFunctionTable[BLOCK_OP]=new SubOp();
|
||||
mFunctionTable[BLOCK_END_OP]=new SubOp();
|
||||
mFunctionTable[SEND_XRP_OP]=new SendXRPOp();
|
||||
/*
|
||||
mFunctionTable[SEND_OP]=new SendOp();
|
||||
mFunctionTable[REMOVE_CONTRACT_OP]=new SubOp();
|
||||
mFunctionTable[FEE_OP]=new SubOp();
|
||||
mFunctionTable[CHANGE_CONTRACT_OWNER_OP]=new SubOp();
|
||||
mFunctionTable[STOP_REMOVE_OP]=new SubOp();
|
||||
mFunctionTable[SET_DATA_OP]=new SubOp();
|
||||
mFunctionTable[GET_DATA_OP]=new SubOp();
|
||||
mFunctionTable[GET_NUM_DATA_OP]=new SubOp();
|
||||
mFunctionTable[SET_REGISTER_OP]=new SubOp();
|
||||
mFunctionTable[GET_REGISTER_OP]=new SubOp();
|
||||
mFunctionTable[GET_ISSUER_ID_OP]=new SubOp();
|
||||
mFunctionTable[GET_OWNER_ID_OP]=new SubOp();
|
||||
mFunctionTable[GET_LEDGER_TIME_OP]=new SubOp();
|
||||
mFunctionTable[GET_LEDGER_NUM_OP]=new SubOp();
|
||||
mFunctionTable[GET_RAND_FLOAT_OP]=new SubOp();
|
||||
mFunctionTable[GET_XRP_ESCROWED_OP]=new SubOp();
|
||||
mFunctionTable[GET_RIPPLE_ESCROWED_OP]=new SubOp();
|
||||
mFunctionTable[GET_RIPPLE_ESCROWED_CURRENCY_OP]=new SubOp();
|
||||
mFunctionTable[GET_RIPPLE_ESCROWED_ISSUER]=new GetRippleEscrowedIssuerOp();
|
||||
mFunctionTable[GET_ACCEPT_DATA_OP]=new AcceptDataOp();
|
||||
mFunctionTable[GET_ACCEPTOR_ID_OP]=new GetAcceptorIDOp();
|
||||
mFunctionTable[GET_CONTRACT_ID_OP]=new GetContractIDOp();
|
||||
*/
|
||||
|
||||
}
|
||||
|
||||
Data::pointer Interpreter::popStack()
|
||||
{
|
||||
if(mStack.size())
|
||||
{
|
||||
Data::pointer item=mStack[mStack.size()-1];
|
||||
mStack.pop_back();
|
||||
return(item);
|
||||
}else
|
||||
{
|
||||
return(Data::pointer(new ErrorData()));
|
||||
}
|
||||
}
|
||||
|
||||
void Interpreter::pushStack(Data::pointer data)
|
||||
{
|
||||
mStack.push_back(data);
|
||||
}
|
||||
|
||||
|
||||
// offset is where to jump to if the block fails
|
||||
bool Interpreter::startBlock(int offset)
|
||||
{
|
||||
if(mInBlock) return(false); // can't nest blocks
|
||||
mBlockSuccess=true;
|
||||
mInBlock=true;
|
||||
mBlockJump=offset+mInstructionPointer;
|
||||
return(true);
|
||||
}
|
||||
|
||||
bool Interpreter::endBlock()
|
||||
{
|
||||
if(!mInBlock) return(false);
|
||||
mInBlock=false;
|
||||
mBlockJump=0;
|
||||
pushStack(Data::pointer(new BoolData(mBlockSuccess)));
|
||||
return(true);
|
||||
}
|
||||
|
||||
TER Interpreter::interpret(Contract* contract,const SerializedTransaction& txn,std::vector<unsigned char>& code)
|
||||
{
|
||||
mContract=contract;
|
||||
mCode=&code;
|
||||
mTotalFee=0;
|
||||
mInstructionPointer=0;
|
||||
while(mInstructionPointer<code.size())
|
||||
{
|
||||
unsigned int fun=(*mCode)[mInstructionPointer];
|
||||
mInstructionPointer++;
|
||||
|
||||
if(fun>=mFunctionTable.size())
|
||||
{
|
||||
// TODO: log
|
||||
return(temMALFORMED); // TODO: is this actually what we want to do?
|
||||
}
|
||||
|
||||
mTotalFee += mFunctionTable[ fun ]->getFee();
|
||||
if(mTotalFee>txn.getTransactionFee().getNValue())
|
||||
{
|
||||
// TODO: log
|
||||
return(telINSUF_FEE_P);
|
||||
}else
|
||||
{
|
||||
if(!mFunctionTable[ fun ]->work(this))
|
||||
{
|
||||
// TODO: log
|
||||
return(temMALFORMED); // TODO: is this actually what we want to do?
|
||||
}
|
||||
}
|
||||
}
|
||||
return(tesSUCCESS);
|
||||
}
|
||||
|
||||
|
||||
Data::pointer Interpreter::getIntData()
|
||||
{
|
||||
int value=0; // TODO
|
||||
mInstructionPointer += 4;
|
||||
return(Data::pointer(new IntData(value)));
|
||||
}
|
||||
|
||||
Data::pointer Interpreter::getFloatData()
|
||||
{
|
||||
float value=0; // TODO
|
||||
mInstructionPointer += 4;
|
||||
return(Data::pointer(new FloatData(value)));
|
||||
}
|
||||
|
||||
Data::pointer Interpreter::getUint160Data()
|
||||
{
|
||||
uint160 value; // TODO
|
||||
mInstructionPointer += 20;
|
||||
return(Data::pointer(new Uint160Data(value)));
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool Interpreter::jumpTo(int offset)
|
||||
{
|
||||
mInstructionPointer += offset;
|
||||
if( (mInstructionPointer<0) || (mInstructionPointer>mCode->size()) )
|
||||
{
|
||||
mInstructionPointer -= offset;
|
||||
return(false);
|
||||
}
|
||||
return(true);
|
||||
}
|
||||
|
||||
void Interpreter::stop()
|
||||
{
|
||||
mInstructionPointer=mCode->size();
|
||||
}
|
||||
|
||||
Data::pointer Interpreter::getContractData(int index)
|
||||
{
|
||||
return(Data::pointer(new ErrorData()));
|
||||
}
|
||||
|
||||
bool Interpreter::canSign(const uint160& signer)
|
||||
{
|
||||
return(true);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
} // end namespace
|
||||
82
src/cpp/ripple/Interpreter.h
Normal file
82
src/cpp/ripple/Interpreter.h
Normal file
@@ -0,0 +1,82 @@
|
||||
#ifndef __INTERPRETER__
|
||||
#define __INTERPRETER__
|
||||
|
||||
#include "uint256.h"
|
||||
#include "Contract.h"
|
||||
#include <boost/shared_ptr.hpp>
|
||||
#include <vector>
|
||||
#include "ScriptData.h"
|
||||
#include "TransactionEngine.h"
|
||||
|
||||
namespace Script {
|
||||
|
||||
class Operation;
|
||||
// Contracts are non typed have variable data types
|
||||
|
||||
class Interpreter
|
||||
{
|
||||
std::vector<Operation*> mFunctionTable;
|
||||
|
||||
std::vector<Data::pointer> mStack;
|
||||
|
||||
Contract* mContract;
|
||||
std::vector<unsigned char>* mCode;
|
||||
unsigned int mInstructionPointer;
|
||||
int mTotalFee;
|
||||
|
||||
bool mInBlock;
|
||||
int mBlockJump;
|
||||
bool mBlockSuccess;
|
||||
|
||||
public:
|
||||
|
||||
|
||||
enum { INT_OP=1,FLOAT_OP,UINT160_OP,BOOL_OP,PATH_OP,
|
||||
ADD_OP,SUB_OP,MUL_OP,DIV_OP,MOD_OP,
|
||||
GTR_OP,LESS_OP,EQUAL_OP,NOT_EQUAL_OP,
|
||||
AND_OP,OR_OP,NOT_OP,
|
||||
JUMP_OP, JUMPIF_OP,
|
||||
STOP_OP, CANCEL_OP,
|
||||
|
||||
BLOCK_OP, BLOCK_END_OP,
|
||||
SEND_XRP_OP,SEND_OP,REMOVE_CONTRACT_OP,FEE_OP,CHANGE_CONTRACT_OWNER_OP,
|
||||
STOP_REMOVE_OP,
|
||||
SET_DATA_OP,GET_DATA_OP, GET_NUM_DATA_OP,
|
||||
SET_REGISTER_OP,GET_REGISTER_OP,
|
||||
GET_ISSUER_ID_OP, GET_OWNER_ID_OP, GET_LEDGER_TIME_OP, GET_LEDGER_NUM_OP, GET_RAND_FLOAT_OP,
|
||||
GET_XRP_ESCROWED_OP, GET_RIPPLE_ESCROWED_OP, GET_RIPPLE_ESCROWED_CURRENCY_OP, GET_RIPPLE_ESCROWED_ISSUER,
|
||||
GET_ACCEPT_DATA_OP, GET_ACCEPTOR_ID_OP, GET_CONTRACT_ID_OP,
|
||||
NUM_OF_OPS };
|
||||
|
||||
Interpreter();
|
||||
|
||||
// returns a TransactionEngineResult
|
||||
TER interpret(Contract* contract,const SerializedTransaction& txn,std::vector<unsigned char>& code);
|
||||
|
||||
void stop();
|
||||
|
||||
bool canSign(const uint160& signer);
|
||||
|
||||
int getInstructionPointer(){ return(mInstructionPointer); }
|
||||
void setInstructionPointer(int n){ mInstructionPointer=n;}
|
||||
|
||||
Data::pointer popStack();
|
||||
void pushStack(Data::pointer data);
|
||||
bool jumpTo(int offset);
|
||||
|
||||
bool startBlock(int offset);
|
||||
bool endBlock();
|
||||
|
||||
|
||||
Data::pointer getIntData();
|
||||
Data::pointer getFloatData();
|
||||
Data::pointer getUint160Data();
|
||||
Data::pointer getAcceptData(int index);
|
||||
Data::pointer getContractData(int index);
|
||||
|
||||
|
||||
};
|
||||
|
||||
} // end namespace
|
||||
|
||||
#endif
|
||||
181
src/cpp/ripple/JobQueue.cpp
Normal file
181
src/cpp/ripple/JobQueue.cpp
Normal file
@@ -0,0 +1,181 @@
|
||||
#include "JobQueue.h"
|
||||
|
||||
#include <boost/make_shared.hpp>
|
||||
#include <boost/foreach.hpp>
|
||||
#include <boost/bind.hpp>
|
||||
#include <boost/thread.hpp>
|
||||
|
||||
#include "Log.h"
|
||||
|
||||
SETUP_LOG();
|
||||
|
||||
const char* Job::toString(JobType t)
|
||||
{
|
||||
switch(t)
|
||||
{
|
||||
case jtINVALID: return "invalid";
|
||||
case jtVALIDATION_ut: return "untrustedValidation";
|
||||
case jtTRANSACTION: return "transaction";
|
||||
case jtPROPOSAL_ut: return "untrustedProposal";
|
||||
case jtVALIDATION_t: return "trustedValidation";
|
||||
case jtPROPOSAL_t: return "trustedProposal";
|
||||
case jtADMIN: return "administration";
|
||||
case jtDEATH: return "jobOfDeath";
|
||||
default: assert(false); return "unknown";
|
||||
}
|
||||
}
|
||||
|
||||
bool Job::operator<(const Job& j) const
|
||||
{ // These comparison operators make the jobs sort in priority order in the job set
|
||||
if (mType < j.mType)
|
||||
return true;
|
||||
if (mType > j.mType)
|
||||
return false;
|
||||
return mJobIndex < j.mJobIndex;
|
||||
}
|
||||
|
||||
bool Job::operator<=(const Job& j) const
|
||||
{
|
||||
if (mType < j.mType)
|
||||
return true;
|
||||
if (mType > j.mType)
|
||||
return false;
|
||||
return mJobIndex <= j.mJobIndex;
|
||||
}
|
||||
|
||||
bool Job::operator>(const Job& j) const
|
||||
{
|
||||
if (mType < j.mType)
|
||||
return false;
|
||||
if (mType > j.mType)
|
||||
return true;
|
||||
return mJobIndex > j.mJobIndex;
|
||||
}
|
||||
|
||||
bool Job::operator>=(const Job& j) const
|
||||
{
|
||||
if (mType < j.mType)
|
||||
return false;
|
||||
if (mType > j.mType)
|
||||
return true;
|
||||
return mJobIndex >= j.mJobIndex;
|
||||
}
|
||||
|
||||
void JobQueue::addJob(JobType type, const boost::function<void(Job&)>& jobFunc)
|
||||
{
|
||||
assert(type != jtINVALID);
|
||||
|
||||
boost::mutex::scoped_lock sl(mJobLock);
|
||||
assert(mThreadCount != 0); // do not add jobs to a queue with no threads
|
||||
|
||||
mJobSet.insert(Job(type, ++mLastJob, jobFunc));
|
||||
++mJobCounts[type];
|
||||
mJobCond.notify_one();
|
||||
}
|
||||
|
||||
int JobQueue::getJobCount(JobType t)
|
||||
{
|
||||
boost::mutex::scoped_lock sl(mJobLock);
|
||||
|
||||
std::map<JobType, int>::iterator c = mJobCounts.find(t);
|
||||
return (c == mJobCounts.end()) ? 0 : c->second;
|
||||
}
|
||||
|
||||
int JobQueue::getJobCountGE(JobType t)
|
||||
{ // return the number of jobs at this priority level or greater
|
||||
int ret = 0;
|
||||
|
||||
boost::mutex::scoped_lock sl(mJobLock);
|
||||
|
||||
typedef std::pair<JobType, int> jt_int_pair;
|
||||
BOOST_FOREACH(const jt_int_pair& it, mJobCounts)
|
||||
if (it.first >= t)
|
||||
ret += it.second;
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::vector< std::pair<JobType, int> > JobQueue::getJobCounts()
|
||||
{ // return all jobs at all priority levels
|
||||
std::vector< std::pair<JobType, int> > ret;
|
||||
|
||||
boost::mutex::scoped_lock sl(mJobLock);
|
||||
ret.reserve(mJobCounts.size());
|
||||
|
||||
typedef std::pair<JobType, int> jt_int_pair;
|
||||
BOOST_FOREACH(const jt_int_pair& it, mJobCounts)
|
||||
ret.push_back(it);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void JobQueue::shutdown()
|
||||
{ // shut down the job queue without completing pending jobs
|
||||
cLog(lsINFO) << "Job queue shutting down";
|
||||
boost::mutex::scoped_lock sl(mJobLock);
|
||||
mShuttingDown = true;
|
||||
mJobCond.notify_all();
|
||||
while (mThreadCount != 0)
|
||||
mJobCond.wait(sl);
|
||||
}
|
||||
|
||||
void JobQueue::setThreadCount(int c)
|
||||
{ // set the number of thread serving the job queue to precisely this number
|
||||
if (c == 0)
|
||||
{
|
||||
c = boost::thread::hardware_concurrency();
|
||||
if (c < 2)
|
||||
c = 2;
|
||||
cLog(lsINFO) << "Auto-tuning to " << c << " validation/transaction/proposal threads";
|
||||
}
|
||||
|
||||
boost::mutex::scoped_lock sl(mJobLock);
|
||||
|
||||
while (mJobCounts[jtDEATH] != 0)
|
||||
mJobCond.wait(sl);
|
||||
|
||||
while (mThreadCount < c)
|
||||
{
|
||||
++mThreadCount;
|
||||
boost::thread t(boost::bind(&JobQueue::threadEntry, this));
|
||||
t.detach();
|
||||
}
|
||||
while (mThreadCount > c)
|
||||
{
|
||||
if (mJobCounts[jtDEATH] != 0)
|
||||
mJobCond.wait(sl);
|
||||
else
|
||||
{
|
||||
mJobSet.insert(Job(jtDEATH, 0));
|
||||
++mJobCounts[jtDEATH];
|
||||
}
|
||||
}
|
||||
mJobCond.notify_one(); // in case we sucked up someone else's signal
|
||||
}
|
||||
|
||||
void JobQueue::threadEntry()
|
||||
{ // do jobs until asked to stop
|
||||
boost::mutex::scoped_lock sl(mJobLock);
|
||||
while (1)
|
||||
{
|
||||
while (mJobSet.empty() && !mShuttingDown)
|
||||
mJobCond.wait(sl);
|
||||
|
||||
if (mShuttingDown)
|
||||
break;
|
||||
|
||||
std::set<Job>::iterator it = mJobSet.begin();
|
||||
Job job(*it);
|
||||
mJobSet.erase(it);
|
||||
--mJobCounts[job.getType()];
|
||||
|
||||
if (job.getType() == jtDEATH)
|
||||
break;
|
||||
|
||||
sl.unlock();
|
||||
cLog(lsDEBUG) << "Doing " << Job::toString(job.getType()) << " job";
|
||||
job.doJob();
|
||||
sl.lock();
|
||||
}
|
||||
--mThreadCount;
|
||||
mJobCond.notify_all();
|
||||
}
|
||||
83
src/cpp/ripple/JobQueue.h
Normal file
83
src/cpp/ripple/JobQueue.h
Normal file
@@ -0,0 +1,83 @@
|
||||
#ifndef JOB_QUEUE__H
|
||||
#define JOB_QUEUE__H
|
||||
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <vector>
|
||||
|
||||
#include <boost/thread/mutex.hpp>
|
||||
#include <boost/thread/condition_variable.hpp>
|
||||
#include <boost/function.hpp>
|
||||
|
||||
#include "types.h"
|
||||
|
||||
// Note that this queue should only be used for CPU-bound jobs
|
||||
// It is primarily intended for signature checking
|
||||
|
||||
enum JobType
|
||||
{ // must be in priority order, low to high
|
||||
jtINVALID,
|
||||
jtVALIDATION_ut,
|
||||
jtTRANSACTION,
|
||||
jtPROPOSAL_ut,
|
||||
jtVALIDATION_t,
|
||||
jtPROPOSAL_t,
|
||||
jtADMIN,
|
||||
jtDEATH, // job of death, used internally
|
||||
};
|
||||
|
||||
class Job
|
||||
{
|
||||
protected:
|
||||
JobType mType;
|
||||
uint64 mJobIndex;
|
||||
boost::function<void(Job&)> mJob;
|
||||
|
||||
public:
|
||||
Job() : mType(jtINVALID), mJobIndex(0) { ; }
|
||||
Job(JobType type, uint64 index) : mType(type), mJobIndex(index) { ; }
|
||||
|
||||
Job(JobType type, uint64 index, const boost::function<void(Job&)>& job)
|
||||
: mType(type), mJobIndex(index), mJob(job) { ; }
|
||||
|
||||
JobType getType() const { return mType; }
|
||||
void doJob(void) { mJob(*this); }
|
||||
|
||||
bool operator<(const Job& j) const;
|
||||
bool operator>(const Job& j) const;
|
||||
bool operator<=(const Job& j) const;
|
||||
bool operator>=(const Job& j) const;
|
||||
|
||||
static const char* toString(JobType);
|
||||
};
|
||||
|
||||
class JobQueue
|
||||
{
|
||||
protected:
|
||||
boost::mutex mJobLock;
|
||||
boost::condition_variable mJobCond;
|
||||
|
||||
uint64 mLastJob;
|
||||
std::set<Job> mJobSet;
|
||||
std::map<JobType, int> mJobCounts;
|
||||
int mThreadCount;
|
||||
bool mShuttingDown;
|
||||
|
||||
|
||||
void threadEntry(void);
|
||||
|
||||
public:
|
||||
|
||||
JobQueue() : mLastJob(0), mThreadCount(0), mShuttingDown(false) { ; }
|
||||
|
||||
void addJob(JobType type, const boost::function<void(Job&)>& job);
|
||||
|
||||
int getJobCount(JobType t); // Jobs at this priority
|
||||
int getJobCountGE(JobType t); // All jobs at or greater than this priority
|
||||
std::vector< std::pair<JobType, int> > getJobCounts();
|
||||
|
||||
void shutdown();
|
||||
void setThreadCount(int c = 0);
|
||||
};
|
||||
|
||||
#endif
|
||||
1093
src/cpp/ripple/Ledger.cpp
Normal file
1093
src/cpp/ripple/Ledger.cpp
Normal file
File diff suppressed because it is too large
Load Diff
315
src/cpp/ripple/Ledger.h
Normal file
315
src/cpp/ripple/Ledger.h
Normal file
@@ -0,0 +1,315 @@
|
||||
#ifndef __LEDGER__
|
||||
#define __LEDGER__
|
||||
|
||||
#include <map>
|
||||
#include <list>
|
||||
|
||||
#include <boost/shared_ptr.hpp>
|
||||
#include <boost/enable_shared_from_this.hpp>
|
||||
#include <boost/date_time/posix_time/posix_time.hpp>
|
||||
|
||||
#include "../json/value.h"
|
||||
|
||||
#include "Transaction.h"
|
||||
#include "TransactionMeta.h"
|
||||
#include "AccountState.h"
|
||||
#include "RippleState.h"
|
||||
#include "NicknameState.h"
|
||||
#include "types.h"
|
||||
#include "BitcoinUtil.h"
|
||||
#include "SHAMap.h"
|
||||
#include "InstanceCounter.h"
|
||||
|
||||
enum LedgerStateParms
|
||||
{
|
||||
lepNONE = 0, // no special flags
|
||||
|
||||
// input flags
|
||||
lepCREATE = 1, // Create if not present
|
||||
|
||||
// output flags
|
||||
lepOKAY = 2, // success
|
||||
lepMISSING = 4, // No node in that slot
|
||||
lepWRONGTYPE = 8, // Node of different type there
|
||||
lepCREATED = 16, // Node was created
|
||||
lepERROR = 32, // error
|
||||
};
|
||||
|
||||
#define LEDGER_JSON_DUMP_TXRP 0x10000000
|
||||
#define LEDGER_JSON_DUMP_STATE 0x20000000
|
||||
#define LEDGER_JSON_FULL 0x40000000
|
||||
|
||||
DEFINE_INSTANCE(Ledger);
|
||||
|
||||
class Ledger : public boost::enable_shared_from_this<Ledger>, public IS_INSTANCE(Ledger)
|
||||
{ // The basic Ledger structure, can be opened, closed, or synching
|
||||
friend class TransactionEngine;
|
||||
public:
|
||||
typedef boost::shared_ptr<Ledger> pointer;
|
||||
typedef const boost::shared_ptr<Ledger>& ref;
|
||||
|
||||
enum TransResult
|
||||
{
|
||||
TR_ERROR = -1,
|
||||
TR_SUCCESS = 0,
|
||||
TR_NOTFOUND = 1,
|
||||
TR_ALREADY = 2,
|
||||
TR_BADTRANS = 3, // the transaction itself is corrupt
|
||||
TR_BADACCT = 4, // one of the accounts is invalid
|
||||
TR_INSUFF = 5, // the sending(apply)/receiving(remove) account is broke
|
||||
TR_PASTASEQ = 6, // account is past this transaction
|
||||
TR_PREASEQ = 7, // account is missing transactions before this
|
||||
TR_BADLSEQ = 8, // ledger too early
|
||||
TR_TOOSMALL = 9, // amount is less than Tx fee
|
||||
};
|
||||
|
||||
// ledger close flags
|
||||
static const uint32 sLCF_NoConsensusTime = 1;
|
||||
|
||||
private:
|
||||
|
||||
uint256 mHash, mParentHash, mTransHash, mAccountHash;
|
||||
uint64 mTotCoins;
|
||||
uint32 mLedgerSeq;
|
||||
uint32 mCloseTime; // when this ledger closed
|
||||
uint32 mParentCloseTime; // when the previous ledger closed
|
||||
int mCloseResolution; // the resolution for this ledger close time (2-120 seconds)
|
||||
uint32 mCloseFlags; // flags indicating how this ledger close took place
|
||||
bool mClosed, mValidHash, mAccepted, mImmutable;
|
||||
|
||||
SHAMap::pointer mTransactionMap, mAccountStateMap;
|
||||
|
||||
mutable boost::recursive_mutex mLock;
|
||||
|
||||
static int sPendingSaves;
|
||||
static boost::recursive_mutex sPendingSaveLock;
|
||||
|
||||
Ledger(const Ledger&); // no implementation
|
||||
Ledger& operator=(const Ledger&); // no implementation
|
||||
|
||||
protected:
|
||||
SLE::pointer getASNode(LedgerStateParms& parms, const uint256& nodeID, LedgerEntryType let);
|
||||
|
||||
static void incPendingSaves();
|
||||
static void decPendingSaves();
|
||||
void saveAcceptedLedger(bool fromConsensus);
|
||||
|
||||
public:
|
||||
Ledger(const RippleAddress& masterID, uint64 startAmount); // used for the starting bootstrap ledger
|
||||
|
||||
Ledger(const uint256 &parentHash, const uint256 &transHash, const uint256 &accountHash,
|
||||
uint64 totCoins, uint32 closeTime, uint32 parentCloseTime, int closeFlags, int closeResolution,
|
||||
uint32 ledgerSeq); // used for database ledgers
|
||||
|
||||
Ledger(const std::vector<unsigned char>& rawLedger);
|
||||
|
||||
Ledger(const std::string& rawLedger);
|
||||
|
||||
Ledger(bool dummy, Ledger& previous); // ledger after this one
|
||||
|
||||
Ledger(Ledger& target, bool isMutable); // snapshot
|
||||
|
||||
static Ledger::pointer getSQL(const std::string& sqlStatement);
|
||||
static Ledger::pointer getLastFullLedger();
|
||||
static int getPendingSaves();
|
||||
|
||||
void updateHash();
|
||||
void setClosed() { mClosed = true; }
|
||||
void setAccepted(uint32 closeTime, int closeResolution, bool correctCloseTime);
|
||||
void setAccepted();
|
||||
void setImmutable() { updateHash(); mImmutable = true; }
|
||||
bool isClosed() { return mClosed; }
|
||||
bool isAccepted() { return mAccepted; }
|
||||
bool isImmutable() { return mImmutable; }
|
||||
|
||||
// ledger signature operations
|
||||
void addRaw(Serializer &s) const;
|
||||
void setRaw(Serializer& s);
|
||||
|
||||
uint256 getHash();
|
||||
const uint256& getParentHash() const { return mParentHash; }
|
||||
const uint256& getTransHash() const { return mTransHash; }
|
||||
const uint256& getAccountHash() const { return mAccountHash; }
|
||||
uint64 getTotalCoins() const { return mTotCoins; }
|
||||
void destroyCoins(uint64 fee) { mTotCoins -= fee; }
|
||||
uint32 getCloseTimeNC() const { return mCloseTime; }
|
||||
uint32 getParentCloseTimeNC() const { return mParentCloseTime; }
|
||||
uint32 getLedgerSeq() const { return mLedgerSeq; }
|
||||
int getCloseResolution() const { return mCloseResolution; }
|
||||
bool getCloseAgree() const { return (mCloseFlags & sLCF_NoConsensusTime) == 0; }
|
||||
|
||||
// close time functions
|
||||
void setCloseTime(uint32 ct) { assert(!mImmutable); mCloseTime = ct; }
|
||||
void setCloseTime(boost::posix_time::ptime);
|
||||
boost::posix_time::ptime getCloseTime() const;
|
||||
|
||||
// low level functions
|
||||
SHAMap::ref peekTransactionMap() { return mTransactionMap; }
|
||||
SHAMap::ref peekAccountStateMap() { return mAccountStateMap; }
|
||||
|
||||
// ledger sync functions
|
||||
void setAcquiring(void);
|
||||
bool isAcquiring(void);
|
||||
bool isAcquiringTx(void);
|
||||
bool isAcquiringAS(void);
|
||||
|
||||
// Transaction Functions
|
||||
bool addTransaction(const uint256& id, const Serializer& txn);
|
||||
bool addTransaction(const uint256& id, const Serializer& txn, const Serializer& metaData);
|
||||
bool hasTransaction(const uint256& TransID) const { return mTransactionMap->hasItem(TransID); }
|
||||
Transaction::pointer getTransaction(const uint256& transID) const;
|
||||
bool getTransaction(const uint256& transID, Transaction::pointer& txn, TransactionMetaSet::pointer& txMeta);
|
||||
static SerializedTransaction::pointer getSTransaction(SHAMapItem::ref, SHAMapTreeNode::TNType);
|
||||
|
||||
// high-level functions
|
||||
AccountState::pointer getAccountState(const RippleAddress& acctID);
|
||||
LedgerStateParms writeBack(LedgerStateParms parms, SLE::ref);
|
||||
SLE::pointer getAccountRoot(const uint160& accountID);
|
||||
SLE::pointer getAccountRoot(const RippleAddress& naAccountID);
|
||||
|
||||
// database functions
|
||||
static Ledger::pointer loadByIndex(uint32 ledgerIndex);
|
||||
static Ledger::pointer loadByHash(const uint256& ledgerHash);
|
||||
void pendSave(bool fromConsensus);
|
||||
|
||||
// next/prev function
|
||||
SLE::pointer getSLE(const uint256& uHash);
|
||||
uint256 getFirstLedgerIndex();
|
||||
uint256 getLastLedgerIndex();
|
||||
uint256 getNextLedgerIndex(const uint256& uHash); // first node >hash
|
||||
uint256 getNextLedgerIndex(const uint256& uHash, const uint256& uEnd); // first node >hash, <end
|
||||
uint256 getPrevLedgerIndex(const uint256& uHash); // last node <hash
|
||||
uint256 getPrevLedgerIndex(const uint256& uHash, const uint256& uBegin); // last node <hash, >begin
|
||||
|
||||
// Ledger hash table function
|
||||
static uint256 getLedgerHashIndex();
|
||||
static uint256 getLedgerHashIndex(uint32 desiredLedgerIndex);
|
||||
static int getLedgerHashOffset(uint32 desiredLedgerIndex);
|
||||
static int getLedgerHashOffset(uint32 desiredLedgerIndex, uint32 currentLedgerIndex);
|
||||
|
||||
// index calculation functions
|
||||
static uint256 getAccountRootIndex(const uint160& uAccountID);
|
||||
|
||||
static uint256 getAccountRootIndex(const RippleAddress& account)
|
||||
{ return getAccountRootIndex(account.getAccountID()); }
|
||||
|
||||
//
|
||||
// Generator Map functions
|
||||
//
|
||||
|
||||
SLE::pointer getGenerator(LedgerStateParms& parms, const uint160& uGeneratorID);
|
||||
|
||||
static uint256 getGeneratorIndex(const uint160& uGeneratorID);
|
||||
|
||||
//
|
||||
// Nickname functions
|
||||
//
|
||||
|
||||
static uint256 getNicknameHash(const std::string& strNickname)
|
||||
{ Serializer s(strNickname); return s.getSHA256(); }
|
||||
|
||||
NicknameState::pointer getNicknameState(const uint256& uNickname);
|
||||
NicknameState::pointer getNicknameState(const std::string& strNickname)
|
||||
{ return getNicknameState(getNicknameHash(strNickname)); }
|
||||
|
||||
SLE::pointer getNickname(LedgerStateParms& parms, const uint256& uNickname);
|
||||
SLE::pointer getNickname(LedgerStateParms& parms, const std::string& strNickname)
|
||||
{ return getNickname(parms, getNicknameHash(strNickname)); }
|
||||
|
||||
static uint256 getNicknameIndex(const uint256& uNickname);
|
||||
|
||||
//
|
||||
// Order book functions
|
||||
//
|
||||
|
||||
// Order book dirs have a base so we can use next to step through them in quality order.
|
||||
static uint256 getBookBase(const uint160& uTakerPaysCurrency, const uint160& uTakerPaysIssuerID,
|
||||
const uint160& uTakerGetsCurrency, const uint160& uTakerGetsIssuerID);
|
||||
|
||||
//
|
||||
// Offer functions
|
||||
//
|
||||
|
||||
SLE::pointer getOffer(LedgerStateParms& parms, const uint256& uIndex);
|
||||
|
||||
SLE::pointer getOffer(const uint256& uIndex)
|
||||
{
|
||||
LedgerStateParms qry = lepNONE;
|
||||
return getOffer(qry, uIndex);
|
||||
}
|
||||
|
||||
SLE::pointer getOffer(LedgerStateParms& parms, const uint160& uAccountID, uint32 uSequence)
|
||||
{ return getOffer(parms, getOfferIndex(uAccountID, uSequence)); }
|
||||
|
||||
// The index of an offer.
|
||||
static uint256 getOfferIndex(const uint160& uAccountID, uint32 uSequence);
|
||||
|
||||
//
|
||||
// Owner functions
|
||||
//
|
||||
|
||||
// All items controlled by an account are here: offers
|
||||
static uint256 getOwnerDirIndex(const uint160& uAccountID);
|
||||
|
||||
//
|
||||
// Directory functions
|
||||
// Directories are doubly linked lists of nodes.
|
||||
|
||||
// Given a directory root and and index compute the index of a node.
|
||||
static uint256 getDirNodeIndex(const uint256& uDirRoot, const uint64 uNodeIndex=0);
|
||||
|
||||
// Return a node: root or normal
|
||||
SLE::pointer getDirNode(LedgerStateParms& parms, const uint256& uNodeIndex);
|
||||
|
||||
//
|
||||
// Quality
|
||||
//
|
||||
|
||||
static uint256 getQualityIndex(const uint256& uBase, const uint64 uNodeDir=0);
|
||||
static uint256 getQualityNext(const uint256& uBase);
|
||||
static uint64 getQuality(const uint256& uBase);
|
||||
|
||||
//
|
||||
// Ripple functions : credit lines
|
||||
//
|
||||
|
||||
// Index of node which is the ripple state between two accounts for a currency.
|
||||
static uint256 getRippleStateIndex(const RippleAddress& naA, const RippleAddress& naB, const uint160& uCurrency);
|
||||
static uint256 getRippleStateIndex(const uint160& uiA, const uint160& uiB, const uint160& uCurrency)
|
||||
{ return getRippleStateIndex(RippleAddress::createAccountID(uiA), RippleAddress::createAccountID(uiB), uCurrency); }
|
||||
|
||||
RippleState::pointer accessRippleState(const uint256& uNode);
|
||||
|
||||
SLE::pointer getRippleState(LedgerStateParms& parms, const uint256& uNode);
|
||||
|
||||
SLE::pointer getRippleState(const uint256& uNode)
|
||||
{
|
||||
LedgerStateParms qry = lepNONE;
|
||||
return getRippleState(qry, uNode);
|
||||
}
|
||||
|
||||
SLE::pointer getRippleState(const RippleAddress& naA, const RippleAddress& naB, const uint160& uCurrency)
|
||||
{ return getRippleState(getRippleStateIndex(naA, naB, uCurrency)); }
|
||||
|
||||
SLE::pointer getRippleState(const uint160& uiA, const uint160& uiB, const uint160& uCurrency)
|
||||
{ return getRippleState(getRippleStateIndex(RippleAddress::createAccountID(uiA), RippleAddress::createAccountID(uiB), uCurrency)); }
|
||||
|
||||
Json::Value getJson(int options);
|
||||
void addJson(Json::Value&, int options);
|
||||
|
||||
bool walkLedger();
|
||||
bool assertSane();
|
||||
};
|
||||
|
||||
inline LedgerStateParms operator|(const LedgerStateParms& l1, const LedgerStateParms& l2)
|
||||
{
|
||||
return static_cast<LedgerStateParms>(static_cast<int>(l1) | static_cast<int>(l2));
|
||||
}
|
||||
|
||||
inline LedgerStateParms operator&(const LedgerStateParms& l1, const LedgerStateParms& l2)
|
||||
{
|
||||
return static_cast<LedgerStateParms>(static_cast<int>(l1) & static_cast<int>(l2));
|
||||
}
|
||||
|
||||
#endif
|
||||
// vim:ts=4
|
||||
615
src/cpp/ripple/LedgerAcquire.cpp
Normal file
615
src/cpp/ripple/LedgerAcquire.cpp
Normal file
@@ -0,0 +1,615 @@
|
||||
|
||||
#include "LedgerAcquire.h"
|
||||
|
||||
#include <boost/foreach.hpp>
|
||||
#include <boost/make_shared.hpp>
|
||||
#include <boost/bind.hpp>
|
||||
|
||||
#include "Application.h"
|
||||
#include "Log.h"
|
||||
#include "SHAMapSync.h"
|
||||
#include "HashPrefixes.h"
|
||||
|
||||
SETUP_LOG();
|
||||
DECLARE_INSTANCE(PeerSet);
|
||||
|
||||
#define LA_DEBUG
|
||||
#define LEDGER_ACQUIRE_TIMEOUT 750
|
||||
#define TRUST_NETWORK
|
||||
|
||||
PeerSet::PeerSet(const uint256& hash, int interval) : mHash(hash), mTimerInterval(interval), mTimeouts(0),
|
||||
mComplete(false), mFailed(false), mProgress(true), mTimer(theApp->getIOService())
|
||||
{
|
||||
assert((mTimerInterval > 10) && (mTimerInterval < 30000));
|
||||
}
|
||||
|
||||
void PeerSet::peerHas(Peer::ref ptr)
|
||||
{
|
||||
boost::recursive_mutex::scoped_lock sl(mLock);
|
||||
if (!mPeers.insert(std::make_pair(ptr->getPeerId(), 0)).second)
|
||||
return;
|
||||
newPeer(ptr);
|
||||
}
|
||||
|
||||
void PeerSet::badPeer(Peer::ref ptr)
|
||||
{
|
||||
boost::recursive_mutex::scoped_lock sl(mLock);
|
||||
mPeers.erase(ptr->getPeerId());
|
||||
}
|
||||
|
||||
void PeerSet::resetTimer()
|
||||
{
|
||||
mTimer.expires_from_now(boost::posix_time::milliseconds(mTimerInterval));
|
||||
mTimer.async_wait(boost::bind(&PeerSet::TimerEntry, pmDowncast(), boost::asio::placeholders::error));
|
||||
}
|
||||
|
||||
void PeerSet::invokeOnTimer()
|
||||
{
|
||||
if (!mProgress)
|
||||
{
|
||||
++mTimeouts;
|
||||
cLog(lsWARNING) << "Timeout(" << mTimeouts << ") pc=" << mPeers.size() << " acquiring " << mHash;
|
||||
onTimer(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
mProgress = false;
|
||||
onTimer(true);
|
||||
}
|
||||
}
|
||||
|
||||
void PeerSet::TimerEntry(boost::weak_ptr<PeerSet> wptr, const boost::system::error_code& result)
|
||||
{
|
||||
if (result == boost::asio::error::operation_aborted)
|
||||
return;
|
||||
boost::shared_ptr<PeerSet> ptr = wptr.lock();
|
||||
if (ptr)
|
||||
ptr->invokeOnTimer();
|
||||
}
|
||||
|
||||
LedgerAcquire::LedgerAcquire(const uint256& hash) : PeerSet(hash, LEDGER_ACQUIRE_TIMEOUT),
|
||||
mHaveBase(false), mHaveState(false), mHaveTransactions(false), mAborted(false), mSignaled(false), mAccept(false)
|
||||
{
|
||||
#ifdef LA_DEBUG
|
||||
cLog(lsTRACE) << "Acquiring ledger " << mHash;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool LedgerAcquire::tryLocal()
|
||||
{ // return value: true = no more work to do
|
||||
HashedObject::pointer node = theApp->getHashedObjectStore().retrieve(mHash);
|
||||
if (!node)
|
||||
return false;
|
||||
|
||||
mLedger = boost::make_shared<Ledger>(strCopy(node->getData()));
|
||||
assert(mLedger->getHash() == mHash);
|
||||
mHaveBase = true;
|
||||
|
||||
if (!mLedger->getTransHash())
|
||||
mHaveTransactions = true;
|
||||
else
|
||||
{
|
||||
try
|
||||
{
|
||||
mLedger->peekTransactionMap()->fetchRoot(mLedger->getTransHash());
|
||||
}
|
||||
catch (SHAMapMissingNode&)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
if (!mLedger->getAccountHash())
|
||||
mHaveState = true;
|
||||
else
|
||||
{
|
||||
try
|
||||
{
|
||||
mLedger->peekAccountStateMap()->fetchRoot(mLedger->getAccountHash());
|
||||
}
|
||||
catch (SHAMapMissingNode&)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
return mHaveTransactions && mHaveState;
|
||||
}
|
||||
|
||||
void LedgerAcquire::onTimer(bool progress)
|
||||
{
|
||||
if (getTimeouts() > 6)
|
||||
{
|
||||
setFailed();
|
||||
done();
|
||||
}
|
||||
else if (!progress)
|
||||
{
|
||||
if (!getPeerCount())
|
||||
addPeers();
|
||||
else
|
||||
trigger(Peer::pointer(), true);
|
||||
}
|
||||
}
|
||||
|
||||
void LedgerAcquire::addPeers()
|
||||
{
|
||||
std::vector<Peer::pointer> peerList = theApp->getConnectionPool().getPeerVector();
|
||||
|
||||
bool found = false;
|
||||
BOOST_FOREACH(Peer::ref peer, peerList)
|
||||
{
|
||||
if (peer->hasLedger(getHash()))
|
||||
{
|
||||
found = true;
|
||||
peerHas(peer);
|
||||
}
|
||||
}
|
||||
|
||||
if (!found)
|
||||
{
|
||||
BOOST_FOREACH(Peer::ref peer, peerList)
|
||||
peerHas(peer);
|
||||
}
|
||||
}
|
||||
|
||||
boost::weak_ptr<PeerSet> LedgerAcquire::pmDowncast()
|
||||
{
|
||||
return boost::shared_polymorphic_downcast<PeerSet>(shared_from_this());
|
||||
}
|
||||
|
||||
void LedgerAcquire::done()
|
||||
{
|
||||
if (mSignaled)
|
||||
return;
|
||||
mSignaled = true;
|
||||
#ifdef LA_DEBUG
|
||||
cLog(lsTRACE) << "Done acquiring ledger " << mHash;
|
||||
#endif
|
||||
std::vector< boost::function<void (LedgerAcquire::pointer)> > triggers;
|
||||
|
||||
setComplete();
|
||||
mLock.lock();
|
||||
triggers = mOnComplete;
|
||||
mOnComplete.clear();
|
||||
mLock.unlock();
|
||||
|
||||
if (mLedger)
|
||||
{
|
||||
if (mAccept)
|
||||
mLedger->setAccepted();
|
||||
theApp->getMasterLedger().storeLedger(mLedger);
|
||||
}
|
||||
|
||||
for (unsigned int i = 0; i < triggers.size(); ++i)
|
||||
triggers[i](shared_from_this());
|
||||
}
|
||||
|
||||
void LedgerAcquire::addOnComplete(boost::function<void (LedgerAcquire::pointer)> trigger)
|
||||
{
|
||||
mLock.lock();
|
||||
mOnComplete.push_back(trigger);
|
||||
mLock.unlock();
|
||||
}
|
||||
|
||||
void LedgerAcquire::trigger(Peer::ref peer, bool timer)
|
||||
{
|
||||
if (mAborted || mComplete || mFailed)
|
||||
{
|
||||
cLog(lsTRACE) << "Trigger on ledger:" <<
|
||||
(mAborted ? " aborted": "") << (mComplete ? " completed": "") << (mFailed ? " failed" : "");
|
||||
return;
|
||||
}
|
||||
|
||||
if (sLog(lsTRACE))
|
||||
{
|
||||
if (peer)
|
||||
cLog(lsTRACE) << "Trigger acquiring ledger " << mHash << " from " << peer->getIP();
|
||||
else
|
||||
cLog(lsTRACE) << "Trigger acquiring ledger " << mHash;
|
||||
if (mComplete || mFailed)
|
||||
cLog(lsTRACE) << "complete=" << mComplete << " failed=" << mFailed;
|
||||
else
|
||||
cLog(lsTRACE) << "base=" << mHaveBase << " tx=" << mHaveTransactions << " as=" << mHaveState;
|
||||
}
|
||||
|
||||
if (!mHaveBase)
|
||||
{
|
||||
ripple::TMGetLedger tmGL;
|
||||
tmGL.set_ledgerhash(mHash.begin(), mHash.size());
|
||||
tmGL.set_itype(ripple::liBASE);
|
||||
cLog(lsTRACE) << "Sending base request to " << (peer ? "selected peer" : "all peers");
|
||||
sendRequest(tmGL, peer);
|
||||
}
|
||||
|
||||
if (mHaveBase && !mHaveTransactions)
|
||||
{
|
||||
assert(mLedger);
|
||||
if (mLedger->peekTransactionMap()->getHash().isZero())
|
||||
{ // we need the root node
|
||||
ripple::TMGetLedger tmGL;
|
||||
tmGL.set_ledgerhash(mHash.begin(), mHash.size());
|
||||
tmGL.set_ledgerseq(mLedger->getLedgerSeq());
|
||||
tmGL.set_itype(ripple::liTX_NODE);
|
||||
*(tmGL.add_nodeids()) = SHAMapNode().getRawString();
|
||||
cLog(lsTRACE) << "Sending TX root request to " << (peer ? "selected peer" : "all peers");
|
||||
sendRequest(tmGL, peer);
|
||||
}
|
||||
else
|
||||
{
|
||||
std::vector<SHAMapNode> nodeIDs;
|
||||
std::vector<uint256> nodeHashes;
|
||||
TransactionStateSF tFilter(mLedger->getHash(), mLedger->getLedgerSeq());
|
||||
mLedger->peekTransactionMap()->getMissingNodes(nodeIDs, nodeHashes, 128, &tFilter);
|
||||
if (nodeIDs.empty())
|
||||
{
|
||||
if (!mLedger->peekTransactionMap()->isValid()) mFailed = true;
|
||||
else
|
||||
{
|
||||
mHaveTransactions = true;
|
||||
if (mHaveState)
|
||||
mComplete = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ripple::TMGetLedger tmGL;
|
||||
tmGL.set_ledgerhash(mHash.begin(), mHash.size());
|
||||
tmGL.set_ledgerseq(mLedger->getLedgerSeq());
|
||||
tmGL.set_itype(ripple::liTX_NODE);
|
||||
BOOST_FOREACH(SHAMapNode& it, nodeIDs)
|
||||
*(tmGL.add_nodeids()) = it.getRawString();
|
||||
cLog(lsTRACE) << "Sending TX node " << nodeIDs.size()
|
||||
<< " request to " << (peer ? "selected peer" : "all peers");
|
||||
sendRequest(tmGL, peer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (mHaveBase && !mHaveState)
|
||||
{
|
||||
assert(mLedger);
|
||||
if (mLedger->peekAccountStateMap()->getHash().isZero())
|
||||
{ // we need the root node
|
||||
ripple::TMGetLedger tmGL;
|
||||
tmGL.set_ledgerhash(mHash.begin(), mHash.size());
|
||||
tmGL.set_ledgerseq(mLedger->getLedgerSeq());
|
||||
tmGL.set_itype(ripple::liAS_NODE);
|
||||
*(tmGL.add_nodeids()) = SHAMapNode().getRawString();
|
||||
cLog(lsTRACE) << "Sending AS root request to " << (peer ? "selected peer" : "all peers");
|
||||
sendRequest(tmGL, peer);
|
||||
}
|
||||
else
|
||||
{
|
||||
std::vector<SHAMapNode> nodeIDs;
|
||||
std::vector<uint256> nodeHashes;
|
||||
AccountStateSF aFilter(mLedger->getHash(), mLedger->getLedgerSeq());
|
||||
mLedger->peekAccountStateMap()->getMissingNodes(nodeIDs, nodeHashes, 128, &aFilter);
|
||||
if (nodeIDs.empty())
|
||||
{
|
||||
if (!mLedger->peekAccountStateMap()->isValid()) mFailed = true;
|
||||
else
|
||||
{
|
||||
mHaveState = true;
|
||||
if (mHaveTransactions)
|
||||
mComplete = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ripple::TMGetLedger tmGL;
|
||||
tmGL.set_ledgerhash(mHash.begin(), mHash.size());
|
||||
tmGL.set_ledgerseq(mLedger->getLedgerSeq());
|
||||
tmGL.set_itype(ripple::liAS_NODE);
|
||||
BOOST_FOREACH(SHAMapNode& it, nodeIDs)
|
||||
*(tmGL.add_nodeids()) = it.getRawString();
|
||||
cLog(lsTRACE) << "Sending AS node " << nodeIDs.size()
|
||||
<< " request to " << (peer ? "selected peer" : "all peers");
|
||||
tLog(nodeIDs.size() == 1, lsTRACE) << "AS node: " << nodeIDs[0];
|
||||
sendRequest(tmGL, peer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (mComplete || mFailed)
|
||||
{
|
||||
cLog(lsDEBUG) << "Done:" << (mComplete ? " complete" : "") << (mFailed ? " failed" : "");
|
||||
done();
|
||||
}
|
||||
else if (timer)
|
||||
resetTimer();
|
||||
}
|
||||
|
||||
void PeerSet::sendRequest(const ripple::TMGetLedger& tmGL, Peer::ref peer)
|
||||
{
|
||||
if (!peer)
|
||||
sendRequest(tmGL);
|
||||
else
|
||||
peer->sendPacket(boost::make_shared<PackedMessage>(tmGL, ripple::mtGET_LEDGER));
|
||||
}
|
||||
|
||||
void PeerSet::sendRequest(const ripple::TMGetLedger& tmGL)
|
||||
{
|
||||
boost::recursive_mutex::scoped_lock sl(mLock);
|
||||
if (mPeers.empty())
|
||||
return;
|
||||
|
||||
PackedMessage::pointer packet = boost::make_shared<PackedMessage>(tmGL, ripple::mtGET_LEDGER);
|
||||
for (boost::unordered_map<uint64, int>::iterator it = mPeers.begin(), end = mPeers.end(); it != end; ++it)
|
||||
{
|
||||
Peer::pointer peer = theApp->getConnectionPool().getPeerById(it->first);
|
||||
if (peer)
|
||||
peer->sendPacket(packet);
|
||||
}
|
||||
}
|
||||
|
||||
int PeerSet::takePeerSetFrom(const PeerSet& s)
|
||||
{
|
||||
int ret = 0;
|
||||
mPeers.clear();
|
||||
for (boost::unordered_map<uint64, int>::const_iterator it = s.mPeers.begin(), end = s.mPeers.end();
|
||||
it != end; ++it)
|
||||
{
|
||||
mPeers.insert(std::make_pair(it->first, 0));
|
||||
++ret;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
int PeerSet::getPeerCount() const
|
||||
{
|
||||
int ret = 0;
|
||||
for (boost::unordered_map<uint64, int>::const_iterator it = mPeers.begin(), end = mPeers.end(); it != end; ++it)
|
||||
if (theApp->getConnectionPool().hasPeer(it->first))
|
||||
++ret;
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool LedgerAcquire::takeBase(const std::string& data)
|
||||
{ // Return value: true=normal, false=bad data
|
||||
#ifdef LA_DEBUG
|
||||
cLog(lsTRACE) << "got base acquiring ledger " << mHash;
|
||||
#endif
|
||||
boost::recursive_mutex::scoped_lock sl(mLock);
|
||||
if (mHaveBase) return true;
|
||||
mLedger = boost::make_shared<Ledger>(data);
|
||||
if (mLedger->getHash() != mHash)
|
||||
{
|
||||
cLog(lsWARNING) << "Acquire hash mismatch";
|
||||
cLog(lsWARNING) << mLedger->getHash() << "!=" << mHash;
|
||||
mLedger.reset();
|
||||
#ifdef TRUST_NETWORK
|
||||
assert(false);
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
mHaveBase = true;
|
||||
|
||||
Serializer s(data.size() + 4);
|
||||
s.add32(sHP_Ledger);
|
||||
s.addRaw(data);
|
||||
theApp->getHashedObjectStore().store(hotLEDGER, mLedger->getLedgerSeq(), s.peekData(), mHash);
|
||||
|
||||
progress();
|
||||
if (!mLedger->getTransHash())
|
||||
mHaveTransactions = true;
|
||||
if (!mLedger->getAccountHash())
|
||||
mHaveState = true;
|
||||
mLedger->setAcquiring();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool LedgerAcquire::takeTxNode(const std::list<SHAMapNode>& nodeIDs,
|
||||
const std::list< std::vector<unsigned char> >& data)
|
||||
{
|
||||
if (!mHaveBase) return false;
|
||||
std::list<SHAMapNode>::const_iterator nodeIDit = nodeIDs.begin();
|
||||
std::list< std::vector<unsigned char> >::const_iterator nodeDatait = data.begin();
|
||||
TransactionStateSF tFilter(mLedger->getHash(), mLedger->getLedgerSeq());
|
||||
while (nodeIDit != nodeIDs.end())
|
||||
{
|
||||
if (nodeIDit->isRoot())
|
||||
{
|
||||
if (!mLedger->peekTransactionMap()->addRootNode(mLedger->getTransHash(), *nodeDatait, snfWIRE, &tFilter))
|
||||
return false;
|
||||
}
|
||||
else if (!mLedger->peekTransactionMap()->addKnownNode(*nodeIDit, *nodeDatait, &tFilter))
|
||||
return false;
|
||||
++nodeIDit;
|
||||
++nodeDatait;
|
||||
}
|
||||
if (!mLedger->peekTransactionMap()->isSynching())
|
||||
{
|
||||
mHaveTransactions = true;
|
||||
if (mHaveState)
|
||||
{
|
||||
mComplete = true;
|
||||
done();
|
||||
}
|
||||
}
|
||||
progress();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool LedgerAcquire::takeAsNode(const std::list<SHAMapNode>& nodeIDs,
|
||||
const std::list< std::vector<unsigned char> >& data)
|
||||
{
|
||||
cLog(lsTRACE) << "got ASdata (" << nodeIDs.size() <<") acquiring ledger " << mHash;
|
||||
tLog(nodeIDs.size() == 1, lsTRACE) << "got AS node: " << nodeIDs.front();
|
||||
|
||||
if (!mHaveBase)
|
||||
{
|
||||
cLog(lsWARNING) << "Don't have ledger base";
|
||||
return false;
|
||||
}
|
||||
|
||||
std::list<SHAMapNode>::const_iterator nodeIDit = nodeIDs.begin();
|
||||
std::list< std::vector<unsigned char> >::const_iterator nodeDatait = data.begin();
|
||||
AccountStateSF tFilter(mLedger->getHash(), mLedger->getLedgerSeq());
|
||||
while (nodeIDit != nodeIDs.end())
|
||||
{
|
||||
if (nodeIDit->isRoot())
|
||||
{
|
||||
if (!mLedger->peekAccountStateMap()->addRootNode(mLedger->getAccountHash(),
|
||||
*nodeDatait, snfWIRE, &tFilter))
|
||||
{
|
||||
cLog(lsWARNING) << "Bad ledger base";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (!mLedger->peekAccountStateMap()->addKnownNode(*nodeIDit, *nodeDatait, &tFilter))
|
||||
{
|
||||
cLog(lsWARNING) << "Unable to add AS node";
|
||||
return false;
|
||||
}
|
||||
++nodeIDit;
|
||||
++nodeDatait;
|
||||
}
|
||||
if (!mLedger->peekAccountStateMap()->isSynching())
|
||||
{
|
||||
mHaveState = true;
|
||||
if (mHaveTransactions)
|
||||
{
|
||||
mComplete = true;
|
||||
done();
|
||||
}
|
||||
}
|
||||
progress();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool LedgerAcquire::takeAsRootNode(const std::vector<unsigned char>& data)
|
||||
{
|
||||
if (!mHaveBase)
|
||||
return false;
|
||||
AccountStateSF tFilter(mLedger->getHash(), mLedger->getLedgerSeq());
|
||||
if (!mLedger->peekAccountStateMap()->addRootNode(mLedger->getAccountHash(), data, snfWIRE, &tFilter))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool LedgerAcquire::takeTxRootNode(const std::vector<unsigned char>& data)
|
||||
{
|
||||
if (!mHaveBase)
|
||||
return false;
|
||||
TransactionStateSF tFilter(mLedger->getHash(), mLedger->getLedgerSeq());
|
||||
if (!mLedger->peekTransactionMap()->addRootNode(mLedger->getTransHash(), data, snfWIRE, &tFilter))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
LedgerAcquire::pointer LedgerAcquireMaster::findCreate(const uint256& hash)
|
||||
{
|
||||
assert(hash.isNonZero());
|
||||
boost::mutex::scoped_lock sl(mLock);
|
||||
LedgerAcquire::pointer& ptr = mLedgers[hash];
|
||||
if (ptr)
|
||||
return ptr;
|
||||
ptr = boost::make_shared<LedgerAcquire>(hash);
|
||||
assert(mLedgers[hash] == ptr);
|
||||
ptr->addPeers();
|
||||
ptr->resetTimer(); // Cannot call in constructor
|
||||
return ptr;
|
||||
}
|
||||
|
||||
LedgerAcquire::pointer LedgerAcquireMaster::find(const uint256& hash)
|
||||
{
|
||||
assert(hash.isNonZero());
|
||||
boost::mutex::scoped_lock sl(mLock);
|
||||
std::map<uint256, LedgerAcquire::pointer>::iterator it = mLedgers.find(hash);
|
||||
if (it != mLedgers.end())
|
||||
return it->second;
|
||||
return LedgerAcquire::pointer();
|
||||
}
|
||||
|
||||
bool LedgerAcquireMaster::hasLedger(const uint256& hash)
|
||||
{
|
||||
assert(hash.isNonZero());
|
||||
boost::mutex::scoped_lock sl(mLock);
|
||||
return mLedgers.find(hash) != mLedgers.end();
|
||||
}
|
||||
|
||||
void LedgerAcquireMaster::dropLedger(const uint256& hash)
|
||||
{
|
||||
assert(hash.isNonZero());
|
||||
boost::mutex::scoped_lock sl(mLock);
|
||||
mLedgers.erase(hash);
|
||||
}
|
||||
|
||||
bool LedgerAcquireMaster::gotLedgerData(ripple::TMLedgerData& packet, Peer::ref peer)
|
||||
{
|
||||
uint256 hash;
|
||||
if (packet.ledgerhash().size() != 32)
|
||||
{
|
||||
std::cerr << "Acquire error" << std::endl;
|
||||
return false;
|
||||
}
|
||||
memcpy(hash.begin(), packet.ledgerhash().data(), 32);
|
||||
cLog(lsTRACE) << "Got data ( " << packet.nodes().size() << ") for acquiring ledger: " << hash;
|
||||
|
||||
LedgerAcquire::pointer ledger = find(hash);
|
||||
if (!ledger)
|
||||
{
|
||||
cLog(lsINFO) << "Got data for ledger we're not acquiring";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (packet.type() == ripple::liBASE)
|
||||
{
|
||||
if (packet.nodes_size() < 1)
|
||||
{
|
||||
cLog(lsWARNING) << "Got empty base data";
|
||||
return false;
|
||||
}
|
||||
if (!ledger->takeBase(packet.nodes(0).nodedata()))
|
||||
{
|
||||
cLog(lsWARNING) << "Got invalid base data";
|
||||
return false;
|
||||
}
|
||||
if ((packet.nodes().size() > 1) && !ledger->takeAsRootNode(strCopy(packet.nodes(1).nodedata())))
|
||||
{
|
||||
cLog(lsWARNING) << "Included ASbase invalid";
|
||||
}
|
||||
if ((packet.nodes().size() > 2) && !ledger->takeTxRootNode(strCopy(packet.nodes(2).nodedata())))
|
||||
{
|
||||
cLog(lsWARNING) << "Included TXbase invalid";
|
||||
}
|
||||
ledger->trigger(peer, false);
|
||||
return true;
|
||||
}
|
||||
|
||||
if ((packet.type() == ripple::liTX_NODE) || (packet.type() == ripple::liAS_NODE))
|
||||
{
|
||||
std::list<SHAMapNode> nodeIDs;
|
||||
std::list< std::vector<unsigned char> > nodeData;
|
||||
|
||||
if (packet.nodes().size() <= 0)
|
||||
{
|
||||
cLog(lsINFO) << "Got request for no nodes";
|
||||
return false;
|
||||
}
|
||||
for (int i = 0; i < packet.nodes().size(); ++i)
|
||||
{
|
||||
const ripple::TMLedgerNode& node = packet.nodes(i);
|
||||
if (!node.has_nodeid() || !node.has_nodedata())
|
||||
{
|
||||
cLog(lsWARNING) << "Got bad node";
|
||||
return false;
|
||||
}
|
||||
|
||||
nodeIDs.push_back(SHAMapNode(node.nodeid().data(), node.nodeid().size()));
|
||||
nodeData.push_back(std::vector<unsigned char>(node.nodedata().begin(), node.nodedata().end()));
|
||||
}
|
||||
bool ret;
|
||||
if (packet.type() == ripple::liTX_NODE)
|
||||
ret = ledger->takeTxNode(nodeIDs, nodeData);
|
||||
else
|
||||
ret = ledger->takeAsNode(nodeIDs, nodeData);
|
||||
if (ret)
|
||||
ledger->trigger(peer, false);
|
||||
return ret;
|
||||
}
|
||||
|
||||
cLog(lsWARNING) << "Not sure what ledger data we got";
|
||||
return false;
|
||||
}
|
||||
|
||||
// vim:ts=4
|
||||
127
src/cpp/ripple/LedgerAcquire.h
Normal file
127
src/cpp/ripple/LedgerAcquire.h
Normal file
@@ -0,0 +1,127 @@
|
||||
#ifndef __LEDGERACQUIRE__
|
||||
#define __LEDGERACQUIRE__
|
||||
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include <list>
|
||||
|
||||
#include <boost/enable_shared_from_this.hpp>
|
||||
#include <boost/function.hpp>
|
||||
#include <boost/asio.hpp>
|
||||
#include <boost/thread/mutex.hpp>
|
||||
#include <boost/unordered_map.hpp>
|
||||
#include <boost/weak_ptr.hpp>
|
||||
|
||||
#include "Ledger.h"
|
||||
#include "Peer.h"
|
||||
#include "TaggedCache.h"
|
||||
#include "InstanceCounter.h"
|
||||
#include "ripple.pb.h"
|
||||
|
||||
DEFINE_INSTANCE(PeerSet);
|
||||
|
||||
class PeerSet : private IS_INSTANCE(PeerSet)
|
||||
{
|
||||
protected:
|
||||
uint256 mHash;
|
||||
int mTimerInterval, mTimeouts;
|
||||
bool mComplete, mFailed, mProgress;
|
||||
|
||||
boost::recursive_mutex mLock;
|
||||
boost::asio::deadline_timer mTimer;
|
||||
boost::unordered_map<uint64, int> mPeers;
|
||||
|
||||
PeerSet(const uint256& hash, int interval);
|
||||
virtual ~PeerSet() { ; }
|
||||
|
||||
void sendRequest(const ripple::TMGetLedger& message);
|
||||
void sendRequest(const ripple::TMGetLedger& message, Peer::ref peer);
|
||||
|
||||
public:
|
||||
const uint256& getHash() const { return mHash; }
|
||||
bool isComplete() const { return mComplete; }
|
||||
bool isFailed() const { return mFailed; }
|
||||
int getTimeouts() const { return mTimeouts; }
|
||||
|
||||
void progress() { mProgress = true; }
|
||||
|
||||
void peerHas(Peer::ref);
|
||||
void badPeer(Peer::ref);
|
||||
void resetTimer();
|
||||
|
||||
int takePeerSetFrom(const PeerSet& s);
|
||||
int getPeerCount() const;
|
||||
|
||||
protected:
|
||||
virtual void newPeer(Peer::ref) = 0;
|
||||
virtual void onTimer(bool progress) = 0;
|
||||
virtual boost::weak_ptr<PeerSet> pmDowncast() = 0;
|
||||
|
||||
void setComplete() { mComplete = true; }
|
||||
void setFailed() { mFailed = true; }
|
||||
void invokeOnTimer();
|
||||
|
||||
private:
|
||||
static void TimerEntry(boost::weak_ptr<PeerSet>, const boost::system::error_code& result);
|
||||
};
|
||||
|
||||
class LedgerAcquire : public PeerSet, public boost::enable_shared_from_this<LedgerAcquire>
|
||||
{ // A ledger we are trying to acquire
|
||||
public:
|
||||
typedef boost::shared_ptr<LedgerAcquire> pointer;
|
||||
|
||||
protected:
|
||||
Ledger::pointer mLedger;
|
||||
bool mHaveBase, mHaveState, mHaveTransactions, mAborted, mSignaled, mAccept;
|
||||
|
||||
std::vector< boost::function<void (LedgerAcquire::pointer)> > mOnComplete;
|
||||
|
||||
void done();
|
||||
void onTimer(bool progress);
|
||||
|
||||
void newPeer(Peer::ref peer) { trigger(peer, false); }
|
||||
|
||||
boost::weak_ptr<PeerSet> pmDowncast();
|
||||
|
||||
public:
|
||||
LedgerAcquire(const uint256& hash);
|
||||
virtual ~LedgerAcquire() { ; }
|
||||
|
||||
bool isBase() const { return mHaveBase; }
|
||||
bool isAcctStComplete() const { return mHaveState; }
|
||||
bool isTransComplete() const { return mHaveTransactions; }
|
||||
Ledger::pointer getLedger() { return mLedger; }
|
||||
void abort() { mAborted = true; }
|
||||
bool setAccept() { if (mAccept) return false; mAccept = true; return true; }
|
||||
|
||||
void addOnComplete(boost::function<void (LedgerAcquire::pointer)>);
|
||||
|
||||
bool takeBase(const std::string& data);
|
||||
bool takeTxNode(const std::list<SHAMapNode>& IDs, const std::list<std::vector<unsigned char> >& data);
|
||||
bool takeTxRootNode(const std::vector<unsigned char>& data);
|
||||
bool takeAsNode(const std::list<SHAMapNode>& IDs, const std::list<std::vector<unsigned char> >& data);
|
||||
bool takeAsRootNode(const std::vector<unsigned char>& data);
|
||||
void trigger(Peer::ref, bool timer);
|
||||
bool tryLocal();
|
||||
void addPeers();
|
||||
};
|
||||
|
||||
class LedgerAcquireMaster
|
||||
{
|
||||
protected:
|
||||
boost::mutex mLock;
|
||||
std::map<uint256, LedgerAcquire::pointer> mLedgers;
|
||||
|
||||
public:
|
||||
LedgerAcquireMaster() { ; }
|
||||
|
||||
LedgerAcquire::pointer findCreate(const uint256& hash);
|
||||
LedgerAcquire::pointer find(const uint256& hash);
|
||||
bool hasLedger(const uint256& ledgerHash);
|
||||
void dropLedger(const uint256& ledgerHash);
|
||||
bool gotLedgerData(ripple::TMLedgerData& packet, Peer::ref);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
// vim:ts=4
|
||||
1314
src/cpp/ripple/LedgerConsensus.cpp
Normal file
1314
src/cpp/ripple/LedgerConsensus.cpp
Normal file
File diff suppressed because it is too large
Load Diff
195
src/cpp/ripple/LedgerConsensus.h
Normal file
195
src/cpp/ripple/LedgerConsensus.h
Normal file
@@ -0,0 +1,195 @@
|
||||
#ifndef __LEDGER_CONSENSUS__
|
||||
#define __LEDGER_CONSENSUS__
|
||||
|
||||
#include <list>
|
||||
#include <map>
|
||||
|
||||
#include <boost/weak_ptr.hpp>
|
||||
#include <boost/enable_shared_from_this.hpp>
|
||||
#include <boost/unordered/unordered_map.hpp>
|
||||
|
||||
#include "../json/value.h"
|
||||
|
||||
#include "key.h"
|
||||
#include "Transaction.h"
|
||||
#include "LedgerAcquire.h"
|
||||
#include "LedgerProposal.h"
|
||||
#include "Peer.h"
|
||||
#include "CanonicalTXSet.h"
|
||||
#include "TransactionEngine.h"
|
||||
#include "InstanceCounter.h"
|
||||
|
||||
DEFINE_INSTANCE(LedgerConsensus);
|
||||
|
||||
class TransactionAcquire : public PeerSet, public boost::enable_shared_from_this<TransactionAcquire>
|
||||
{ // A transaction set we are trying to acquire
|
||||
public:
|
||||
typedef boost::shared_ptr<TransactionAcquire> pointer;
|
||||
|
||||
protected:
|
||||
SHAMap::pointer mMap;
|
||||
bool mHaveRoot;
|
||||
|
||||
void onTimer(bool progress);
|
||||
void newPeer(Peer::ref peer) { trigger(peer, false); }
|
||||
|
||||
void done();
|
||||
void trigger(Peer::ref, bool timer);
|
||||
boost::weak_ptr<PeerSet> pmDowncast();
|
||||
|
||||
public:
|
||||
|
||||
TransactionAcquire(const uint256& hash);
|
||||
virtual ~TransactionAcquire() { ; }
|
||||
|
||||
SHAMap::pointer getMap() { return mMap; }
|
||||
|
||||
bool takeNodes(const std::list<SHAMapNode>& IDs, const std::list< std::vector<unsigned char> >& data, Peer::ref);
|
||||
};
|
||||
|
||||
class LCTransaction
|
||||
{ // A transaction that may be disputed
|
||||
protected:
|
||||
uint256 mTransactionID;
|
||||
int mYays, mNays;
|
||||
bool mOurVote;
|
||||
Serializer transaction;
|
||||
boost::unordered_map<uint160, bool> mVotes;
|
||||
|
||||
public:
|
||||
typedef boost::shared_ptr<LCTransaction> pointer;
|
||||
|
||||
LCTransaction(const uint256 &txID, const std::vector<unsigned char>& tx, bool ourVote) :
|
||||
mTransactionID(txID), mYays(0), mNays(0), mOurVote(ourVote), transaction(tx) { ; }
|
||||
|
||||
const uint256& getTransactionID() const { return mTransactionID; }
|
||||
bool getOurVote() const { return mOurVote; }
|
||||
Serializer& peekTransaction() { return transaction; }
|
||||
void setOurVote(bool o) { mOurVote = o; }
|
||||
|
||||
void setVote(const uint160& peer, bool votesYes);
|
||||
void unVote(const uint160& peer);
|
||||
|
||||
bool updateVote(int percentTime, bool proposing);
|
||||
};
|
||||
|
||||
enum LCState
|
||||
{
|
||||
lcsPRE_CLOSE, // We haven't closed our ledger yet, but others might have
|
||||
lcsESTABLISH, // Establishing consensus
|
||||
lcsFINISHED, // We have closed on a transaction set
|
||||
lcsACCEPTED, // We have accepted/validated a new last closed ledger
|
||||
};
|
||||
|
||||
class LedgerConsensus : public boost::enable_shared_from_this<LedgerConsensus>, IS_INSTANCE(LedgerConsensus)
|
||||
{
|
||||
protected:
|
||||
LCState mState;
|
||||
uint32 mCloseTime; // The wall time this ledger closed
|
||||
uint256 mPrevLedgerHash, mNewLedgerHash;
|
||||
Ledger::pointer mPreviousLedger;
|
||||
LedgerAcquire::pointer mAcquiringLedger;
|
||||
LedgerProposal::pointer mOurPosition;
|
||||
RippleAddress mValPublic, mValPrivate;
|
||||
bool mProposing, mValidating, mHaveCorrectLCL;
|
||||
|
||||
int mCurrentMSeconds, mClosePercent, mCloseResolution;
|
||||
bool mHaveCloseTimeConsensus;
|
||||
|
||||
boost::posix_time::ptime mConsensusStartTime;
|
||||
int mPreviousProposers;
|
||||
int mPreviousMSeconds;
|
||||
|
||||
// Convergence tracking, trusted peers indexed by hash of public key
|
||||
boost::unordered_map<uint160, LedgerProposal::pointer> mPeerPositions;
|
||||
|
||||
// Transaction Sets, indexed by hash of transaction tree
|
||||
boost::unordered_map<uint256, SHAMap::pointer> mAcquired;
|
||||
boost::unordered_map<uint256, TransactionAcquire::pointer> mAcquiring;
|
||||
|
||||
// Peer sets
|
||||
boost::unordered_map<uint256, std::vector< boost::weak_ptr<Peer> > > mPeerData;
|
||||
|
||||
// Disputed transactions
|
||||
boost::unordered_map<uint256, LCTransaction::pointer> mDisputes;
|
||||
|
||||
// Close time estimates
|
||||
std::map<uint32, int> mCloseTimes;
|
||||
|
||||
// nodes that have bowed out of this consensus process
|
||||
boost::unordered_set<uint160> mDeadNodes;
|
||||
|
||||
// final accept logic
|
||||
void accept(SHAMap::ref txSet);
|
||||
|
||||
void weHave(const uint256& id, Peer::ref avoidPeer);
|
||||
void startAcquiring(const TransactionAcquire::pointer&);
|
||||
SHAMap::pointer find(const uint256& hash);
|
||||
|
||||
void createDisputes(SHAMap::ref, SHAMap::ref);
|
||||
void addDisputedTransaction(const uint256&, const std::vector<unsigned char>& transaction);
|
||||
void adjustCount(SHAMap::ref map, const std::vector<uint160>& peers);
|
||||
void propose();
|
||||
|
||||
void addPosition(LedgerProposal&, bool ours);
|
||||
void removePosition(LedgerProposal&, bool ours);
|
||||
void sendHaveTxSet(const uint256& set, bool direct);
|
||||
void applyTransactions(SHAMap::ref transactionSet, Ledger::ref targetLedger,
|
||||
Ledger::ref checkLedger, CanonicalTXSet& failedTransactions, bool openLgr);
|
||||
void applyTransaction(TransactionEngine& engine, SerializedTransaction::ref txn,
|
||||
Ledger::ref targetLedger, CanonicalTXSet& failedTransactions, bool openLgr);
|
||||
|
||||
uint32 roundCloseTime(uint32 closeTime);
|
||||
|
||||
// manipulating our own position
|
||||
void statusChange(ripple::NodeEvent, Ledger& ledger);
|
||||
void takeInitialPosition(Ledger& initialLedger);
|
||||
void updateOurPositions();
|
||||
void playbackProposals();
|
||||
int getThreshold();
|
||||
void closeLedger();
|
||||
|
||||
void beginAccept(bool synchronous);
|
||||
void endConsensus();
|
||||
|
||||
public:
|
||||
LedgerConsensus(const uint256& prevLCLHash, Ledger::ref previousLedger, uint32 closeTime);
|
||||
|
||||
int startup();
|
||||
Json::Value getJson();
|
||||
|
||||
Ledger::pointer peekPreviousLedger() { return mPreviousLedger; }
|
||||
uint256 getLCL() { return mPrevLedgerHash; }
|
||||
|
||||
SHAMap::pointer getTransactionTree(const uint256& hash, bool doAcquire);
|
||||
TransactionAcquire::pointer getAcquiring(const uint256& hash);
|
||||
void mapComplete(const uint256& hash, SHAMap::ref map, bool acquired);
|
||||
void checkLCL();
|
||||
void handleLCL(const uint256& lclHash);
|
||||
|
||||
void timerEntry();
|
||||
|
||||
// state handlers
|
||||
void statePreClose();
|
||||
void stateEstablish();
|
||||
void stateCutoff();
|
||||
void stateFinished();
|
||||
void stateAccepted();
|
||||
|
||||
bool haveConsensus(bool forReal);
|
||||
|
||||
bool peerPosition(const LedgerProposal::pointer&);
|
||||
|
||||
bool peerHasSet(Peer::ref peer, const uint256& set, ripple::TxSetStatus status);
|
||||
|
||||
bool peerGaveNodes(Peer::ref peer, const uint256& setHash,
|
||||
const std::list<SHAMapNode>& nodeIDs, const std::list< std::vector<unsigned char> >& nodeData);
|
||||
|
||||
bool isOurPubKey(const RippleAddress &k) { return k == mValPublic; }
|
||||
|
||||
// test/debug
|
||||
void simulate();
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
1133
src/cpp/ripple/LedgerEntrySet.cpp
Normal file
1133
src/cpp/ripple/LedgerEntrySet.cpp
Normal file
File diff suppressed because it is too large
Load Diff
150
src/cpp/ripple/LedgerEntrySet.h
Normal file
150
src/cpp/ripple/LedgerEntrySet.h
Normal file
@@ -0,0 +1,150 @@
|
||||
#ifndef __LEDGERENTRYSET__
|
||||
#define __LEDGERENTRYSET__
|
||||
|
||||
#include <boost/unordered_map.hpp>
|
||||
|
||||
#include "SerializedLedger.h"
|
||||
#include "TransactionMeta.h"
|
||||
#include "Ledger.h"
|
||||
#include "TransactionErr.h"
|
||||
#include "InstanceCounter.h"
|
||||
|
||||
DEFINE_INSTANCE(LedgerEntrySetEntry);
|
||||
DEFINE_INSTANCE(LedgerEntrySet);
|
||||
|
||||
enum LedgerEntryAction
|
||||
{
|
||||
taaNONE,
|
||||
taaCACHED, // Unmodified.
|
||||
taaMODIFY, // Modifed, must have previously been taaCACHED.
|
||||
taaDELETE, // Delete, must have previously been taaDELETE or taaMODIFY.
|
||||
taaCREATE, // Newly created.
|
||||
};
|
||||
|
||||
class LedgerEntrySetEntry : private IS_INSTANCE(LedgerEntrySetEntry)
|
||||
{
|
||||
public:
|
||||
SLE::pointer mEntry;
|
||||
LedgerEntryAction mAction;
|
||||
int mSeq;
|
||||
|
||||
LedgerEntrySetEntry(SLE::ref e, LedgerEntryAction a, int s) : mEntry(e), mAction(a), mSeq(s) { ; }
|
||||
};
|
||||
|
||||
|
||||
class LedgerEntrySet : private IS_INSTANCE(LedgerEntrySet)
|
||||
{
|
||||
protected:
|
||||
Ledger::pointer mLedger;
|
||||
std::map<uint256, LedgerEntrySetEntry> mEntries; // cannot be unordered!
|
||||
TransactionMetaSet mSet;
|
||||
int mSeq;
|
||||
|
||||
LedgerEntrySet(Ledger::ref ledger, const std::map<uint256, LedgerEntrySetEntry> &e,
|
||||
const TransactionMetaSet& s, int m) : mLedger(ledger), mEntries(e), mSet(s), mSeq(m) { ; }
|
||||
|
||||
SLE::pointer getForMod(const uint256& node, Ledger::ref ledger,
|
||||
boost::unordered_map<uint256, SLE::pointer>& newMods);
|
||||
|
||||
bool threadTx(const RippleAddress& threadTo, Ledger::ref ledger,
|
||||
boost::unordered_map<uint256, SLE::pointer>& newMods);
|
||||
|
||||
bool threadTx(SLE::ref threadTo, Ledger::ref ledger, boost::unordered_map<uint256, SLE::pointer>& newMods);
|
||||
|
||||
bool threadOwners(SLE::ref node, Ledger::ref ledger, boost::unordered_map<uint256, SLE::pointer>& newMods);
|
||||
|
||||
public:
|
||||
|
||||
LedgerEntrySet(Ledger::ref ledger) : mLedger(ledger), mSeq(0) { ; }
|
||||
|
||||
LedgerEntrySet() : mSeq(0) { ; }
|
||||
|
||||
// set functions
|
||||
LedgerEntrySet duplicate() const; // Make a duplicate of this set
|
||||
void setTo(const LedgerEntrySet&); // Set this set to have the same contents as another
|
||||
void swapWith(LedgerEntrySet&); // Swap the contents of two sets
|
||||
|
||||
int getSeq() const { return mSeq; }
|
||||
void bumpSeq() { ++mSeq; }
|
||||
void init(Ledger::ref ledger, const uint256& transactionID, uint32 ledgerID);
|
||||
void clear();
|
||||
|
||||
Ledger::pointer& getLedger() { return mLedger; }
|
||||
Ledger::ref getLedgerRef() const { return mLedger; }
|
||||
|
||||
// basic entry functions
|
||||
SLE::pointer getEntry(const uint256& index, LedgerEntryAction&);
|
||||
LedgerEntryAction hasEntry(const uint256& index) const;
|
||||
void entryCache(SLE::ref); // Add this entry to the cache
|
||||
void entryCreate(SLE::ref); // This entry will be created
|
||||
void entryDelete(SLE::ref); // This entry will be deleted
|
||||
void entryModify(SLE::ref); // This entry will be modified
|
||||
|
||||
// higher-level ledger functions
|
||||
SLE::pointer entryCreate(LedgerEntryType letType, const uint256& uIndex);
|
||||
SLE::pointer entryCache(LedgerEntryType letType, const uint256& uIndex);
|
||||
|
||||
// Directory functions.
|
||||
TER dirAdd(
|
||||
uint64& uNodeDir, // Node of entry.
|
||||
const uint256& uRootIndex,
|
||||
const uint256& uLedgerIndex);
|
||||
|
||||
TER dirDelete(
|
||||
const bool bKeepRoot,
|
||||
const uint64& uNodeDir, // Node item is mentioned in.
|
||||
const uint256& uRootIndex,
|
||||
const uint256& uLedgerIndex, // Item being deleted
|
||||
const bool bStable);
|
||||
|
||||
bool dirFirst(const uint256& uRootIndex, SLE::pointer& sleNode, unsigned int& uDirEntry, uint256& uEntryIndex);
|
||||
bool dirNext(const uint256& uRootIndex, SLE::pointer& sleNode, unsigned int& uDirEntry, uint256& uEntryIndex);
|
||||
|
||||
// Offer functions.
|
||||
TER offerDelete(const uint256& uOfferIndex);
|
||||
TER offerDelete(const SLE::pointer& sleOffer, const uint256& uOfferIndex, const uint160& uOwnerID);
|
||||
|
||||
// Balance functions.
|
||||
uint32 rippleTransferRate(const uint160& uIssuerID);
|
||||
uint32 rippleTransferRate(const uint160& uSenderID, const uint160& uReceiverID, const uint160& uIssuerID);
|
||||
STAmount rippleOwed(const uint160& uToAccountID, const uint160& uFromAccountID, const uint160& uCurrencyID);
|
||||
STAmount rippleLimit(const uint160& uToAccountID, const uint160& uFromAccountID, const uint160& uCurrencyID);
|
||||
uint32 rippleQualityIn(const uint160& uToAccountID, const uint160& uFromAccountID, const uint160& uCurrencyID,
|
||||
SField::ref sfLow = sfLowQualityIn, SField::ref sfHigh = sfHighQualityIn);
|
||||
uint32 rippleQualityOut(const uint160& uToAccountID, const uint160& uFromAccountID, const uint160& uCurrencyID)
|
||||
{ return rippleQualityIn(uToAccountID, uFromAccountID, uCurrencyID, sfLowQualityOut, sfHighQualityOut); }
|
||||
|
||||
STAmount rippleHolds(const uint160& uAccountID, const uint160& uCurrencyID, const uint160& uIssuerID);
|
||||
STAmount rippleTransferFee(const uint160& uSenderID, const uint160& uReceiverID, const uint160& uIssuerID, const STAmount& saAmount);
|
||||
void rippleCredit(const uint160& uSenderID, const uint160& uReceiverID, const STAmount& saAmount, bool bCheckIssuer=true);
|
||||
STAmount rippleSend(const uint160& uSenderID, const uint160& uReceiverID, const STAmount& saAmount);
|
||||
|
||||
STAmount accountHolds(const uint160& uAccountID, const uint160& uCurrencyID, const uint160& uIssuerID);
|
||||
void accountSend(const uint160& uSenderID, const uint160& uReceiverID, const STAmount& saAmount);
|
||||
STAmount accountFunds(const uint160& uAccountID, const STAmount& saDefault);
|
||||
|
||||
Json::Value getJson(int) const;
|
||||
void calcRawMeta(Serializer&, TER result);
|
||||
|
||||
// iterator functions
|
||||
typedef std::map<uint256, LedgerEntrySetEntry>::iterator iterator;
|
||||
typedef std::map<uint256, LedgerEntrySetEntry>::const_iterator const_iterator;
|
||||
bool isEmpty() const { return mEntries.empty(); }
|
||||
std::map<uint256, LedgerEntrySetEntry>::const_iterator begin() const { return mEntries.begin(); }
|
||||
std::map<uint256, LedgerEntrySetEntry>::const_iterator end() const { return mEntries.end(); }
|
||||
std::map<uint256, LedgerEntrySetEntry>::iterator begin() { return mEntries.begin(); }
|
||||
std::map<uint256, LedgerEntrySetEntry>::iterator end() { return mEntries.end(); }
|
||||
|
||||
static bool intersect(const LedgerEntrySet& lesLeft, const LedgerEntrySet& lesRight);
|
||||
};
|
||||
|
||||
inline LedgerEntrySet::iterator range_begin(LedgerEntrySet& x) { return x.begin(); }
|
||||
inline LedgerEntrySet::iterator range_end(LedgerEntrySet &x) { return x.end(); }
|
||||
namespace boost
|
||||
{
|
||||
template<> struct range_mutable_iterator<LedgerEntrySet> { typedef LedgerEntrySet::iterator type; };
|
||||
template<> struct range_const_iterator<LedgerEntrySet> { typedef LedgerEntrySet::const_iterator type; };
|
||||
}
|
||||
|
||||
#endif
|
||||
// vim:ts=4
|
||||
121
src/cpp/ripple/LedgerFormats.cpp
Normal file
121
src/cpp/ripple/LedgerFormats.cpp
Normal file
@@ -0,0 +1,121 @@
|
||||
|
||||
#include "LedgerFormats.h"
|
||||
|
||||
std::map<int, LedgerEntryFormat*> LedgerEntryFormat::byType;
|
||||
std::map<std::string, LedgerEntryFormat*> LedgerEntryFormat::byName;
|
||||
|
||||
#define LEF_BASE \
|
||||
<< SOElement(sfLedgerIndex, SOE_OPTIONAL) \
|
||||
<< SOElement(sfLedgerEntryType, SOE_REQUIRED) \
|
||||
<< SOElement(sfFlags, SOE_REQUIRED)
|
||||
|
||||
#define DECLARE_LEF(name, type) lef = new LedgerEntryFormat(#name, type); (*lef) LEF_BASE
|
||||
|
||||
static bool LEFInit()
|
||||
{
|
||||
LedgerEntryFormat* lef;
|
||||
|
||||
DECLARE_LEF(AccountRoot, ltACCOUNT_ROOT)
|
||||
<< SOElement(sfAccount, SOE_REQUIRED)
|
||||
<< SOElement(sfSequence, SOE_REQUIRED)
|
||||
<< SOElement(sfBalance, SOE_REQUIRED)
|
||||
<< SOElement(sfPreviousTxnID, SOE_REQUIRED)
|
||||
<< SOElement(sfPreviousTxnLgrSeq, SOE_REQUIRED)
|
||||
<< SOElement(sfAuthorizedKey, SOE_OPTIONAL)
|
||||
<< SOElement(sfEmailHash, SOE_OPTIONAL)
|
||||
<< SOElement(sfWalletLocator, SOE_OPTIONAL)
|
||||
<< SOElement(sfWalletSize, SOE_OPTIONAL)
|
||||
<< SOElement(sfMessageKey, SOE_OPTIONAL)
|
||||
<< SOElement(sfTransferRate, SOE_OPTIONAL)
|
||||
<< SOElement(sfDomain, SOE_OPTIONAL)
|
||||
;
|
||||
|
||||
DECLARE_LEF(Contract, ltCONTRACT)
|
||||
<< SOElement(sfAccount, SOE_REQUIRED)
|
||||
<< SOElement(sfBalance, SOE_REQUIRED)
|
||||
<< SOElement(sfPreviousTxnID, SOE_REQUIRED)
|
||||
<< SOElement(sfPreviousTxnLgrSeq, SOE_REQUIRED)
|
||||
<< SOElement(sfIssuer, SOE_REQUIRED)
|
||||
<< SOElement(sfOwner, SOE_REQUIRED)
|
||||
<< SOElement(sfExpiration, SOE_REQUIRED)
|
||||
<< SOElement(sfBondAmount, SOE_REQUIRED)
|
||||
<< SOElement(sfCreateCode, SOE_REQUIRED)
|
||||
<< SOElement(sfFundCode, SOE_REQUIRED)
|
||||
<< SOElement(sfRemoveCode, SOE_REQUIRED)
|
||||
<< SOElement(sfExpireCode, SOE_REQUIRED)
|
||||
;
|
||||
|
||||
DECLARE_LEF(DirectoryNode, ltDIR_NODE)
|
||||
<< SOElement(sfIndexes, SOE_REQUIRED)
|
||||
<< SOElement(sfIndexNext, SOE_OPTIONAL)
|
||||
<< SOElement(sfIndexPrevious, SOE_OPTIONAL)
|
||||
;
|
||||
|
||||
DECLARE_LEF(GeneratorMap, ltGENERATOR_MAP)
|
||||
<< SOElement(sfGenerator, SOE_REQUIRED)
|
||||
;
|
||||
|
||||
DECLARE_LEF(Nickname, ltNICKNAME)
|
||||
<< SOElement(sfAccount, SOE_REQUIRED)
|
||||
<< SOElement(sfMinimumOffer, SOE_OPTIONAL)
|
||||
;
|
||||
|
||||
DECLARE_LEF(Offer, ltOFFER)
|
||||
<< SOElement(sfAccount, SOE_REQUIRED)
|
||||
<< SOElement(sfSequence, SOE_REQUIRED)
|
||||
<< SOElement(sfTakerPays, SOE_REQUIRED)
|
||||
<< SOElement(sfTakerGets, SOE_REQUIRED)
|
||||
<< SOElement(sfBookDirectory, SOE_REQUIRED)
|
||||
<< SOElement(sfBookNode, SOE_REQUIRED)
|
||||
<< SOElement(sfOwnerNode, SOE_REQUIRED)
|
||||
<< SOElement(sfPreviousTxnID, SOE_REQUIRED)
|
||||
<< SOElement(sfPreviousTxnLgrSeq, SOE_REQUIRED)
|
||||
<< SOElement(sfExpiration, SOE_OPTIONAL)
|
||||
;
|
||||
|
||||
DECLARE_LEF(RippleState, ltRIPPLE_STATE)
|
||||
<< SOElement(sfBalance, SOE_REQUIRED)
|
||||
<< SOElement(sfLowLimit, SOE_REQUIRED)
|
||||
<< SOElement(sfHighLimit, SOE_REQUIRED)
|
||||
<< SOElement(sfPreviousTxnID, SOE_REQUIRED)
|
||||
<< SOElement(sfPreviousTxnLgrSeq, SOE_REQUIRED)
|
||||
<< SOElement(sfLowQualityIn, SOE_OPTIONAL)
|
||||
<< SOElement(sfLowQualityOut, SOE_OPTIONAL)
|
||||
<< SOElement(sfHighQualityIn, SOE_OPTIONAL)
|
||||
<< SOElement(sfHighQualityOut, SOE_OPTIONAL)
|
||||
;
|
||||
|
||||
DECLARE_LEF(LedgerHashes, ltLEDGER_HASHES)
|
||||
<< SOElement(sfHashes, SOE_REQUIRED)
|
||||
;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool LEFInitComplete = LEFInit();
|
||||
|
||||
LedgerEntryFormat* LedgerEntryFormat::getLgrFormat(LedgerEntryType t)
|
||||
{
|
||||
std::map<int, LedgerEntryFormat*>::iterator it = byType.find(static_cast<int>(t));
|
||||
if (it == byType.end())
|
||||
return NULL;
|
||||
return it->second;
|
||||
}
|
||||
|
||||
LedgerEntryFormat* LedgerEntryFormat::getLgrFormat(int t)
|
||||
{
|
||||
std::map<int, LedgerEntryFormat*>::iterator it = byType.find((t));
|
||||
if (it == byType.end())
|
||||
return NULL;
|
||||
return it->second;
|
||||
}
|
||||
|
||||
LedgerEntryFormat* LedgerEntryFormat::getLgrFormat(const std::string& t)
|
||||
{
|
||||
std::map<std::string, LedgerEntryFormat*>::iterator it = byName.find((t));
|
||||
if (it == byName.end())
|
||||
return NULL;
|
||||
return it->second;
|
||||
}
|
||||
|
||||
// vim:ts=4
|
||||
71
src/cpp/ripple/LedgerFormats.h
Normal file
71
src/cpp/ripple/LedgerFormats.h
Normal file
@@ -0,0 +1,71 @@
|
||||
#ifndef __LEDGERFORMATS__
|
||||
#define __LEDGERFORMATS__
|
||||
|
||||
#include "SerializedObject.h"
|
||||
|
||||
// Used as the type of a transaction or the type of a ledger entry.
|
||||
enum LedgerEntryType
|
||||
{
|
||||
ltINVALID = -1,
|
||||
ltACCOUNT_ROOT = 'a',
|
||||
ltDIR_NODE = 'd',
|
||||
ltGENERATOR_MAP = 'g',
|
||||
ltRIPPLE_STATE = 'r',
|
||||
ltNICKNAME = 'n',
|
||||
ltOFFER = 'o',
|
||||
ltCONTRACT = 'c',
|
||||
ltLEDGER_HASHES = 'h',
|
||||
};
|
||||
|
||||
// Used as a prefix for computing ledger indexes (keys).
|
||||
enum LedgerNameSpace
|
||||
{
|
||||
spaceAccount = 'a',
|
||||
spaceDirNode = 'd',
|
||||
spaceGenerator = 'g',
|
||||
spaceNickname = 'n',
|
||||
spaceRipple = 'r',
|
||||
spaceOffer = 'o', // Entry for an offer.
|
||||
spaceOwnerDir = 'O', // Directory of things owned by an account.
|
||||
spaceBookDir = 'B', // Directory of order books.
|
||||
spaceContract = 'c',
|
||||
spaceHashes = 'h',
|
||||
};
|
||||
|
||||
enum LedgerSpecificFlags
|
||||
{
|
||||
// ltACCOUNT_ROOT
|
||||
lsfPasswordSpent = 0x00010000, // True if password set fee is spent.
|
||||
|
||||
// ltOFFER
|
||||
lsfPassive = 0x00010000,
|
||||
};
|
||||
|
||||
class LedgerEntryFormat
|
||||
{
|
||||
public:
|
||||
std::string t_name;
|
||||
LedgerEntryType t_type;
|
||||
std::vector<SOElement::ptr> elements;
|
||||
|
||||
static std::map<int, LedgerEntryFormat*> byType;
|
||||
static std::map<std::string, LedgerEntryFormat*> byName;
|
||||
|
||||
LedgerEntryFormat(const char *name, LedgerEntryType type) : t_name(name), t_type(type)
|
||||
{
|
||||
byName[name] = this;
|
||||
byType[type] = this;
|
||||
}
|
||||
LedgerEntryFormat& operator<<(const SOElement& el)
|
||||
{
|
||||
elements.push_back(new SOElement(el));
|
||||
return *this;
|
||||
}
|
||||
|
||||
static LedgerEntryFormat* getLgrFormat(LedgerEntryType t);
|
||||
static LedgerEntryFormat* getLgrFormat(const std::string& t);
|
||||
static LedgerEntryFormat* getLgrFormat(int t);
|
||||
};
|
||||
|
||||
#endif
|
||||
// vim:ts=4
|
||||
102
src/cpp/ripple/LedgerHistory.cpp
Normal file
102
src/cpp/ripple/LedgerHistory.cpp
Normal file
@@ -0,0 +1,102 @@
|
||||
|
||||
#include "LedgerHistory.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <boost/bind.hpp>
|
||||
#include <boost/thread.hpp>
|
||||
|
||||
#include "Config.h"
|
||||
#include "Application.h"
|
||||
|
||||
#ifndef CACHED_LEDGER_NUM
|
||||
#define CACHED_LEDGER_NUM 128
|
||||
#endif
|
||||
|
||||
#ifndef CACHED_LEDGER_AGE
|
||||
#define CACHED_LEDGER_AGE 900
|
||||
#endif
|
||||
|
||||
// FIXME: Need to clean up ledgers by index at some point
|
||||
|
||||
LedgerHistory::LedgerHistory() : mLedgersByHash("LedgerCache", CACHED_LEDGER_NUM, CACHED_LEDGER_AGE)
|
||||
{ ; }
|
||||
|
||||
void LedgerHistory::addLedger(Ledger::pointer ledger)
|
||||
{
|
||||
mLedgersByHash.canonicalize(ledger->getHash(), ledger, true);
|
||||
}
|
||||
|
||||
void LedgerHistory::addAcceptedLedger(Ledger::pointer ledger, bool fromConsensus)
|
||||
{
|
||||
assert(ledger && ledger->isAccepted());
|
||||
uint256 h(ledger->getHash());
|
||||
boost::recursive_mutex::scoped_lock sl(mLedgersByHash.peekMutex());
|
||||
mLedgersByHash.canonicalize(h, ledger, true);
|
||||
assert(ledger);
|
||||
assert(ledger->isAccepted());
|
||||
assert(ledger->isImmutable());
|
||||
mLedgersByIndex[ledger->getLedgerSeq()] = ledger->getHash();
|
||||
|
||||
ledger->pendSave(fromConsensus);
|
||||
}
|
||||
|
||||
Ledger::pointer LedgerHistory::getLedgerBySeq(uint32 index)
|
||||
{
|
||||
boost::recursive_mutex::scoped_lock sl(mLedgersByHash.peekMutex());
|
||||
std::map<uint32, uint256>::iterator it(mLedgersByIndex.find(index));
|
||||
if (it != mLedgersByIndex.end())
|
||||
{
|
||||
uint256 hash = it->second;
|
||||
sl.unlock();
|
||||
return getLedgerByHash(hash);
|
||||
}
|
||||
sl.unlock();
|
||||
|
||||
Ledger::pointer ret(Ledger::loadByIndex(index));
|
||||
if (!ret)
|
||||
return ret;
|
||||
assert(ret->getLedgerSeq() == index);
|
||||
|
||||
sl.lock();
|
||||
mLedgersByHash.canonicalize(ret->getHash(), ret);
|
||||
mLedgersByIndex[ret->getLedgerSeq()] = ret->getHash();
|
||||
return (ret->getLedgerSeq() == index) ? ret : Ledger::pointer();
|
||||
}
|
||||
|
||||
Ledger::pointer LedgerHistory::getLedgerByHash(const uint256& hash)
|
||||
{
|
||||
Ledger::pointer ret = mLedgersByHash.fetch(hash);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = Ledger::loadByHash(hash);
|
||||
if (!ret)
|
||||
return ret;
|
||||
assert(ret->getHash() == hash);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
Ledger::pointer LedgerHistory::canonicalizeLedger(Ledger::pointer ledger, bool save)
|
||||
{
|
||||
assert(ledger->isImmutable());
|
||||
uint256 h(ledger->getHash());
|
||||
|
||||
if (!save)
|
||||
{ // return input ledger if not in map, otherwise, return corresponding map ledger
|
||||
Ledger::pointer ret = mLedgersByHash.fetch(h);
|
||||
if (ret)
|
||||
return ret;
|
||||
return ledger;
|
||||
}
|
||||
|
||||
// save input ledger in map if not in map, otherwise return corresponding map ledger
|
||||
boost::recursive_mutex::scoped_lock sl(mLedgersByHash.peekMutex());
|
||||
mLedgersByHash.canonicalize(h, ledger);
|
||||
if (ledger->isAccepted())
|
||||
mLedgersByIndex[ledger->getLedgerSeq()] = ledger->getHash();
|
||||
return ledger;
|
||||
}
|
||||
|
||||
// vim:ts=4
|
||||
24
src/cpp/ripple/LedgerHistory.h
Normal file
24
src/cpp/ripple/LedgerHistory.h
Normal file
@@ -0,0 +1,24 @@
|
||||
#ifndef __LEDGERHISTORY__
|
||||
#define __LEDGERHISTORY__
|
||||
|
||||
#include "TaggedCache.h"
|
||||
#include "Ledger.h"
|
||||
|
||||
class LedgerHistory
|
||||
{
|
||||
TaggedCache<uint256, Ledger> mLedgersByHash;
|
||||
std::map<uint32, uint256> mLedgersByIndex; // accepted ledgers
|
||||
|
||||
public:
|
||||
LedgerHistory();
|
||||
|
||||
void addLedger(Ledger::pointer ledger);
|
||||
void addAcceptedLedger(Ledger::pointer ledger, bool fromConsensus);
|
||||
|
||||
Ledger::pointer getLedgerBySeq(uint32 index);
|
||||
Ledger::pointer getLedgerByHash(const uint256& hash);
|
||||
Ledger::pointer canonicalizeLedger(Ledger::pointer, bool cache);
|
||||
void sweep() { mLedgersByHash.sweep(); }
|
||||
};
|
||||
|
||||
#endif
|
||||
208
src/cpp/ripple/LedgerMaster.cpp
Normal file
208
src/cpp/ripple/LedgerMaster.cpp
Normal file
@@ -0,0 +1,208 @@
|
||||
|
||||
#include "LedgerMaster.h"
|
||||
|
||||
#include <boost/foreach.hpp>
|
||||
|
||||
#include "Application.h"
|
||||
#include "RippleAddress.h"
|
||||
#include "Log.h"
|
||||
|
||||
SETUP_LOG();
|
||||
|
||||
uint32 LedgerMaster::getCurrentLedgerIndex()
|
||||
{
|
||||
return mCurrentLedger->getLedgerSeq();
|
||||
}
|
||||
|
||||
void LedgerMaster::addHeldTransaction(const Transaction::pointer& transaction)
|
||||
{ // returns true if transaction was added
|
||||
boost::recursive_mutex::scoped_lock ml(mLock);
|
||||
mHeldTransactions.push_back(transaction->getSTransaction());
|
||||
}
|
||||
|
||||
void LedgerMaster::pushLedger(Ledger::ref newLedger)
|
||||
{
|
||||
// Caller should already have properly assembled this ledger into "ready-to-close" form --
|
||||
// all candidate transactions must already be applied
|
||||
cLog(lsINFO) << "PushLedger: " << newLedger->getHash();
|
||||
boost::recursive_mutex::scoped_lock ml(mLock);
|
||||
if (!!mFinalizedLedger)
|
||||
{
|
||||
mFinalizedLedger->setClosed();
|
||||
cLog(lsTRACE) << "Finalizes: " << mFinalizedLedger->getHash();
|
||||
}
|
||||
mFinalizedLedger = mCurrentLedger;
|
||||
mCurrentLedger = newLedger;
|
||||
mEngine.setLedger(newLedger);
|
||||
}
|
||||
|
||||
void LedgerMaster::pushLedger(Ledger::ref newLCL, Ledger::ref newOL, bool fromConsensus)
|
||||
{
|
||||
assert(newLCL->isClosed() && newLCL->isAccepted());
|
||||
assert(!newOL->isClosed() && !newOL->isAccepted());
|
||||
|
||||
if (newLCL->isAccepted())
|
||||
{
|
||||
assert(newLCL->isClosed());
|
||||
assert(newLCL->isImmutable());
|
||||
mLedgerHistory.addAcceptedLedger(newLCL, fromConsensus);
|
||||
cLog(lsINFO) << "StashAccepted: " << newLCL->getHash();
|
||||
}
|
||||
|
||||
boost::recursive_mutex::scoped_lock ml(mLock);
|
||||
mFinalizedLedger = newLCL;
|
||||
mCurrentLedger = newOL;
|
||||
mEngine.setLedger(newOL);
|
||||
}
|
||||
|
||||
void LedgerMaster::switchLedgers(Ledger::ref lastClosed, Ledger::ref current)
|
||||
{
|
||||
assert(lastClosed && current);
|
||||
|
||||
{
|
||||
boost::recursive_mutex::scoped_lock ml(mLock);
|
||||
mFinalizedLedger = lastClosed;
|
||||
mFinalizedLedger->setClosed();
|
||||
mFinalizedLedger->setAccepted();
|
||||
mCurrentLedger = current;
|
||||
}
|
||||
|
||||
assert(!mCurrentLedger->isClosed());
|
||||
mEngine.setLedger(mCurrentLedger);
|
||||
}
|
||||
|
||||
void LedgerMaster::storeLedger(Ledger::ref ledger)
|
||||
{
|
||||
mLedgerHistory.addLedger(ledger);
|
||||
if (ledger->isAccepted())
|
||||
mLedgerHistory.addAcceptedLedger(ledger, false);
|
||||
}
|
||||
|
||||
|
||||
Ledger::pointer LedgerMaster::closeLedger(bool recover)
|
||||
{
|
||||
boost::recursive_mutex::scoped_lock sl(mLock);
|
||||
Ledger::pointer closingLedger = mCurrentLedger;
|
||||
|
||||
if (recover)
|
||||
{
|
||||
int recovers = 0;
|
||||
for (CanonicalTXSet::iterator it = mHeldTransactions.begin(), end = mHeldTransactions.end(); it != end; ++it)
|
||||
{
|
||||
try
|
||||
{
|
||||
TER result = mEngine.applyTransaction(*it->second, tapOPEN_LEDGER);
|
||||
if (isTepSuccess(result))
|
||||
++recovers;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
cLog(lsWARNING) << "Held transaction throws";
|
||||
}
|
||||
}
|
||||
tLog(recovers != 0, lsINFO) << "Recovered " << recovers << " held transactions";
|
||||
mHeldTransactions.reset(closingLedger->getHash());
|
||||
}
|
||||
|
||||
mCurrentLedger = boost::make_shared<Ledger>(boost::ref(*closingLedger), true);
|
||||
mEngine.setLedger(mCurrentLedger);
|
||||
|
||||
return closingLedger;
|
||||
}
|
||||
|
||||
TER LedgerMaster::doTransaction(const SerializedTransaction& txn, TransactionEngineParams params)
|
||||
{
|
||||
TER result = mEngine.applyTransaction(txn, params);
|
||||
theApp->getOPs().pubProposedTransaction(mEngine.getLedger(), txn, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
void LedgerMaster::acquireMissingLedger(const uint256& ledgerHash, uint32 ledgerSeq)
|
||||
{
|
||||
mMissingLedger = theApp->getMasterLedgerAcquire().findCreate(ledgerHash);
|
||||
if (mMissingLedger->isComplete())
|
||||
{
|
||||
Ledger::pointer lgr = mMissingLedger->getLedger();
|
||||
if (lgr && (lgr->getLedgerSeq() == ledgerSeq))
|
||||
missingAcquireComplete(mMissingLedger);
|
||||
mMissingLedger.reset();
|
||||
return;
|
||||
}
|
||||
mMissingSeq = ledgerSeq;
|
||||
if (mMissingLedger->setAccept())
|
||||
mMissingLedger->addOnComplete(boost::bind(&LedgerMaster::missingAcquireComplete, this, _1));
|
||||
}
|
||||
|
||||
void LedgerMaster::missingAcquireComplete(LedgerAcquire::pointer acq)
|
||||
{
|
||||
boost::recursive_mutex::scoped_lock ml(mLock);
|
||||
|
||||
if (acq->isFailed() && (mMissingSeq != 0))
|
||||
{
|
||||
cLog(lsWARNING) << "Acquire failed for " << mMissingSeq;
|
||||
}
|
||||
|
||||
mMissingLedger.reset();
|
||||
mMissingSeq = 0;
|
||||
|
||||
if (!acq->isFailed())
|
||||
{
|
||||
setFullLedger(acq->getLedger());
|
||||
acq->getLedger()->pendSave(false);
|
||||
}
|
||||
}
|
||||
|
||||
void LedgerMaster::setFullLedger(Ledger::ref ledger)
|
||||
{
|
||||
boost::recursive_mutex::scoped_lock ml(mLock);
|
||||
|
||||
mCompleteLedgers.setValue(ledger->getLedgerSeq());
|
||||
|
||||
if ((ledger->getLedgerSeq() != 0) && mCompleteLedgers.hasValue(ledger->getLedgerSeq() - 1))
|
||||
{ // we think we have the previous ledger, double check
|
||||
Ledger::pointer prevLedger = getLedgerBySeq(ledger->getLedgerSeq() - 1);
|
||||
if (prevLedger && (prevLedger->getHash() != ledger->getParentHash()))
|
||||
{
|
||||
cLog(lsWARNING) << "Ledger " << ledger->getLedgerSeq() << " invalidates prior ledger";
|
||||
mCompleteLedgers.clearValue(prevLedger->getLedgerSeq());
|
||||
}
|
||||
}
|
||||
|
||||
if (mMissingLedger && mMissingLedger->isComplete())
|
||||
mMissingLedger.reset();
|
||||
|
||||
if (mMissingLedger || !theConfig.FULL_HISTORY)
|
||||
return;
|
||||
|
||||
if (Ledger::getPendingSaves() > 3)
|
||||
{
|
||||
cLog(lsINFO) << "Too many pending ledger saves";
|
||||
return;
|
||||
}
|
||||
|
||||
// see if there's a ledger gap we need to fill
|
||||
if (!mCompleteLedgers.hasValue(ledger->getLedgerSeq() - 1))
|
||||
{
|
||||
cLog(lsINFO) << "We need the ledger before the ledger we just accepted";
|
||||
acquireMissingLedger(ledger->getParentHash(), ledger->getLedgerSeq() - 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
uint32 prevMissing = mCompleteLedgers.prevMissing(ledger->getLedgerSeq());
|
||||
if (prevMissing != RangeSet::RangeSetAbsent)
|
||||
{
|
||||
cLog(lsINFO) << "Ledger " << prevMissing << " is missing";
|
||||
assert(!mCompleteLedgers.hasValue(prevMissing));
|
||||
Ledger::pointer nextLedger = getLedgerBySeq(prevMissing + 1);
|
||||
if (nextLedger)
|
||||
acquireMissingLedger(nextLedger->getParentHash(), nextLedger->getLedgerSeq() - 1);
|
||||
else
|
||||
{
|
||||
mCompleteLedgers.clearValue(prevMissing);
|
||||
cLog(lsWARNING) << "We have a gap we can't fix: " << prevMissing + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// vim:ts=4
|
||||
100
src/cpp/ripple/LedgerMaster.h
Normal file
100
src/cpp/ripple/LedgerMaster.h
Normal file
@@ -0,0 +1,100 @@
|
||||
#ifndef __LEDGERMASTER__
|
||||
#define __LEDGERMASTER__
|
||||
|
||||
#include "Ledger.h"
|
||||
#include "LedgerHistory.h"
|
||||
#include "Peer.h"
|
||||
#include "types.h"
|
||||
#include "LedgerAcquire.h"
|
||||
#include "Transaction.h"
|
||||
#include "TransactionEngine.h"
|
||||
#include "RangeSet.h"
|
||||
#include "CanonicalTXSet.h"
|
||||
|
||||
// Tracks the current ledger and any ledgers in the process of closing
|
||||
// Tracks ledger history
|
||||
// Tracks held transactions
|
||||
|
||||
class LedgerMaster
|
||||
{
|
||||
boost::recursive_mutex mLock;
|
||||
|
||||
TransactionEngine mEngine;
|
||||
|
||||
Ledger::pointer mCurrentLedger; // The ledger we are currently processiong
|
||||
Ledger::pointer mFinalizedLedger; // The ledger that most recently closed
|
||||
|
||||
LedgerHistory mLedgerHistory;
|
||||
|
||||
CanonicalTXSet mHeldTransactions;
|
||||
|
||||
RangeSet mCompleteLedgers;
|
||||
LedgerAcquire::pointer mMissingLedger;
|
||||
uint32 mMissingSeq;
|
||||
|
||||
void applyFutureTransactions(uint32 ledgerIndex);
|
||||
bool isValidTransaction(const Transaction::pointer& trans);
|
||||
bool isTransactionOnFutureList(const Transaction::pointer& trans);
|
||||
|
||||
void acquireMissingLedger(const uint256& ledgerHash, uint32 ledgerSeq);
|
||||
void missingAcquireComplete(LedgerAcquire::pointer);
|
||||
|
||||
public:
|
||||
|
||||
LedgerMaster() : mHeldTransactions(uint256()), mMissingSeq(0) { ; }
|
||||
|
||||
uint32 getCurrentLedgerIndex();
|
||||
|
||||
ScopedLock getLock() { return ScopedLock(mLock); }
|
||||
|
||||
// The current ledger is the ledger we believe new transactions should go in
|
||||
Ledger::pointer getCurrentLedger() { return mCurrentLedger; }
|
||||
|
||||
// The finalized ledger is the last closed/accepted ledger
|
||||
Ledger::pointer getClosedLedger() { return mFinalizedLedger; }
|
||||
|
||||
TER doTransaction(const SerializedTransaction& txn, TransactionEngineParams params);
|
||||
|
||||
void pushLedger(Ledger::ref newLedger);
|
||||
void pushLedger(Ledger::ref newLCL, Ledger::ref newOL, bool fromConsensus);
|
||||
void storeLedger(Ledger::ref);
|
||||
|
||||
void setFullLedger(Ledger::ref ledger);
|
||||
|
||||
void switchLedgers(Ledger::ref lastClosed, Ledger::ref newCurrent);
|
||||
|
||||
std::string getCompleteLedgers() { return mCompleteLedgers.toString(); }
|
||||
|
||||
Ledger::pointer closeLedger(bool recoverHeldTransactions);
|
||||
|
||||
Ledger::pointer getLedgerBySeq(uint32 index)
|
||||
{
|
||||
if (mCurrentLedger && (mCurrentLedger->getLedgerSeq() == index))
|
||||
return mCurrentLedger;
|
||||
if (mFinalizedLedger && (mFinalizedLedger->getLedgerSeq() == index))
|
||||
return mFinalizedLedger;
|
||||
return mLedgerHistory.getLedgerBySeq(index);
|
||||
}
|
||||
|
||||
Ledger::pointer getLedgerByHash(const uint256& hash)
|
||||
{
|
||||
if (hash.isZero())
|
||||
return mCurrentLedger;
|
||||
|
||||
if (mCurrentLedger && (mCurrentLedger->getHash() == hash))
|
||||
return mCurrentLedger;
|
||||
if (mFinalizedLedger && (mFinalizedLedger->getHash() == hash))
|
||||
return mFinalizedLedger;
|
||||
|
||||
return mLedgerHistory.getLedgerByHash(hash);
|
||||
}
|
||||
|
||||
void setLedgerRangePresent(uint32 minV, uint32 maxV) { mCompleteLedgers.setRange(minV, maxV); }
|
||||
|
||||
void addHeldTransaction(const Transaction::pointer& trans);
|
||||
|
||||
void sweep(void) { mLedgerHistory.sweep(); }
|
||||
};
|
||||
|
||||
#endif
|
||||
// vim:ts=4
|
||||
108
src/cpp/ripple/LedgerProposal.cpp
Normal file
108
src/cpp/ripple/LedgerProposal.cpp
Normal file
@@ -0,0 +1,108 @@
|
||||
|
||||
#include "LedgerProposal.h"
|
||||
|
||||
#include <boost/make_shared.hpp>
|
||||
|
||||
#include "key.h"
|
||||
#include "Application.h"
|
||||
#include "HashPrefixes.h"
|
||||
|
||||
DECLARE_INSTANCE(LedgerProposal);
|
||||
|
||||
LedgerProposal::LedgerProposal(const uint256& pLgr, uint32 seq, const uint256& tx, uint32 closeTime,
|
||||
const RippleAddress& naPeerPublic, const uint256& suppression) :
|
||||
mPreviousLedger(pLgr), mCurrentHash(tx), mSuppression(suppression), mCloseTime(closeTime),
|
||||
mProposeSeq(seq), mPublicKey(naPeerPublic)
|
||||
{
|
||||
// XXX Validate key.
|
||||
// if (!mKey->SetPubKey(pubKey))
|
||||
// throw std::runtime_error("Invalid public key in proposal");
|
||||
|
||||
mPeerID = mPublicKey.getNodeID();
|
||||
mTime = boost::posix_time::second_clock::universal_time();
|
||||
}
|
||||
|
||||
|
||||
LedgerProposal::LedgerProposal(const RippleAddress& naPub, const RippleAddress& naPriv,
|
||||
const uint256& prevLgr, const uint256& position, uint32 closeTime) :
|
||||
mPreviousLedger(prevLgr), mCurrentHash(position), mCloseTime(closeTime), mProposeSeq(0),
|
||||
mPublicKey(naPub), mPrivateKey(naPriv)
|
||||
{
|
||||
mPeerID = mPublicKey.getNodeID();
|
||||
mTime = boost::posix_time::second_clock::universal_time();
|
||||
}
|
||||
|
||||
LedgerProposal::LedgerProposal(const uint256& prevLgr, const uint256& position, uint32 closeTime) :
|
||||
mPreviousLedger(prevLgr), mCurrentHash(position), mCloseTime(closeTime), mProposeSeq(0)
|
||||
{
|
||||
mTime = boost::posix_time::second_clock::universal_time();
|
||||
}
|
||||
|
||||
uint256 LedgerProposal::getSigningHash() const
|
||||
{
|
||||
Serializer s((32 + 32 + 32 + 256 + 256) / 8);
|
||||
|
||||
s.add32(sHP_Proposal);
|
||||
s.add32(mProposeSeq);
|
||||
s.add32(mCloseTime);
|
||||
s.add256(mPreviousLedger);
|
||||
s.add256(mCurrentHash);
|
||||
|
||||
return s.getSHA512Half();
|
||||
}
|
||||
|
||||
bool LedgerProposal::checkSign(const std::string& signature, const uint256& signingHash)
|
||||
{
|
||||
return mPublicKey.verifyNodePublic(signingHash, signature);
|
||||
}
|
||||
|
||||
bool LedgerProposal::changePosition(const uint256& newPosition, uint32 closeTime)
|
||||
{
|
||||
if (mProposeSeq == seqLeave)
|
||||
return false;
|
||||
|
||||
mCurrentHash = newPosition;
|
||||
mCloseTime = closeTime;
|
||||
mTime = boost::posix_time::second_clock::universal_time();
|
||||
++mProposeSeq;
|
||||
return true;
|
||||
}
|
||||
|
||||
void LedgerProposal::bowOut()
|
||||
{
|
||||
mTime = boost::posix_time::second_clock::universal_time();
|
||||
mProposeSeq = seqLeave;
|
||||
}
|
||||
|
||||
std::vector<unsigned char> LedgerProposal::sign(void)
|
||||
{
|
||||
std::vector<unsigned char> ret;
|
||||
|
||||
mPrivateKey.signNodePrivate(getSigningHash(), ret);
|
||||
// XXX If this can fail, find out sooner.
|
||||
// if (!mPrivateKey.signNodePrivate(getSigningHash(), ret))
|
||||
// throw std::runtime_error("unable to sign proposal");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
Json::Value LedgerProposal::getJson() const
|
||||
{
|
||||
Json::Value ret = Json::objectValue;
|
||||
ret["previous_ledger"] = mPreviousLedger.GetHex();
|
||||
|
||||
if (mProposeSeq != seqLeave)
|
||||
{
|
||||
ret["transaction_hash"] = mCurrentHash.GetHex();
|
||||
ret["propose_seq"] = mProposeSeq;
|
||||
}
|
||||
|
||||
ret["close_time"] = mCloseTime;
|
||||
|
||||
if (mPublicKey.isValid())
|
||||
ret["peer_id"] = mPublicKey.humanNodePublic();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
// vim:ts=4
|
||||
78
src/cpp/ripple/LedgerProposal.h
Normal file
78
src/cpp/ripple/LedgerProposal.h
Normal file
@@ -0,0 +1,78 @@
|
||||
#ifndef __PROPOSELEDGER__
|
||||
#define __PROPOSELEDGER__
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
#include <boost/shared_ptr.hpp>
|
||||
|
||||
#include "../json/value.h"
|
||||
|
||||
#include "RippleAddress.h"
|
||||
#include "Serializer.h"
|
||||
#include "InstanceCounter.h"
|
||||
|
||||
DEFINE_INSTANCE(LedgerProposal);
|
||||
|
||||
class LedgerProposal : private IS_INSTANCE(LedgerProposal)
|
||||
{
|
||||
protected:
|
||||
|
||||
uint256 mPreviousLedger, mCurrentHash, mSuppression;
|
||||
uint32 mCloseTime, mProposeSeq;
|
||||
|
||||
uint160 mPeerID;
|
||||
RippleAddress mPublicKey;
|
||||
RippleAddress mPrivateKey; // If ours
|
||||
|
||||
std::string mSignature; // set only if needed
|
||||
boost::posix_time::ptime mTime;
|
||||
|
||||
public:
|
||||
static const uint32 seqLeave = 0xffffffff; // leaving the consensus process
|
||||
|
||||
typedef boost::shared_ptr<LedgerProposal> pointer;
|
||||
|
||||
// proposal from peer
|
||||
LedgerProposal(const uint256& prevLgr, uint32 proposeSeq, const uint256& propose,
|
||||
uint32 closeTime, const RippleAddress& naPeerPublic, const uint256& suppress);
|
||||
|
||||
// our first proposal
|
||||
LedgerProposal(const RippleAddress& pubKey, const RippleAddress& privKey,
|
||||
const uint256& prevLedger, const uint256& position, uint32 closeTime);
|
||||
|
||||
// an unsigned "dummy" proposal for nodes not validating
|
||||
LedgerProposal(const uint256& prevLedger, const uint256& position, uint32 closeTime);
|
||||
|
||||
uint256 getSigningHash() const;
|
||||
bool checkSign(const std::string& signature, const uint256& signingHash);
|
||||
bool checkSign(const std::string& signature) { return checkSign(signature, getSigningHash()); }
|
||||
bool checkSign() { return checkSign(mSignature, getSigningHash()); }
|
||||
|
||||
const uint160& getPeerID() const { return mPeerID; }
|
||||
const uint256& getCurrentHash() const { return mCurrentHash; }
|
||||
const uint256& getPrevLedger() const { return mPreviousLedger; }
|
||||
const uint256& getSuppression() const { return mSuppression; }
|
||||
uint32 getProposeSeq() const { return mProposeSeq; }
|
||||
uint32 getCloseTime() const { return mCloseTime; }
|
||||
const RippleAddress& peekPublic() const { return mPublicKey; }
|
||||
std::vector<unsigned char> getPubKey() const { return mPublicKey.getNodePublic(); }
|
||||
std::vector<unsigned char> sign();
|
||||
|
||||
void setPrevLedger(const uint256& prevLedger) { mPreviousLedger = prevLedger; }
|
||||
void setSignature(const std::string& signature) { mSignature = signature; }
|
||||
bool hasSignature() { return !mSignature.empty(); }
|
||||
bool isPrevLedger(const uint256& pl) { return mPreviousLedger == pl; }
|
||||
bool isBowOut() { return mProposeSeq == seqLeave; }
|
||||
|
||||
const boost::posix_time::ptime getCreateTime() { return mTime; }
|
||||
bool isStale(boost::posix_time::ptime cutoff) { return mTime <= cutoff; }
|
||||
|
||||
bool changePosition(const uint256& newPosition, uint32 newCloseTime);
|
||||
void bowOut();
|
||||
Json::Value getJson() const;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
// vim:ts=4
|
||||
123
src/cpp/ripple/LedgerTiming.cpp
Normal file
123
src/cpp/ripple/LedgerTiming.cpp
Normal file
@@ -0,0 +1,123 @@
|
||||
|
||||
#include "LedgerTiming.h"
|
||||
|
||||
#include <cassert>
|
||||
|
||||
#include <boost/format.hpp>
|
||||
|
||||
#include "Log.h"
|
||||
SETUP_LOG();
|
||||
|
||||
// NOTE: First and last times must be repeated
|
||||
int ContinuousLedgerTiming::LedgerTimeResolution[] = { 10, 10, 20, 30, 60, 90, 120, 120 };
|
||||
|
||||
// Called when a ledger is open and no close is in progress -- when a transaction is received and no close
|
||||
// is in process, or when a close completes. Returns the number of seconds the ledger should be be open.
|
||||
bool ContinuousLedgerTiming::shouldClose(
|
||||
bool anyTransactions,
|
||||
int previousProposers, // proposers in the last closing
|
||||
int proposersClosed, // proposers who have currently closed this ledgers
|
||||
int previousMSeconds, // seconds the previous ledger took to reach consensus
|
||||
int currentMSeconds, // seconds since the previous ledger closed
|
||||
int idleInterval) // network's desired idle interval
|
||||
{
|
||||
if ((previousMSeconds < -1000) || (previousMSeconds > 600000) ||
|
||||
(currentMSeconds < -1000) || (currentMSeconds > 600000))
|
||||
{
|
||||
cLog(lsWARNING) <<
|
||||
boost::str(boost::format("CLC::shouldClose range Trans=%s, Prop: %d/%d, Secs: %d (last:%d)")
|
||||
% (anyTransactions ? "yes" : "no") % previousProposers % proposersClosed
|
||||
% currentMSeconds % previousMSeconds);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!anyTransactions)
|
||||
{ // no transactions so far this interval
|
||||
if (proposersClosed > (previousProposers / 4)) // did we miss a transaction?
|
||||
{
|
||||
cLog(lsTRACE) << "no transactions, many proposers: now (" << proposersClosed << " closed, "
|
||||
<< previousProposers << " before)";
|
||||
return true;
|
||||
}
|
||||
#if 0 // This false triggers on the genesis ledger
|
||||
if (previousMSeconds > (1000 * (LEDGER_IDLE_INTERVAL + 2))) // the last ledger was very slow to close
|
||||
{
|
||||
cLog(lsTRACE) << "was slow to converge (p=" << (previousMSeconds) << ")";
|
||||
if (previousMSeconds < 2000)
|
||||
return previousMSeconds;
|
||||
return previousMSeconds - 1000;
|
||||
}
|
||||
#endif
|
||||
return currentMSeconds >= (idleInterval * 1000); // normal idle
|
||||
}
|
||||
|
||||
return true; // this ledger should close now
|
||||
}
|
||||
|
||||
// Returns whether we have a consensus or not. If so, we expect all honest nodes
|
||||
// to already have everything they need to accept a consensus. Our vote is 'locked in'.
|
||||
bool ContinuousLedgerTiming::haveConsensus(
|
||||
int previousProposers, // proposers in the last closing (not including us)
|
||||
int currentProposers, // proposers in this closing so far (not including us)
|
||||
int currentAgree, // proposers who agree with us
|
||||
int currentFinished, // proposers who have validated a ledger after this one
|
||||
int previousAgreeTime, // how long it took to agree on the last ledger
|
||||
int currentAgreeTime, // how long we've been trying to agree
|
||||
bool forReal) // deciding whether to stop consensus process
|
||||
{
|
||||
cLog(lsTRACE) << boost::str(boost::format("CLC::haveConsensus: prop=%d/%d agree=%d validated=%d time=%d/%d%s") %
|
||||
currentProposers % previousProposers % currentAgree % currentFinished % currentAgreeTime % previousAgreeTime %
|
||||
(forReal ? "" : "X"));
|
||||
|
||||
if (currentAgreeTime <= LEDGER_MIN_CONSENSUS)
|
||||
return false;
|
||||
|
||||
if (currentProposers < (previousProposers * 3 / 4))
|
||||
{ // Less than 3/4 of the last ledger's proposers are present, we may need more time
|
||||
if (currentAgreeTime < (previousAgreeTime + LEDGER_MIN_CONSENSUS))
|
||||
{
|
||||
tLog(forReal, lsTRACE) << "too fast, not enough proposers";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// If 80% of current proposers (plus us) agree on a set, we have consensus
|
||||
if (((currentAgree * 100 + 100) / (currentProposers + 1)) > 80)
|
||||
{
|
||||
tLog(forReal, lsINFO) << "normal consensus";
|
||||
return true;
|
||||
}
|
||||
|
||||
// If 50% of the nodes on your UNL have moved on, you should declare consensus
|
||||
if (((currentFinished * 100) / (currentProposers + 1)) > 50)
|
||||
{
|
||||
tLog(forReal, lsWARNING) << "We see no consensus, but 50% of nodes have moved on";
|
||||
return true;
|
||||
}
|
||||
|
||||
// no consensus yet
|
||||
tLog(forReal, lsTRACE) << "no consensus";
|
||||
return false;
|
||||
}
|
||||
|
||||
int ContinuousLedgerTiming::getNextLedgerTimeResolution(int previousResolution, bool previousAgree, int ledgerSeq)
|
||||
{
|
||||
assert(ledgerSeq);
|
||||
if ((!previousAgree) && ((ledgerSeq % LEDGER_RES_DECREASE) == 0))
|
||||
{ // reduce resolution
|
||||
int i = 1;
|
||||
while (LedgerTimeResolution[i] != previousResolution)
|
||||
++i;
|
||||
return LedgerTimeResolution[i + 1];
|
||||
}
|
||||
|
||||
if ((previousAgree) && ((ledgerSeq % LEDGER_RES_INCREASE) == 0))
|
||||
{ // increase resolution
|
||||
int i = 1;
|
||||
while (LedgerTimeResolution[i] != previousResolution)
|
||||
++i;
|
||||
return LedgerTimeResolution[i - 1];
|
||||
}
|
||||
|
||||
return previousResolution;
|
||||
}
|
||||
74
src/cpp/ripple/LedgerTiming.h
Normal file
74
src/cpp/ripple/LedgerTiming.h
Normal file
@@ -0,0 +1,74 @@
|
||||
#ifndef __LEDGERTIMING__
|
||||
#define __LEDGERTIMING__
|
||||
|
||||
// The number of seconds a ledger may remain idle before closing
|
||||
# define LEDGER_IDLE_INTERVAL 15
|
||||
|
||||
// The number of seconds a validation remains current after its ledger's close time
|
||||
// This is a safety to protect against very old validations and the time it takes to adjust
|
||||
// the close time accuracy window
|
||||
# define LEDGER_VAL_INTERVAL 600
|
||||
|
||||
// The number of seconds before a close time that we consider a validation acceptable
|
||||
// This protects against extreme clock errors
|
||||
# define LEDGER_EARLY_INTERVAL 240
|
||||
|
||||
// The number of milliseconds we wait minimum to ensure participation
|
||||
# define LEDGER_MIN_CONSENSUS 2000
|
||||
|
||||
// Initial resolution of ledger close time
|
||||
# define LEDGER_TIME_ACCURACY 30
|
||||
|
||||
// How often to increase resolution
|
||||
# define LEDGER_RES_INCREASE 8
|
||||
|
||||
// How often to decrease resolution
|
||||
# define LEDGER_RES_DECREASE 1
|
||||
|
||||
// How often we check state or change positions (in milliseconds)
|
||||
# define LEDGER_GRANULARITY 1000
|
||||
|
||||
// The percentage of active trusted validators that must be able to
|
||||
// keep up with the network or we consider the network overloaded
|
||||
# define LEDGER_NET_RATIO 70
|
||||
|
||||
// How long we consider a proposal fresh
|
||||
# define PROPOSE_FRESHNESS 20
|
||||
|
||||
// How often we force generating a new proposal to keep ours fresh
|
||||
# define PROPOSE_INTERVAL 12
|
||||
|
||||
// Avalanche tuning
|
||||
#define AV_INIT_CONSENSUS_PCT 50 // percentage of nodes on our UNL that must vote yes
|
||||
|
||||
#define AV_MID_CONSENSUS_TIME 50 // percentage of previous close time before we advance
|
||||
#define AV_MID_CONSENSUS_PCT 65 // percentage of nodes that most vote yes after advancing
|
||||
|
||||
#define AV_LATE_CONSENSUS_TIME 85 // percentage of previous close time before we advance
|
||||
#define AV_LATE_CONSENSUS_PCT 70 // percentage of nodes that most vote yes after advancing
|
||||
|
||||
|
||||
class ContinuousLedgerTiming
|
||||
{
|
||||
public:
|
||||
|
||||
static int LedgerTimeResolution[];
|
||||
|
||||
// Returns the number of seconds the ledger was or should be open
|
||||
// Call when a consensus is reached and when any transaction is relayed to be added
|
||||
static bool shouldClose(
|
||||
bool anyTransactions,
|
||||
int previousProposers, int proposersClosed,
|
||||
int previousSeconds, int currentSeconds,
|
||||
int idleInterval);
|
||||
|
||||
static bool haveConsensus(
|
||||
int previousProposers, int currentProposers,
|
||||
int currentAgree, int currentClosed,
|
||||
int previousAgreeTime, int currentAgreeTime,
|
||||
bool forReal);
|
||||
|
||||
static int getNextLedgerTimeResolution(int previousResolution, bool previousAgree, int ledgerSeq);
|
||||
};
|
||||
|
||||
#endif
|
||||
188
src/cpp/ripple/Log.cpp
Normal file
188
src/cpp/ripple/Log.cpp
Normal file
@@ -0,0 +1,188 @@
|
||||
|
||||
#include "Log.h"
|
||||
|
||||
#include <fstream>
|
||||
|
||||
#include <boost/date_time/posix_time/posix_time.hpp>
|
||||
#include <boost/algorithm/string.hpp>
|
||||
|
||||
boost::recursive_mutex Log::sLock;
|
||||
|
||||
LogSeverity Log::sMinSeverity = lsINFO;
|
||||
|
||||
std::ofstream* Log::outStream = NULL;
|
||||
boost::filesystem::path *Log::pathToLog = NULL;
|
||||
uint32 Log::logRotateCounter = 0;
|
||||
|
||||
LogPartition* LogPartition::headLog = NULL;
|
||||
|
||||
LogPartition::LogPartition(const char *name) : mNextLog(headLog), mMinSeverity(lsWARNING)
|
||||
{
|
||||
const char *ptr = strrchr(name, '/');
|
||||
mName = (ptr == NULL) ? name : (ptr + 1);
|
||||
|
||||
size_t p = mName.find(".cpp");
|
||||
if (p != std::string::npos)
|
||||
mName.erase(mName.begin() + p, mName.end());
|
||||
|
||||
headLog = this;
|
||||
}
|
||||
|
||||
std::vector< std::pair<std::string, std::string> > LogPartition::getSeverities()
|
||||
{
|
||||
std::vector< std::pair<std::string, std::string> > sevs;
|
||||
|
||||
for (LogPartition *l = headLog; l != NULL; l = l->mNextLog)
|
||||
sevs.push_back(std::make_pair(l->mName, Log::severityToString(l->mMinSeverity)));
|
||||
|
||||
return sevs;
|
||||
}
|
||||
|
||||
Log::~Log()
|
||||
{
|
||||
std::string logMsg = boost::posix_time::to_simple_string(boost::posix_time::second_clock::universal_time());
|
||||
if (!mPartitionName.empty())
|
||||
logMsg += " " + mPartitionName + ":";
|
||||
else
|
||||
logMsg += " ";
|
||||
switch (mSeverity)
|
||||
{
|
||||
case lsTRACE: logMsg += "TRC "; break;
|
||||
case lsDEBUG: logMsg += "DBG "; break;
|
||||
case lsINFO: logMsg += "NFO "; break;
|
||||
case lsWARNING: logMsg += "WRN "; break;
|
||||
case lsERROR: logMsg += "ERR "; break;
|
||||
case lsFATAL: logMsg += "FTL "; break;
|
||||
case lsINVALID: assert(false); return;
|
||||
}
|
||||
logMsg += oss.str();
|
||||
boost::recursive_mutex::scoped_lock sl(sLock);
|
||||
if (mSeverity >= sMinSeverity)
|
||||
std::cerr << logMsg << std::endl;
|
||||
if (outStream != NULL)
|
||||
(*outStream) << logMsg << std::endl;
|
||||
}
|
||||
|
||||
|
||||
std::string Log::rotateLog(void)
|
||||
{
|
||||
boost::recursive_mutex::scoped_lock sl(sLock);
|
||||
boost::filesystem::path abs_path;
|
||||
std::string abs_path_str;
|
||||
|
||||
uint32 failsafe = 0;
|
||||
|
||||
std::string abs_new_path_str;
|
||||
do {
|
||||
std::string s;
|
||||
std::stringstream out;
|
||||
|
||||
failsafe++;
|
||||
if (failsafe == std::numeric_limits<uint32>::max()) {
|
||||
return "unable to create new log file; too many log files!";
|
||||
}
|
||||
abs_path = boost::filesystem::absolute("");
|
||||
abs_path /= *pathToLog;
|
||||
abs_path_str = abs_path.parent_path().string();
|
||||
out << logRotateCounter;
|
||||
s = out.str();
|
||||
|
||||
|
||||
abs_new_path_str = abs_path_str + "/" + s + + "_" + pathToLog->filename().string();
|
||||
|
||||
logRotateCounter++;
|
||||
|
||||
} while (boost::filesystem::exists(boost::filesystem::path(abs_new_path_str)));
|
||||
|
||||
outStream->close();
|
||||
boost::filesystem::rename(abs_path, boost::filesystem::path(abs_new_path_str));
|
||||
|
||||
|
||||
|
||||
setLogFile(*pathToLog);
|
||||
|
||||
return abs_new_path_str;
|
||||
|
||||
}
|
||||
|
||||
void Log::setMinSeverity(LogSeverity s, bool all)
|
||||
{
|
||||
boost::recursive_mutex::scoped_lock sl(sLock);
|
||||
sMinSeverity = s;
|
||||
if (all)
|
||||
LogPartition::setSeverity(s);
|
||||
}
|
||||
|
||||
LogSeverity Log::getMinSeverity()
|
||||
{
|
||||
boost::recursive_mutex::scoped_lock sl(sLock);
|
||||
return sMinSeverity;
|
||||
}
|
||||
|
||||
std::string Log::severityToString(LogSeverity s)
|
||||
{
|
||||
switch (s)
|
||||
{
|
||||
case lsTRACE: return "Trace";
|
||||
case lsDEBUG: return "Debug";
|
||||
case lsINFO: return "Info";
|
||||
case lsWARNING: return "Warning";
|
||||
case lsERROR: return "Error";
|
||||
case lsFATAL: return "Fatal";
|
||||
default: assert(false); return "Unknown";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
LogSeverity Log::stringToSeverity(const std::string& s)
|
||||
{
|
||||
if (boost::iequals(s, "trace"))
|
||||
return lsTRACE;
|
||||
if (boost::iequals(s, "debug"))
|
||||
return lsDEBUG;
|
||||
if (boost::iequals(s, "info") || boost::iequals(s, "information"))
|
||||
return lsINFO;
|
||||
if (boost::iequals(s, "warn") || boost::iequals(s, "warning") || boost::iequals(s, "warnings"))
|
||||
return lsWARNING;
|
||||
if (boost::iequals(s, "error") || boost::iequals(s, "errors"))
|
||||
return lsERROR;
|
||||
if (boost::iequals(s, "fatal") || boost::iequals(s, "fatals"))
|
||||
return lsFATAL;
|
||||
return lsINVALID;
|
||||
}
|
||||
|
||||
void Log::setLogFile(boost::filesystem::path path)
|
||||
{
|
||||
std::ofstream* newStream = new std::ofstream(path.c_str(), std::fstream::app);
|
||||
if (!newStream->good())
|
||||
{
|
||||
delete newStream;
|
||||
newStream = NULL;
|
||||
}
|
||||
|
||||
boost::recursive_mutex::scoped_lock sl(sLock);
|
||||
if (outStream != NULL)
|
||||
delete outStream;
|
||||
outStream = newStream;
|
||||
if (outStream)
|
||||
Log(lsINFO) << "Starting up";
|
||||
|
||||
pathToLog = new boost::filesystem::path(path);
|
||||
}
|
||||
|
||||
bool LogPartition::setSeverity(const std::string& partition, LogSeverity severity)
|
||||
{
|
||||
for (LogPartition *p = headLog; p != NULL; p = p->mNextLog)
|
||||
if (boost::iequals(p->mName, partition))
|
||||
{
|
||||
p->mMinSeverity = severity;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void LogPartition::setSeverity(LogSeverity severity)
|
||||
{
|
||||
for (LogPartition *p = headLog; p != NULL; p = p->mNextLog)
|
||||
p->mMinSeverity = severity;
|
||||
}
|
||||
106
src/cpp/ripple/Log.h
Normal file
106
src/cpp/ripple/Log.h
Normal file
@@ -0,0 +1,106 @@
|
||||
#ifndef __LOG__
|
||||
#define __LOG__
|
||||
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <limits>
|
||||
|
||||
#include <boost/thread/recursive_mutex.hpp>
|
||||
#include <boost/filesystem.hpp>
|
||||
|
||||
// Ensure that we don't get value.h without writer.h
|
||||
#include "../json/json.h"
|
||||
|
||||
#include "types.h"
|
||||
|
||||
// Put at the beginning of a C++ file that needs its own log partition
|
||||
#define SETUP_LOG() static LogPartition logPartition(__FILE__)
|
||||
|
||||
// Standard conditional log
|
||||
#define cLog(x) if (!logPartition.doLog(x)) do {} while (0); else Log(x, logPartition)
|
||||
|
||||
// Log only if an additional condition 'c' is true. Condition is not computed if not needed
|
||||
#define tLog(c,x) if (!logPartition.doLog(x) || !(c)) do {} while(0); else Log(x, logPartition)
|
||||
|
||||
// Check if should log
|
||||
#define sLog(x) (logPartition.doLog(x))
|
||||
|
||||
|
||||
enum LogSeverity
|
||||
{
|
||||
lsINVALID = -1, // used to indicate an invalid severity
|
||||
lsTRACE = 0, // Very low-level progress information, details inside an operation
|
||||
lsDEBUG = 1, // Function-level progress information, operations
|
||||
lsINFO = 2, // Server-level progress information, major operations
|
||||
lsWARNING = 3, // Conditions that warrant human attention, may indicate a problem
|
||||
lsERROR = 4, // A condition that indicates a problem
|
||||
lsFATAL = 5 // A severe condition that indicates a server problem
|
||||
};
|
||||
|
||||
class LogPartition
|
||||
{
|
||||
protected:
|
||||
static LogPartition* headLog;
|
||||
|
||||
LogPartition* mNextLog;
|
||||
LogSeverity mMinSeverity;
|
||||
std::string mName;
|
||||
|
||||
public:
|
||||
LogPartition(const char *name);
|
||||
|
||||
bool doLog(LogSeverity s) { return s >= mMinSeverity; }
|
||||
const std::string& getName() const { return mName; }
|
||||
|
||||
static bool setSeverity(const std::string& partition, LogSeverity severity);
|
||||
static void setSeverity(LogSeverity severity);
|
||||
static std::vector< std::pair<std::string, std::string> > getSeverities();
|
||||
};
|
||||
|
||||
class Log
|
||||
{
|
||||
private:
|
||||
Log(const Log&); // no implementation
|
||||
Log& operator=(const Log&); // no implementation
|
||||
|
||||
protected:
|
||||
static boost::recursive_mutex sLock;
|
||||
static LogSeverity sMinSeverity;
|
||||
static std::ofstream* outStream;
|
||||
|
||||
mutable std::ostringstream oss;
|
||||
LogSeverity mSeverity;
|
||||
std::string mPartitionName;
|
||||
|
||||
static boost::filesystem::path *pathToLog;
|
||||
static uint32 logRotateCounter;
|
||||
|
||||
public:
|
||||
Log(LogSeverity s) : mSeverity(s)
|
||||
{ ; }
|
||||
|
||||
Log(LogSeverity s, const LogPartition& p) : mSeverity(s), mPartitionName(p.getName())
|
||||
{ ; }
|
||||
|
||||
~Log();
|
||||
|
||||
template<typename T> std::ostream& operator<<(const T& t) const
|
||||
{
|
||||
return oss << t;
|
||||
}
|
||||
|
||||
std::ostringstream& ref(void) const
|
||||
{
|
||||
return oss;
|
||||
}
|
||||
|
||||
static std::string severityToString(LogSeverity);
|
||||
static LogSeverity stringToSeverity(const std::string&);
|
||||
|
||||
static LogSeverity getMinSeverity();
|
||||
static void setMinSeverity(LogSeverity, bool all);
|
||||
static void setLogFile(boost::filesystem::path);
|
||||
static std::string rotateLog(void);
|
||||
};
|
||||
|
||||
#endif
|
||||
1252
src/cpp/ripple/NetworkOPs.cpp
Normal file
1252
src/cpp/ripple/NetworkOPs.cpp
Normal file
File diff suppressed because it is too large
Load Diff
242
src/cpp/ripple/NetworkOPs.h
Normal file
242
src/cpp/ripple/NetworkOPs.h
Normal file
@@ -0,0 +1,242 @@
|
||||
#ifndef __NETWORK_OPS__
|
||||
#define __NETWORK_OPS__
|
||||
|
||||
#include <boost/interprocess/sync/interprocess_upgradable_mutex.hpp>
|
||||
#include <boost/interprocess/sync/sharable_lock.hpp>
|
||||
#include <boost/unordered_map.hpp>
|
||||
#include <boost/unordered_set.hpp>
|
||||
|
||||
#include "AccountState.h"
|
||||
#include "LedgerMaster.h"
|
||||
#include "NicknameState.h"
|
||||
#include "RippleState.h"
|
||||
#include "SerializedValidation.h"
|
||||
#include "LedgerAcquire.h"
|
||||
#include "LedgerProposal.h"
|
||||
|
||||
// Operations that clients may wish to perform against the network
|
||||
// Master operational handler, server sequencer, network tracker
|
||||
|
||||
class Peer;
|
||||
class LedgerConsensus;
|
||||
|
||||
class InfoSub
|
||||
{
|
||||
public:
|
||||
|
||||
virtual ~InfoSub() { ; }
|
||||
|
||||
virtual void send(const Json::Value& jvObj) = 0;
|
||||
};
|
||||
|
||||
class NetworkOPs
|
||||
{
|
||||
public:
|
||||
enum Fault
|
||||
{ // exceptions these functions can throw
|
||||
IO_ERROR = 1,
|
||||
NO_NETWORK = 2,
|
||||
};
|
||||
|
||||
enum OperatingMode
|
||||
{ // how we process transactions or account balance requests
|
||||
omDISCONNECTED = 0, // not ready to process requests
|
||||
omCONNECTED = 1, // convinced we are talking to the network
|
||||
omTRACKING = 2, // convinced we agree with the network
|
||||
omFULL = 3 // we have the ledger and can even validate
|
||||
};
|
||||
|
||||
protected:
|
||||
typedef boost::unordered_map<uint160,boost::unordered_set<InfoSub*> > subInfoMapType;
|
||||
typedef boost::unordered_map<uint160,boost::unordered_set<InfoSub*> >::value_type subInfoMapValue;
|
||||
typedef boost::unordered_map<uint160,boost::unordered_set<InfoSub*> >::iterator subInfoMapIterator;
|
||||
|
||||
typedef boost::unordered_map<uint160,std::pair<InfoSub*,uint32> > subSubmitMapType;
|
||||
|
||||
OperatingMode mMode;
|
||||
bool mNeedNetworkLedger;
|
||||
boost::posix_time::ptime mConnectTime;
|
||||
boost::asio::deadline_timer mNetTimer;
|
||||
boost::shared_ptr<LedgerConsensus> mConsensus;
|
||||
boost::unordered_map<uint160,
|
||||
std::list<LedgerProposal::pointer> > mStoredProposals;
|
||||
|
||||
LedgerMaster* mLedgerMaster;
|
||||
LedgerAcquire::pointer mAcquiringLedger;
|
||||
|
||||
int mCloseTimeOffset;
|
||||
|
||||
// last ledger close
|
||||
int mLastCloseProposers, mLastCloseConvergeTime;
|
||||
uint256 mLastCloseHash;
|
||||
uint32 mLastCloseTime;
|
||||
uint32 mLastValidationTime;
|
||||
|
||||
// XXX Split into more locks.
|
||||
boost::interprocess::interprocess_upgradable_mutex mMonitorLock;
|
||||
subInfoMapType mSubAccount;
|
||||
subInfoMapType mSubRTAccount;
|
||||
subSubmitMapType mSubmitMap;
|
||||
|
||||
boost::unordered_set<InfoSub*> mSubLedger; // accepted ledgers
|
||||
boost::unordered_set<InfoSub*> mSubServer; // when server changes connectivity state
|
||||
boost::unordered_set<InfoSub*> mSubTransactions; // all accepted transactions
|
||||
boost::unordered_set<InfoSub*> mSubRTTransactions; // all proposed and accepted transactions
|
||||
|
||||
|
||||
void setMode(OperatingMode);
|
||||
|
||||
Json::Value transJson(const SerializedTransaction& stTxn, TER terResult, bool bAccepted, Ledger::ref lpCurrent, const std::string& strType);
|
||||
bool haveConsensusObject();
|
||||
|
||||
Json::Value pubBootstrapAccountInfo(Ledger::ref lpAccepted, const RippleAddress& naAccountID);
|
||||
|
||||
void pubAcceptedTransaction(Ledger::ref lpCurrent, const SerializedTransaction& stTxn, TER terResult);
|
||||
void pubAccountTransaction(Ledger::ref lpCurrent, const SerializedTransaction& stTxn, TER terResult,bool accepted);
|
||||
|
||||
public:
|
||||
NetworkOPs(boost::asio::io_service& io_service, LedgerMaster* pLedgerMaster);
|
||||
|
||||
// network information
|
||||
uint32 getNetworkTimeNC();
|
||||
uint32 getCloseTimeNC();
|
||||
uint32 getValidationTimeNC();
|
||||
void closeTimeOffset(int);
|
||||
boost::posix_time::ptime getNetworkTimePT();
|
||||
uint32 getLedgerID(const uint256& hash);
|
||||
uint32 getCurrentLedgerID();
|
||||
OperatingMode getOperatingMode() { return mMode; }
|
||||
inline bool available() {
|
||||
// XXX Later this can be relaxed to omCONNECTED
|
||||
return mMode >= omTRACKING;
|
||||
}
|
||||
|
||||
Ledger::pointer getClosedLedger() { return mLedgerMaster->getClosedLedger(); }
|
||||
Ledger::pointer getCurrentLedger() { return mLedgerMaster->getCurrentLedger(); }
|
||||
Ledger::pointer getLedgerByHash(const uint256& hash) { return mLedgerMaster->getLedgerByHash(hash); }
|
||||
Ledger::pointer getLedgerBySeq(const uint32 seq) { return mLedgerMaster->getLedgerBySeq(seq); }
|
||||
|
||||
uint256 getClosedLedgerHash()
|
||||
{ return mLedgerMaster->getClosedLedger()->getHash(); }
|
||||
|
||||
SLE::pointer getSLE(Ledger::pointer lpLedger, const uint256& uHash) { return lpLedger->getSLE(uHash); }
|
||||
|
||||
//
|
||||
// Transaction operations
|
||||
//
|
||||
Transaction::pointer submitTransaction(const Transaction::pointer& tpTrans);
|
||||
|
||||
Transaction::pointer processTransaction(Transaction::pointer transaction);
|
||||
Transaction::pointer findTransactionByID(const uint256& transactionID);
|
||||
int findTransactionsBySource(const uint256& uLedger, std::list<Transaction::pointer>&, const RippleAddress& sourceAccount,
|
||||
uint32 minSeq, uint32 maxSeq);
|
||||
int findTransactionsByDestination(std::list<Transaction::pointer>&, const RippleAddress& destinationAccount,
|
||||
uint32 startLedgerSeq, uint32 endLedgerSeq, int maxTransactions);
|
||||
|
||||
//
|
||||
// Account functions
|
||||
//
|
||||
|
||||
AccountState::pointer getAccountState(const uint256& uLedger, const RippleAddress& accountID);
|
||||
SLE::pointer getGenerator(const uint256& uLedger, const uint160& uGeneratorID);
|
||||
|
||||
//
|
||||
// Directory functions
|
||||
//
|
||||
|
||||
STVector256 getDirNodeInfo(const uint256& uLedger, const uint256& uRootIndex,
|
||||
uint64& uNodePrevious, uint64& uNodeNext);
|
||||
|
||||
//
|
||||
// Nickname functions
|
||||
//
|
||||
|
||||
NicknameState::pointer getNicknameState(const uint256& uLedger, const std::string& strNickname);
|
||||
|
||||
//
|
||||
// Owner functions
|
||||
//
|
||||
|
||||
Json::Value getOwnerInfo(const uint256& uLedger, const RippleAddress& naAccount);
|
||||
Json::Value getOwnerInfo(Ledger::pointer lpLedger, const RippleAddress& naAccount);
|
||||
|
||||
// raw object operations
|
||||
bool findRawLedger(const uint256& ledgerHash, std::vector<unsigned char>& rawLedger);
|
||||
bool findRawTransaction(const uint256& transactionHash, std::vector<unsigned char>& rawTransaction);
|
||||
bool findAccountNode(const uint256& nodeHash, std::vector<unsigned char>& rawAccountNode);
|
||||
bool findTransactionNode(const uint256& nodeHash, std::vector<unsigned char>& rawTransactionNode);
|
||||
|
||||
// tree synchronization operations
|
||||
bool getTransactionTreeNodes(uint32 ledgerSeq, const uint256& myNodeID,
|
||||
const std::vector<unsigned char>& myNode, std::list< std::vector<unsigned char> >& newNodes);
|
||||
bool getAccountStateNodes(uint32 ledgerSeq, const uint256& myNodeId,
|
||||
const std::vector<unsigned char>& myNode, std::list< std::vector<unsigned char> >& newNodes);
|
||||
|
||||
// ledger proposal/close functions
|
||||
void processTrustedProposal(LedgerProposal::pointer proposal, boost::shared_ptr<ripple::TMProposeSet> set,
|
||||
RippleAddress nodePublic, uint256 checkLedger, bool sigGood);
|
||||
bool gotTXData(const boost::shared_ptr<Peer>& peer, const uint256& hash,
|
||||
const std::list<SHAMapNode>& nodeIDs, const std::list< std::vector<unsigned char> >& nodeData);
|
||||
bool recvValidation(const SerializedValidation::pointer& val);
|
||||
SHAMap::pointer getTXMap(const uint256& hash);
|
||||
bool hasTXSet(const boost::shared_ptr<Peer>& peer, const uint256& set, ripple::TxSetStatus status);
|
||||
void mapComplete(const uint256& hash, SHAMap::ref map);
|
||||
|
||||
// network state machine
|
||||
void checkState(const boost::system::error_code& result);
|
||||
void switchLastClosedLedger(Ledger::pointer newLedger, bool duringConsensus); // Used for the "jump" case
|
||||
bool checkLastClosedLedger(const std::vector<Peer::pointer>&, uint256& networkClosed);
|
||||
int beginConsensus(const uint256& networkClosed, Ledger::ref closingLedger);
|
||||
void endConsensus(bool correctLCL);
|
||||
void setStandAlone() { setMode(omFULL); }
|
||||
void setStateTimer();
|
||||
void newLCL(int proposers, int convergeTime, const uint256& ledgerHash);
|
||||
void needNetworkLedger() { mNeedNetworkLedger = true; }
|
||||
void clearNeedNetworkLedger() { mNeedNetworkLedger = false; }
|
||||
bool isNeedNetworkLedger() { return mNeedNetworkLedger; }
|
||||
void consensusViewChange();
|
||||
int getPreviousProposers() { return mLastCloseProposers; }
|
||||
int getPreviousConvergeTime() { return mLastCloseConvergeTime; }
|
||||
uint32 getLastCloseTime() { return mLastCloseTime; }
|
||||
void setLastCloseTime(uint32 t) { mLastCloseTime = t; }
|
||||
Json::Value getServerInfo();
|
||||
uint32 acceptLedger();
|
||||
boost::unordered_map<uint160,
|
||||
std::list<LedgerProposal::pointer> >& peekStoredProposals() { return mStoredProposals; }
|
||||
void storeProposal(const LedgerProposal::pointer& proposal, const RippleAddress& peerPublic);
|
||||
uint256 getConsensusLCL();
|
||||
|
||||
// client information retrieval functions
|
||||
std::vector< std::pair<uint32, uint256> >
|
||||
getAffectedAccounts(const RippleAddress& account, uint32 minLedger, uint32 maxLedger);
|
||||
std::vector<RippleAddress> getLedgerAffectedAccounts(uint32 ledgerSeq);
|
||||
std::vector<SerializedTransaction> getLedgerTransactions(uint32 ledgerSeq);
|
||||
|
||||
//
|
||||
// Monitoring: publisher side
|
||||
//
|
||||
void pubLedger(Ledger::ref lpAccepted);
|
||||
void pubProposedTransaction(Ledger::ref lpCurrent, const SerializedTransaction& stTxn, TER terResult);
|
||||
|
||||
|
||||
//
|
||||
// Monitoring: subscriber side
|
||||
//
|
||||
void subAccount(InfoSub* ispListener, const boost::unordered_set<RippleAddress>& vnaAccountIDs,bool rt);
|
||||
void unsubAccount(InfoSub* ispListener, const boost::unordered_set<RippleAddress>& vnaAccountIDs,bool rt);
|
||||
|
||||
bool subLedger(InfoSub* ispListener, Json::Value& jvResult);
|
||||
bool unsubLedger(InfoSub* ispListener);
|
||||
|
||||
bool subServer(InfoSub* ispListener, Json::Value& jvResult);
|
||||
bool unsubServer(InfoSub* ispListener);
|
||||
|
||||
bool subTransactions(InfoSub* ispListener);
|
||||
bool unsubTransactions(InfoSub* ispListener);
|
||||
|
||||
bool subRTTransactions(InfoSub* ispListener);
|
||||
bool unsubRTTransactions(InfoSub* ispListener);
|
||||
};
|
||||
|
||||
#endif
|
||||
// vim:ts=4
|
||||
27
src/cpp/ripple/NetworkStatus.h
Normal file
27
src/cpp/ripple/NetworkStatus.h
Normal file
@@ -0,0 +1,27 @@
|
||||
#ifndef __NETWORKSTATUS__
|
||||
#define __NETWORKSTATUS__
|
||||
|
||||
struct NSBit
|
||||
{ // a network status bit
|
||||
const char *name, *description;
|
||||
int number;
|
||||
};
|
||||
|
||||
struct NetworkStatus
|
||||
{
|
||||
static const int nsbConnected=0; // connected to the network
|
||||
static const int nsbAccepted=1; // accept this as the real network
|
||||
static const int nsbFastSynching=2; // catching up, skipping transactions
|
||||
static const int nsbSlowSynching=3; // catching up, txn by txn
|
||||
static const int nsbSynched=4; // in synch with the network
|
||||
static const int nsbIdentifiable=5; // not hiding our identity
|
||||
static const int nsbLedgerSync=6; // participating in ledger sync
|
||||
static const int nsbStuck=7; // unable to sync
|
||||
static const int nsbShuttingDown=8; // node is shutting down
|
||||
|
||||
static const int nnbCount=32;
|
||||
std::bitset<nnbCount> nsbValues;
|
||||
std::map<int,NSBit> nsbData;
|
||||
};
|
||||
|
||||
#endif
|
||||
29
src/cpp/ripple/NicknameState.cpp
Normal file
29
src/cpp/ripple/NicknameState.cpp
Normal file
@@ -0,0 +1,29 @@
|
||||
#include "NicknameState.h"
|
||||
|
||||
NicknameState::NicknameState(SerializedLedgerEntry::pointer ledgerEntry) :
|
||||
mLedgerEntry(ledgerEntry)
|
||||
{
|
||||
if (!mLedgerEntry || mLedgerEntry->getType() != ltNICKNAME) return;
|
||||
}
|
||||
|
||||
bool NicknameState::haveMinimumOffer() const
|
||||
{
|
||||
return mLedgerEntry->isFieldPresent(sfMinimumOffer);
|
||||
}
|
||||
|
||||
STAmount NicknameState::getMinimumOffer() const
|
||||
{
|
||||
return mLedgerEntry->isFieldPresent(sfMinimumOffer)
|
||||
? mLedgerEntry->getFieldAmount(sfMinimumOffer)
|
||||
: STAmount();
|
||||
}
|
||||
|
||||
RippleAddress NicknameState::getAccountID() const
|
||||
{
|
||||
return mLedgerEntry->getFieldAccount(sfAccount);
|
||||
}
|
||||
|
||||
void NicknameState::addJson(Json::Value& val)
|
||||
{
|
||||
val = mLedgerEntry->getJson(0);
|
||||
}
|
||||
37
src/cpp/ripple/NicknameState.h
Normal file
37
src/cpp/ripple/NicknameState.h
Normal file
@@ -0,0 +1,37 @@
|
||||
#ifndef _NICKNAMESTATE_
|
||||
#define _NICKNAMESTATE_
|
||||
|
||||
//
|
||||
// State of a nickname node.
|
||||
// - Isolate ledger entry format.
|
||||
//
|
||||
|
||||
#include "SerializedLedger.h"
|
||||
|
||||
#include <boost/shared_ptr.hpp>
|
||||
|
||||
class NicknameState
|
||||
{
|
||||
public:
|
||||
typedef boost::shared_ptr<NicknameState> pointer;
|
||||
|
||||
private:
|
||||
SerializedLedgerEntry::pointer mLedgerEntry;
|
||||
|
||||
public:
|
||||
NicknameState(SerializedLedgerEntry::pointer ledgerEntry); // For accounts in a ledger
|
||||
|
||||
bool haveMinimumOffer() const;
|
||||
STAmount getMinimumOffer() const;
|
||||
RippleAddress getAccountID() const;
|
||||
|
||||
SerializedLedgerEntry::pointer getSLE() { return mLedgerEntry; }
|
||||
const SerializedLedgerEntry& peekSLE() const { return *mLedgerEntry; }
|
||||
SerializedLedgerEntry& peekSLE() { return *mLedgerEntry; }
|
||||
|
||||
std::vector<unsigned char> getRaw() const;
|
||||
void addJson(Json::Value& value);
|
||||
};
|
||||
|
||||
#endif
|
||||
// vim:ts=4
|
||||
17
src/cpp/ripple/Operation.cpp
Normal file
17
src/cpp/ripple/Operation.cpp
Normal file
@@ -0,0 +1,17 @@
|
||||
#include "Operation.h"
|
||||
#include "Config.h"
|
||||
|
||||
/*
|
||||
We also need to charge for each op
|
||||
|
||||
*/
|
||||
|
||||
namespace Script {
|
||||
|
||||
|
||||
int Operation::getFee()
|
||||
{
|
||||
return(theConfig.FEE_CONTRACT_OPERATION);
|
||||
}
|
||||
|
||||
}
|
||||
318
src/cpp/ripple/Operation.h
Normal file
318
src/cpp/ripple/Operation.h
Normal file
@@ -0,0 +1,318 @@
|
||||
#include "Interpreter.h"
|
||||
|
||||
namespace Script {
|
||||
|
||||
// Contracts are non typed have variable data types
|
||||
|
||||
|
||||
class Operation
|
||||
{
|
||||
public:
|
||||
// returns false if there was an error
|
||||
virtual bool work(Interpreter* interpreter)=0;
|
||||
|
||||
virtual int getFee();
|
||||
|
||||
virtual ~Operation() { ; }
|
||||
};
|
||||
|
||||
// this is just an Int in the code
|
||||
class IntOp : public Operation
|
||||
{
|
||||
public:
|
||||
bool work(Interpreter* interpreter)
|
||||
{
|
||||
Data::pointer data=interpreter->getIntData();
|
||||
if(data->isInt32())
|
||||
{
|
||||
interpreter->pushStack( data );
|
||||
return(true);
|
||||
}
|
||||
return(false);
|
||||
}
|
||||
};
|
||||
|
||||
class FloatOp : public Operation
|
||||
{
|
||||
public:
|
||||
bool work(Interpreter* interpreter)
|
||||
{
|
||||
Data::pointer data=interpreter->getFloatData();
|
||||
if(data->isFloat())
|
||||
{
|
||||
interpreter->pushStack( data );
|
||||
return(true);
|
||||
}
|
||||
return(false);
|
||||
}
|
||||
};
|
||||
|
||||
class Uint160Op : public Operation
|
||||
{
|
||||
public:
|
||||
bool work(Interpreter* interpreter)
|
||||
{
|
||||
Data::pointer data=interpreter->getUint160Data();
|
||||
if(data->isUint160())
|
||||
{
|
||||
interpreter->pushStack( data );
|
||||
return(true);
|
||||
}
|
||||
return(false);
|
||||
}
|
||||
};
|
||||
|
||||
class AddOp : public Operation
|
||||
{
|
||||
public:
|
||||
bool work(Interpreter* interpreter)
|
||||
{
|
||||
Data::pointer data1=interpreter->popStack();
|
||||
Data::pointer data2=interpreter->popStack();
|
||||
if( (data1->isInt32() || data1->isFloat()) &&
|
||||
(data2->isInt32() || data2->isFloat()) )
|
||||
{
|
||||
if(data1->isFloat() || data2->isFloat()) interpreter->pushStack(Data::pointer(new FloatData(data1->getFloat()+data2->getFloat())));
|
||||
else interpreter->pushStack(Data::pointer(new IntData(data1->getInt()+data2->getInt())));
|
||||
return(true);
|
||||
}else
|
||||
{
|
||||
return(false);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class SubOp : public Operation
|
||||
{
|
||||
public:
|
||||
bool work(Interpreter* interpreter)
|
||||
{
|
||||
Data::pointer data1=interpreter->popStack();
|
||||
Data::pointer data2=interpreter->popStack();
|
||||
if( (data1->isInt32() || data1->isFloat()) &&
|
||||
(data2->isInt32() || data2->isFloat()) )
|
||||
{
|
||||
if(data1->isFloat() || data2->isFloat()) interpreter->pushStack(Data::pointer(new FloatData(data1->getFloat()-data2->getFloat())));
|
||||
else interpreter->pushStack(Data::pointer(new IntData(data1->getInt()-data2->getInt())));
|
||||
return(true);
|
||||
}else
|
||||
{
|
||||
return(false);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class MulOp : public Operation
|
||||
{
|
||||
public:
|
||||
bool work(Interpreter* interpreter)
|
||||
{
|
||||
Data::pointer data1=interpreter->popStack();
|
||||
Data::pointer data2=interpreter->popStack();
|
||||
if( (data1->isInt32() || data1->isFloat()) &&
|
||||
(data2->isInt32() || data2->isFloat()) )
|
||||
{
|
||||
if(data1->isFloat() || data2->isFloat()) interpreter->pushStack(Data::pointer(new FloatData(data1->getFloat()*data2->getFloat())));
|
||||
else interpreter->pushStack(Data::pointer(new IntData(data1->getInt()*data2->getInt())));
|
||||
return(true);
|
||||
}else
|
||||
{
|
||||
return(false);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class DivOp : public Operation
|
||||
{
|
||||
public:
|
||||
bool work(Interpreter* interpreter)
|
||||
{
|
||||
Data::pointer data1=interpreter->popStack();
|
||||
Data::pointer data2=interpreter->popStack();
|
||||
if( (data1->isInt32() || data1->isFloat()) &&
|
||||
(data2->isInt32() || data2->isFloat()) )
|
||||
{
|
||||
if(data1->isFloat() || data2->isFloat()) interpreter->pushStack(Data::pointer(new FloatData(data1->getFloat()/data2->getFloat())));
|
||||
else interpreter->pushStack(Data::pointer(new IntData(data1->getInt()/data2->getInt())));
|
||||
return(true);
|
||||
}else
|
||||
{
|
||||
return(false);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class GtrOp : public Operation
|
||||
{
|
||||
public:
|
||||
bool work(Interpreter* interpreter)
|
||||
{
|
||||
Data::pointer data1=interpreter->popStack();
|
||||
Data::pointer data2=interpreter->popStack();
|
||||
if( (data1->isInt32() || data1->isFloat()) &&
|
||||
(data2->isInt32() || data2->isFloat()) )
|
||||
{
|
||||
interpreter->pushStack(Data::pointer(new BoolData(data1->getFloat()>data2->getFloat())));
|
||||
return(true);
|
||||
}else
|
||||
{
|
||||
return(false);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class LessOp : public Operation
|
||||
{
|
||||
public:
|
||||
bool work(Interpreter* interpreter)
|
||||
{
|
||||
Data::pointer data1=interpreter->popStack();
|
||||
Data::pointer data2=interpreter->popStack();
|
||||
if( (data1->isInt32() || data1->isFloat()) &&
|
||||
(data2->isInt32() || data2->isFloat()) )
|
||||
{
|
||||
interpreter->pushStack(Data::pointer(new FloatData(data1->getFloat()<data2->getFloat())));
|
||||
return(true);
|
||||
}else
|
||||
{
|
||||
return(false);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class ModOp : public Operation
|
||||
{
|
||||
public:
|
||||
bool work(Interpreter* interpreter)
|
||||
{
|
||||
Data::pointer data1=interpreter->popStack();
|
||||
Data::pointer data2=interpreter->popStack();
|
||||
if( data1->isInt32() && data2->isInt32() )
|
||||
{
|
||||
interpreter->pushStack(Data::pointer(new IntData(data1->getInt()%data2->getInt())));
|
||||
return(true);
|
||||
}else
|
||||
{
|
||||
return(false);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
class StartBlockOp : public Operation
|
||||
{
|
||||
public:
|
||||
bool work(Interpreter* interpreter)
|
||||
{
|
||||
Data::pointer offset=interpreter->getIntData();
|
||||
return(interpreter->startBlock(offset->getInt()));
|
||||
}
|
||||
};
|
||||
|
||||
class EndBlockOp : public Operation
|
||||
{
|
||||
public:
|
||||
bool work(Interpreter* interpreter)
|
||||
{
|
||||
return(interpreter->endBlock());
|
||||
}
|
||||
};
|
||||
|
||||
class StopOp : public Operation
|
||||
{
|
||||
public:
|
||||
bool work(Interpreter* interpreter)
|
||||
{
|
||||
interpreter->stop();
|
||||
return(true);
|
||||
}
|
||||
};
|
||||
|
||||
class AcceptDataOp : public Operation
|
||||
{
|
||||
public:
|
||||
bool work(Interpreter* interpreter)
|
||||
{
|
||||
Data::pointer data=interpreter->popStack();
|
||||
if(data->isInt32())
|
||||
{
|
||||
interpreter->pushStack( interpreter->getAcceptData(data->getInt()) );
|
||||
return(true);
|
||||
}
|
||||
return(false);
|
||||
}
|
||||
};
|
||||
|
||||
class JumpIfOp : public Operation
|
||||
{
|
||||
public:
|
||||
bool work(Interpreter* interpreter)
|
||||
{
|
||||
Data::pointer offset=interpreter->getIntData();
|
||||
Data::pointer cond=interpreter->popStack();
|
||||
if(cond->isBool() && offset->isInt32())
|
||||
{
|
||||
if(cond->isTrue())
|
||||
{
|
||||
return(interpreter->jumpTo(offset->getInt()));
|
||||
}
|
||||
return(true);
|
||||
}
|
||||
return(false);
|
||||
}
|
||||
};
|
||||
|
||||
class JumpOp : public Operation
|
||||
{
|
||||
public:
|
||||
bool work(Interpreter* interpreter)
|
||||
{
|
||||
Data::pointer offset=interpreter->getIntData();
|
||||
if(offset->isInt32())
|
||||
{
|
||||
return(interpreter->jumpTo(offset->getInt()));
|
||||
}
|
||||
return(false);
|
||||
}
|
||||
};
|
||||
|
||||
class SendXRPOp : public Operation
|
||||
{
|
||||
public:
|
||||
bool work(Interpreter* interpreter)
|
||||
{
|
||||
Data::pointer sourceID=interpreter->popStack();
|
||||
Data::pointer destID=interpreter->popStack();
|
||||
Data::pointer amount=interpreter->popStack();
|
||||
if(sourceID->isUint160() && destID->isUint160() && amount->isInt32() && interpreter->canSign(sourceID->getUint160()))
|
||||
{
|
||||
// make sure:
|
||||
// source is either, this contract, issuer, or acceptor
|
||||
|
||||
// TODO do the send
|
||||
//interpreter->pushStack( send result);
|
||||
|
||||
return(true);
|
||||
}
|
||||
|
||||
return(false);
|
||||
}
|
||||
};
|
||||
|
||||
class GetDataOp : public Operation
|
||||
{
|
||||
public:
|
||||
bool work(Interpreter* interpreter)
|
||||
{
|
||||
Data::pointer index=interpreter->popStack();
|
||||
if(index->isInt32())
|
||||
{
|
||||
interpreter->pushStack( interpreter->getContractData(index->getInt()));
|
||||
return(true);
|
||||
}
|
||||
|
||||
return(false);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
23
src/cpp/ripple/OrderBook.cpp
Normal file
23
src/cpp/ripple/OrderBook.cpp
Normal file
@@ -0,0 +1,23 @@
|
||||
#include "OrderBook.h"
|
||||
#include "Ledger.h"
|
||||
|
||||
OrderBook::pointer OrderBook::newOrderBook(SerializedLedgerEntry::pointer ledgerEntry)
|
||||
{
|
||||
if(ledgerEntry->getType() != ltOFFER) return( OrderBook::pointer());
|
||||
|
||||
return( OrderBook::pointer(new OrderBook(ledgerEntry)));
|
||||
}
|
||||
|
||||
OrderBook::OrderBook(SerializedLedgerEntry::pointer ledgerEntry)
|
||||
{
|
||||
const STAmount saTakerGets = ledgerEntry->getFieldAmount(sfTakerGets);
|
||||
const STAmount saTakerPays = ledgerEntry->getFieldAmount(sfTakerPays);
|
||||
|
||||
mCurrencyIn = saTakerGets.getCurrency();
|
||||
mCurrencyOut = saTakerPays.getCurrency();
|
||||
mIssuerIn = saTakerGets.getIssuer();
|
||||
mIssuerOut = saTakerPays.getIssuer();
|
||||
|
||||
mBookBase=Ledger::getBookBase(mCurrencyOut,mIssuerOut,mCurrencyIn,mIssuerIn);
|
||||
}
|
||||
// vim:ts=4
|
||||
34
src/cpp/ripple/OrderBook.h
Normal file
34
src/cpp/ripple/OrderBook.h
Normal file
@@ -0,0 +1,34 @@
|
||||
#include "SerializedLedger.h"
|
||||
#include <boost/shared_ptr.hpp>
|
||||
/*
|
||||
Encapsulates the SLE for an orderbook
|
||||
*/
|
||||
class OrderBook
|
||||
{
|
||||
uint256 mBookBase;
|
||||
|
||||
uint160 mCurrencyIn;
|
||||
uint160 mCurrencyOut;
|
||||
uint160 mIssuerIn;
|
||||
uint160 mIssuerOut;
|
||||
|
||||
//SerializedLedgerEntry::pointer mLedgerEntry;
|
||||
OrderBook(SerializedLedgerEntry::pointer ledgerEntry); // For accounts in a ledger
|
||||
public:
|
||||
typedef boost::shared_ptr<OrderBook> pointer;
|
||||
|
||||
// returns NULL if ledgerEntry doesn't point to an order
|
||||
// if ledgerEntry is an Order it creates the OrderBook this order would live in
|
||||
static OrderBook::pointer newOrderBook(SerializedLedgerEntry::pointer ledgerEntry);
|
||||
|
||||
uint256& getBookBase(){ return(mBookBase); }
|
||||
uint160& getCurrencyIn(){ return(mCurrencyIn); }
|
||||
uint160& getCurrencyOut(){ return(mCurrencyOut); }
|
||||
uint160& getIssuerIn(){ return(mIssuerIn); }
|
||||
uint160& getIssuerOut(){ return(mIssuerOut); }
|
||||
|
||||
// looks through the best offers to see how much it would cost to take the given amount
|
||||
STAmount& getTakePrice(STAmount& takeAmount);
|
||||
|
||||
|
||||
};
|
||||
56
src/cpp/ripple/OrderBookDB.cpp
Normal file
56
src/cpp/ripple/OrderBookDB.cpp
Normal file
@@ -0,0 +1,56 @@
|
||||
#include "OrderBookDB.h"
|
||||
#include "Log.h"
|
||||
#include <boost/foreach.hpp>
|
||||
|
||||
|
||||
// TODO: this would be way faster if we could just look under the order dirs
|
||||
OrderBookDB::OrderBookDB(Ledger::pointer ledger)
|
||||
{
|
||||
// walk through the entire ledger looking for orderbook entries
|
||||
uint256 currentIndex=ledger->getFirstLedgerIndex();
|
||||
while(currentIndex.isNonZero())
|
||||
{
|
||||
SLE::pointer entry=ledger->getSLE(currentIndex);
|
||||
|
||||
OrderBook::pointer book=OrderBook::newOrderBook(entry);
|
||||
if(book)
|
||||
{
|
||||
if( mKnownMap.find(book->getBookBase()) != mKnownMap.end() )
|
||||
{
|
||||
mKnownMap[book->getBookBase()]=true;
|
||||
|
||||
if(!book->getCurrencyIn())
|
||||
{ // XRP
|
||||
mXRPOrders.push_back(book);
|
||||
}else
|
||||
{
|
||||
mIssuerMap[book->getIssuerIn()].push_back(book);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
currentIndex=ledger->getNextLedgerIndex(currentIndex);
|
||||
}
|
||||
}
|
||||
|
||||
// return list of all orderbooks that want IssuerID
|
||||
std::vector<OrderBook::pointer>& OrderBookDB::getBooks(const uint160& issuerID)
|
||||
{
|
||||
if( mIssuerMap.find(issuerID) == mIssuerMap.end() ) return mEmptyVector;
|
||||
else return( mIssuerMap[issuerID]);
|
||||
}
|
||||
|
||||
// return list of all orderbooks that want this issuerID and currencyID
|
||||
void OrderBookDB::getBooks(const uint160& issuerID, const uint160& currencyID, std::vector<OrderBook::pointer>& bookRet)
|
||||
{
|
||||
if( mIssuerMap.find(issuerID) == mIssuerMap.end() )
|
||||
{
|
||||
BOOST_FOREACH(OrderBook::pointer book, mIssuerMap[issuerID])
|
||||
{
|
||||
if(book->getCurrencyIn()==currencyID)
|
||||
{
|
||||
bookRet.push_back(book);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
30
src/cpp/ripple/OrderBookDB.h
Normal file
30
src/cpp/ripple/OrderBookDB.h
Normal file
@@ -0,0 +1,30 @@
|
||||
#include "Ledger.h"
|
||||
#include "OrderBook.h"
|
||||
|
||||
/*
|
||||
we can eventually make this cached and just update it as transactions come in.
|
||||
But for now it is probably faster to just generate it each time
|
||||
*/
|
||||
|
||||
class OrderBookDB
|
||||
{
|
||||
std::vector<OrderBook::pointer> mEmptyVector;
|
||||
std::vector<OrderBook::pointer> mXRPOrders;
|
||||
std::map<uint160, std::vector<OrderBook::pointer> > mIssuerMap;
|
||||
|
||||
std::map<uint256, bool > mKnownMap;
|
||||
|
||||
public:
|
||||
OrderBookDB(Ledger::pointer ledger);
|
||||
|
||||
// return list of all orderbooks that want XRP
|
||||
std::vector<OrderBook::pointer>& getXRPInBooks(){ return mXRPOrders; }
|
||||
// return list of all orderbooks that want IssuerID
|
||||
std::vector<OrderBook::pointer>& getBooks(const uint160& issuerID);
|
||||
// return list of all orderbooks that want this issuerID and currencyID
|
||||
void getBooks(const uint160& issuerID, const uint160& currencyID, std::vector<OrderBook::pointer>& bookRet);
|
||||
|
||||
// returns the best rate we can find
|
||||
float getPrice(uint160& currencyIn,uint160& currencyOut);
|
||||
|
||||
};
|
||||
53
src/cpp/ripple/PackedMessage.cpp
Normal file
53
src/cpp/ripple/PackedMessage.cpp
Normal file
@@ -0,0 +1,53 @@
|
||||
#include "PackedMessage.h"
|
||||
|
||||
|
||||
void PackedMessage::encodeHeader(unsigned size, int type)
|
||||
{
|
||||
assert(mBuffer.size() >= HEADER_SIZE);
|
||||
mBuffer[0] = static_cast<boost::uint8_t>((size >> 24) & 0xFF);
|
||||
mBuffer[1] = static_cast<boost::uint8_t>((size >> 16) & 0xFF);
|
||||
mBuffer[2] = static_cast<boost::uint8_t>((size >> 8) & 0xFF);
|
||||
mBuffer[3] = static_cast<boost::uint8_t>(size & 0xFF);
|
||||
mBuffer[4] = static_cast<boost::uint8_t>((type >> 8) & 0xFF);
|
||||
mBuffer[5] = static_cast<boost::uint8_t>(type & 0xFF);
|
||||
}
|
||||
|
||||
PackedMessage::PackedMessage(const ::google::protobuf::Message &message, int type)
|
||||
{
|
||||
unsigned msg_size = message.ByteSize();
|
||||
assert(msg_size);
|
||||
mBuffer.resize(HEADER_SIZE + msg_size);
|
||||
encodeHeader(msg_size, type);
|
||||
if (msg_size)
|
||||
{
|
||||
message.SerializeToArray(&mBuffer[HEADER_SIZE], msg_size);
|
||||
#ifdef DEBUG
|
||||
// std::cerr << "PackedMessage: type=" << type << ", datalen=" << msg_size << std::endl;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
bool PackedMessage::operator == (const PackedMessage& other)
|
||||
{
|
||||
return (mBuffer == other.mBuffer);
|
||||
}
|
||||
|
||||
unsigned PackedMessage::getLength(std::vector<uint8_t>& buf)
|
||||
{
|
||||
if(buf.size() < HEADER_SIZE)
|
||||
return 0;
|
||||
|
||||
int ret = buf[0];
|
||||
ret <<= 8; ret |= buf[1]; ret <<= 8; ret |= buf[2]; ret <<= 8; ret |= buf[3];
|
||||
return ret;
|
||||
}
|
||||
|
||||
int PackedMessage::getType(std::vector<uint8_t>& buf)
|
||||
{
|
||||
if(buf.size() < HEADER_SIZE)
|
||||
return 0;
|
||||
|
||||
int ret = buf[4];
|
||||
ret <<= 8; ret |= buf[5];
|
||||
return ret;
|
||||
}
|
||||
76
src/cpp/ripple/PackedMessage.h
Normal file
76
src/cpp/ripple/PackedMessage.h
Normal file
@@ -0,0 +1,76 @@
|
||||
//
|
||||
// packaging of messages into length/type-prepended buffers
|
||||
// ready for transmission.
|
||||
|
||||
#ifndef PACKEDMESSAGE_H
|
||||
#define PACKEDMESSAGE_H
|
||||
|
||||
#include <string>
|
||||
#include <cassert>
|
||||
#include <vector>
|
||||
#include <cstdio>
|
||||
|
||||
#include <boost/shared_ptr.hpp>
|
||||
#include <boost/enable_shared_from_this.hpp>
|
||||
#include <boost/cstdint.hpp>
|
||||
|
||||
#include "ripple.pb.h"
|
||||
|
||||
// The header size for packed messages
|
||||
// len(4)+type(2)
|
||||
const unsigned HEADER_SIZE = 6;
|
||||
|
||||
|
||||
// PackedMessage implements simple "packing" of protocol buffers Messages into
|
||||
// a string prepended by a header specifying the message length.
|
||||
// MessageType should be a Message class generated by the protobuf compiler.
|
||||
//
|
||||
|
||||
class PackedMessage : public boost::enable_shared_from_this<PackedMessage>
|
||||
{
|
||||
|
||||
std::vector<uint8_t> mBuffer;
|
||||
|
||||
// Encodes the size and type into a header at the beginning of buf
|
||||
//
|
||||
void encodeHeader(unsigned size, int type);
|
||||
|
||||
public:
|
||||
typedef boost::shared_ptr< ::google::protobuf::Message > MessagePointer;
|
||||
typedef boost::shared_ptr<PackedMessage> pointer;
|
||||
|
||||
PackedMessage(const ::google::protobuf::Message& message, int type);
|
||||
|
||||
std::vector<uint8_t>& getBuffer() { return(mBuffer); }
|
||||
|
||||
static unsigned getLength(std::vector<uint8_t>& buf);
|
||||
static int getType(std::vector<uint8_t>& buf);
|
||||
|
||||
bool operator == (const PackedMessage& other);
|
||||
|
||||
/*
|
||||
void setMsg(MessagePointer msg, int type);
|
||||
|
||||
MessagePointer getMsg();
|
||||
|
||||
// Pack the message into the given data_buffer. The buffer is resized to
|
||||
// exactly fit the message.
|
||||
// Return false in case of an error, true if successful.
|
||||
//
|
||||
bool pack(data_buffer& buf) const;
|
||||
|
||||
// Given a buffer with the first HEADER_SIZE bytes representing the header,
|
||||
// decode the header and return the message length. Return 0 in case of
|
||||
// an error.
|
||||
//
|
||||
unsigned decodeHeader(const data_buffer& buf) const;
|
||||
|
||||
// Unpack and store a message from the given packed buffer.
|
||||
// Return true if unpacking successful, false otherwise.
|
||||
//
|
||||
bool unpack(const data_buffer& buf);
|
||||
*/
|
||||
};
|
||||
|
||||
#endif /* PACKEDMESSAGE_H */
|
||||
|
||||
120
src/cpp/ripple/ParseSection.cpp
Normal file
120
src/cpp/ripple/ParseSection.cpp
Normal file
@@ -0,0 +1,120 @@
|
||||
#include "ParseSection.h"
|
||||
#include "utils.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <boost/algorithm/string.hpp>
|
||||
#include <boost/foreach.hpp>
|
||||
|
||||
#define SECTION_DEFAULT_NAME ""
|
||||
|
||||
section ParseSection(const std::string& strInput, const bool bTrim)
|
||||
{
|
||||
std::string strData(strInput);
|
||||
std::vector<std::string> vLines;
|
||||
section secResult;
|
||||
|
||||
// Convert DOS format to unix.
|
||||
boost::algorithm::replace_all(strData, "\r\n", "\n");
|
||||
|
||||
// Convert MacOS format to unix.
|
||||
boost::algorithm::replace_all(strData, "\r", "\n");
|
||||
|
||||
boost::algorithm::split(vLines, strData, boost::algorithm::is_any_of("\n"));
|
||||
|
||||
// Set the default section name.
|
||||
std::string strSection = SECTION_DEFAULT_NAME;
|
||||
|
||||
// Initialize the default section.
|
||||
secResult[strSection] = section::mapped_type();
|
||||
|
||||
// Parse each line.
|
||||
BOOST_FOREACH(std::string& strValue, vLines)
|
||||
{
|
||||
if (strValue.empty() || strValue[0] == '#')
|
||||
{
|
||||
// Blank line or comment, do nothing.
|
||||
|
||||
nothing();
|
||||
}
|
||||
else if (strValue[0] == '[' && strValue[strValue.length()-1] == ']') {
|
||||
// New section.
|
||||
|
||||
strSection = strValue.substr(1, strValue.length()-2);
|
||||
secResult[strSection] = section::mapped_type();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Another line for section.
|
||||
if (bTrim)
|
||||
boost::algorithm::trim(strValue);
|
||||
|
||||
if (!strValue.empty())
|
||||
secResult[strSection].push_back(strValue);
|
||||
}
|
||||
}
|
||||
|
||||
return secResult;
|
||||
}
|
||||
|
||||
void sectionEntriesPrint(std::vector<std::string>* vspEntries, const std::string& strSection)
|
||||
{
|
||||
std::cerr << "[" << strSection << "]" << std::endl;
|
||||
|
||||
if (vspEntries)
|
||||
{
|
||||
BOOST_FOREACH(std::string& strValue, *vspEntries)
|
||||
{
|
||||
std::cerr << strValue << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void sectionPrint(section secInput)
|
||||
{
|
||||
BOOST_FOREACH(section::value_type& pairSection, secInput)
|
||||
{
|
||||
sectionEntriesPrint(&pairSection.second, pairSection.first);
|
||||
}
|
||||
}
|
||||
|
||||
section::mapped_type* sectionEntries(section& secSource, const std::string& strSection)
|
||||
{
|
||||
section::iterator it;
|
||||
section::mapped_type* smtResult;
|
||||
|
||||
it = secSource.find(strSection);
|
||||
if (it == secSource.end())
|
||||
{
|
||||
smtResult = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
//section::mapped_type& vecEntries = it->second;
|
||||
|
||||
smtResult = &(it->second);
|
||||
}
|
||||
|
||||
return smtResult;
|
||||
}
|
||||
|
||||
int sectionCount(section& secSource, const std::string& strSection)
|
||||
{
|
||||
section::mapped_type* pmtEntries = sectionEntries(secSource, strSection);
|
||||
|
||||
return pmtEntries ? -1 : pmtEntries->size();
|
||||
}
|
||||
|
||||
bool sectionSingleB(section& secSource, const std::string& strSection, std::string& strValue)
|
||||
{
|
||||
section::mapped_type* pmtEntries = sectionEntries(secSource, strSection);
|
||||
bool bSingle = pmtEntries && 1 == pmtEntries->size();
|
||||
|
||||
if (bSingle)
|
||||
{
|
||||
strValue = (*pmtEntries)[0];
|
||||
}
|
||||
|
||||
return bSingle;
|
||||
}
|
||||
|
||||
// vim:ts=4
|
||||
17
src/cpp/ripple/ParseSection.h
Normal file
17
src/cpp/ripple/ParseSection.h
Normal file
@@ -0,0 +1,17 @@
|
||||
#ifndef _PARSE_SECTION_
|
||||
#define _PARSE_SECTION_
|
||||
|
||||
#include <map>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
typedef std::map<const std::string, std::vector<std::string> > section;
|
||||
|
||||
section ParseSection(const std::string& strInput, const bool bTrim);
|
||||
void sectionPrint(section secInput);
|
||||
void sectionEntriesPrint(std::vector<std::string>* vspEntries, const std::string& strSection);
|
||||
bool sectionSingleB(section& secSource, const std::string& strSection, std::string& strValue);
|
||||
int sectionCount(section& secSource, const std::string& strSection);
|
||||
section::mapped_type* sectionEntries(section& secSource, const std::string& strSection);
|
||||
|
||||
#endif
|
||||
267
src/cpp/ripple/Pathfinder.cpp
Normal file
267
src/cpp/ripple/Pathfinder.cpp
Normal file
@@ -0,0 +1,267 @@
|
||||
#include "Pathfinder.h"
|
||||
#include "Application.h"
|
||||
#include "RippleLines.h"
|
||||
#include "Log.h"
|
||||
#include <boost/foreach.hpp>
|
||||
|
||||
/*
|
||||
JED: V IIII
|
||||
|
||||
we just need to find a succession of the highest quality paths there until we find enough width
|
||||
|
||||
Don't do branching within each path
|
||||
|
||||
We have a list of paths we are working on but how do we compare the ones that are terminating in a different currency?
|
||||
|
||||
Loops
|
||||
|
||||
TODO: what is a good way to come up with multiple paths?
|
||||
Maybe just change the sort criteria?
|
||||
first a low cost one and then a fat short one?
|
||||
|
||||
|
||||
OrderDB:
|
||||
getXRPOffers();
|
||||
|
||||
// return list of all orderbooks that want XRP
|
||||
// return list of all orderbooks that want IssuerID
|
||||
// return list of all orderbooks that want this issuerID and currencyID
|
||||
*/
|
||||
|
||||
/*
|
||||
Test sending to XRP
|
||||
Test XRP to XRP
|
||||
Test offer in middle
|
||||
Test XRP to USD
|
||||
Test USD to EUR
|
||||
*/
|
||||
|
||||
|
||||
// we sort the options by:
|
||||
// cost of path
|
||||
// length of path
|
||||
// width of path
|
||||
// correct currency at the end
|
||||
|
||||
|
||||
|
||||
bool sortPathOptions(PathOption::pointer first, PathOption::pointer second)
|
||||
{
|
||||
if(first->mTotalCost<second->mTotalCost) return(true);
|
||||
if(first->mTotalCost>second->mTotalCost) return(false);
|
||||
|
||||
if(first->mCorrectCurrency && !second->mCorrectCurrency) return(true);
|
||||
if(!first->mCorrectCurrency && second->mCorrectCurrency) return(false);
|
||||
|
||||
if(first->mPath.getElementCount()<second->mPath.getElementCount()) return(true);
|
||||
if(first->mPath.getElementCount()>second->mPath.getElementCount()) return(false);
|
||||
|
||||
if(first->mMinWidth<second->mMinWidth) return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
PathOption::PathOption(uint160& srcAccount,uint160& srcCurrencyID,const uint160& dstCurrencyID)
|
||||
{
|
||||
mCurrentAccount=srcAccount;
|
||||
mCurrencyID=srcCurrencyID;
|
||||
mCorrectCurrency=(srcCurrencyID==dstCurrencyID);
|
||||
mQuality=0;
|
||||
mMinWidth=STAmount(dstCurrencyID,99999,80); // this will get lowered when we convert back to the correct currency
|
||||
}
|
||||
|
||||
PathOption::PathOption(PathOption::pointer other)
|
||||
{
|
||||
// TODO:
|
||||
}
|
||||
|
||||
|
||||
Pathfinder::Pathfinder(RippleAddress& srcAccountID, RippleAddress& dstAccountID, uint160& srcCurrencyID, STAmount dstAmount) :
|
||||
mSrcAccountID(srcAccountID.getAccountID()), mDstAccountID(dstAccountID.getAccountID()), mDstAmount(dstAmount), mSrcCurrencyID(srcCurrencyID), mOrderBook(theApp->getMasterLedger().getCurrentLedger())
|
||||
{
|
||||
mLedger=theApp->getMasterLedger().getCurrentLedger();
|
||||
}
|
||||
|
||||
bool Pathfinder::findPaths(int maxSearchSteps, int maxPay, STPathSet& retPathSet)
|
||||
{
|
||||
if(mLedger) {
|
||||
std::queue<STPath> pqueue;
|
||||
STPathElement ele(mSrcAccountID,
|
||||
mSrcCurrencyID,
|
||||
uint160());
|
||||
STPath path;
|
||||
path.addElement(ele);
|
||||
pqueue.push(path);
|
||||
while(pqueue.size()) {
|
||||
|
||||
STPath path = pqueue.front();
|
||||
pqueue.pop();
|
||||
// get the first path from the queue
|
||||
|
||||
ele = path.mPath.back();
|
||||
// get the last node from the path
|
||||
|
||||
if (ele.mAccountID == mDstAccountID) {
|
||||
path.mPath.erase(path.mPath.begin());
|
||||
path.mPath.erase(path.mPath.begin() + path.mPath.size()-1);
|
||||
if (path.mPath.size() == 0) {
|
||||
continue;
|
||||
}
|
||||
retPathSet.addPath(path);
|
||||
return true;
|
||||
}
|
||||
// found the destination
|
||||
|
||||
if (!ele.mCurrencyID) {
|
||||
BOOST_FOREACH(OrderBook::pointer book,mOrderBook.getXRPInBooks())
|
||||
{
|
||||
//if (!path.hasSeen(line->getAccountIDPeer().getAccountID()))
|
||||
{
|
||||
|
||||
STPath new_path(path);
|
||||
STPathElement new_ele(uint160(), book->getCurrencyOut(), book->getIssuerOut());
|
||||
new_path.mPath.push_back(new_ele);
|
||||
new_path.mCurrencyID = book->getCurrencyOut();
|
||||
new_path.mCurrentAccount = book->getCurrencyOut();
|
||||
|
||||
pqueue.push(new_path);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
RippleLines rippleLines(ele.mAccountID);
|
||||
BOOST_FOREACH(RippleState::pointer line,rippleLines.getLines())
|
||||
{
|
||||
if (!path.hasSeen(line->getAccountIDPeer().getAccountID()))
|
||||
{
|
||||
STPath new_path(path);
|
||||
STPathElement new_ele(line->getAccountIDPeer().getAccountID(),
|
||||
ele.mCurrencyID,
|
||||
uint160());
|
||||
|
||||
new_path.mPath.push_back(new_ele);
|
||||
pqueue.push(new_path);
|
||||
}
|
||||
} // BOOST_FOREACHE
|
||||
|
||||
// every offer that wants the source currency
|
||||
std::vector<OrderBook::pointer> books;
|
||||
mOrderBook.getBooks(path.mCurrentAccount, path.mCurrencyID, books);
|
||||
|
||||
BOOST_FOREACH(OrderBook::pointer book,books)
|
||||
{
|
||||
STPath new_path(path);
|
||||
STPathElement new_ele(uint160(), book->getCurrencyOut(), book->getIssuerOut());
|
||||
|
||||
new_path.mPath.push_back(new_ele);
|
||||
new_path.mCurrentAccount=book->getIssuerOut();
|
||||
new_path.mCurrencyID=book->getCurrencyOut();
|
||||
|
||||
pqueue.push(new_path);
|
||||
|
||||
}
|
||||
|
||||
} // else
|
||||
// enumerate all adjacent nodes, construct a new path and push it into the queue
|
||||
} // While
|
||||
} // if there is a ledger
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Pathfinder::checkComplete(STPathSet& retPathSet)
|
||||
{
|
||||
if(mCompletePaths.size())
|
||||
{ // TODO: look through these and pick the most promising
|
||||
int count=0;
|
||||
BOOST_FOREACH(PathOption::pointer pathOption,mCompletePaths)
|
||||
{
|
||||
retPathSet.addPath(pathOption->mPath);
|
||||
count++;
|
||||
if(count>2) return(true);
|
||||
}
|
||||
return(true);
|
||||
}
|
||||
return(false);
|
||||
}
|
||||
|
||||
|
||||
// get all the options from this accountID
|
||||
// if source is XRP
|
||||
// every offer that wants XRP
|
||||
// else
|
||||
// every ripple line that starts with the source currency
|
||||
// every offer that we can take that wants the source currency
|
||||
|
||||
void Pathfinder::addOptions(PathOption::pointer tail)
|
||||
{
|
||||
if(!tail->mCurrencyID)
|
||||
{ // source XRP
|
||||
BOOST_FOREACH(OrderBook::pointer book,mOrderBook.getXRPInBooks())
|
||||
{
|
||||
PathOption::pointer pathOption(new PathOption(tail));
|
||||
|
||||
STPathElement ele(uint160(), book->getCurrencyOut(), book->getIssuerOut());
|
||||
pathOption->mPath.addElement(ele);
|
||||
|
||||
pathOption->mCurrentAccount=book->getIssuerOut();
|
||||
pathOption->mCurrencyID=book->getCurrencyOut();
|
||||
addPathOption(pathOption);
|
||||
}
|
||||
}else
|
||||
{ // ripple
|
||||
RippleLines rippleLines(tail->mCurrentAccount);
|
||||
BOOST_FOREACH(RippleState::pointer line,rippleLines.getLines())
|
||||
{
|
||||
// TODO: make sure we can move in the correct direction
|
||||
STAmount balance=line->getBalance();
|
||||
if(balance.getCurrency()==tail->mCurrencyID)
|
||||
{ // we have a ripple line from the tail to somewhere else
|
||||
PathOption::pointer pathOption(new PathOption(tail));
|
||||
|
||||
STPathElement ele(line->getAccountIDPeer().getAccountID(), uint160(), uint160());
|
||||
pathOption->mPath.addElement(ele);
|
||||
|
||||
|
||||
pathOption->mCurrentAccount=line->getAccountIDPeer().getAccountID();
|
||||
addPathOption(pathOption);
|
||||
}
|
||||
}
|
||||
|
||||
// every offer that wants the source currency
|
||||
std::vector<OrderBook::pointer> books;
|
||||
mOrderBook.getBooks(tail->mCurrentAccount, tail->mCurrencyID, books);
|
||||
|
||||
BOOST_FOREACH(OrderBook::pointer book,books)
|
||||
{
|
||||
PathOption::pointer pathOption(new PathOption(tail));
|
||||
|
||||
STPathElement ele(uint160(), book->getCurrencyOut(), book->getIssuerOut());
|
||||
pathOption->mPath.addElement(ele);
|
||||
|
||||
pathOption->mCurrentAccount=book->getIssuerOut();
|
||||
pathOption->mCurrencyID=book->getCurrencyOut();
|
||||
addPathOption(pathOption);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Pathfinder::addPathOption(PathOption::pointer pathOption)
|
||||
{
|
||||
if(pathOption->mCurrencyID==mDstAmount.getCurrency())
|
||||
{
|
||||
pathOption->mCorrectCurrency=true;
|
||||
|
||||
if(pathOption->mCurrentAccount==mDstAccountID)
|
||||
{ // this path is complete
|
||||
mCompletePaths.push_back(pathOption);
|
||||
}else mBuildingPaths.push_back(pathOption);
|
||||
}
|
||||
else
|
||||
{
|
||||
pathOption->mCorrectCurrency=false;
|
||||
mBuildingPaths.push_back(pathOption);
|
||||
}
|
||||
}
|
||||
// vim:ts=4
|
||||
52
src/cpp/ripple/Pathfinder.h
Normal file
52
src/cpp/ripple/Pathfinder.h
Normal file
@@ -0,0 +1,52 @@
|
||||
#include "SerializedTypes.h"
|
||||
#include "RippleAddress.h"
|
||||
#include "OrderBookDB.h"
|
||||
#include <boost/shared_ptr.hpp>
|
||||
|
||||
/* this is a very simple implementation. This can be made way better.
|
||||
We are simply flooding from the start. And doing an exhaustive search of all paths under maxSearchSteps. An easy improvement would be to flood from both directions
|
||||
*/
|
||||
class PathOption
|
||||
{
|
||||
public:
|
||||
typedef boost::shared_ptr<PathOption> pointer;
|
||||
|
||||
STPath mPath;
|
||||
bool mCorrectCurrency; // for the sorting
|
||||
uint160 mCurrencyID; // what currency we currently have at the end of the path
|
||||
uint160 mCurrentAccount; // what account is at the end of the path
|
||||
int mTotalCost; // in send currency
|
||||
STAmount mMinWidth; // in dest currency
|
||||
float mQuality;
|
||||
|
||||
PathOption(uint160& srcAccount,uint160& srcCurrencyID,const uint160& dstCurrencyID);
|
||||
PathOption(PathOption::pointer other);
|
||||
};
|
||||
|
||||
class Pathfinder
|
||||
{
|
||||
uint160 mSrcAccountID;
|
||||
uint160 mDstAccountID;
|
||||
STAmount mDstAmount;
|
||||
uint160 mSrcCurrencyID;
|
||||
|
||||
OrderBookDB mOrderBook;
|
||||
Ledger::pointer mLedger;
|
||||
|
||||
std::list<PathOption::pointer> mBuildingPaths;
|
||||
std::list<PathOption::pointer> mCompletePaths;
|
||||
|
||||
void addOptions(PathOption::pointer tail);
|
||||
|
||||
// returns true if any building paths are now complete?
|
||||
bool checkComplete(STPathSet& retPathSet);
|
||||
|
||||
void addPathOption(PathOption::pointer pathOption);
|
||||
|
||||
public:
|
||||
Pathfinder(RippleAddress& srcAccountID, RippleAddress& dstAccountID, uint160& srcCurrencyID, STAmount dstAmount);
|
||||
|
||||
// returns false if there is no path. otherwise fills out retPath
|
||||
bool findPaths(int maxSearchSteps, int maxPay, STPathSet& retPathSet);
|
||||
};
|
||||
// vim:ts=4
|
||||
1560
src/cpp/ripple/Peer.cpp
Normal file
1560
src/cpp/ripple/Peer.cpp
Normal file
File diff suppressed because it is too large
Load Diff
175
src/cpp/ripple/Peer.h
Normal file
175
src/cpp/ripple/Peer.h
Normal file
@@ -0,0 +1,175 @@
|
||||
#ifndef __PEER__
|
||||
#define __PEER__
|
||||
|
||||
#include <bitset>
|
||||
#include <boost/asio.hpp>
|
||||
#include <boost/asio/ssl.hpp>
|
||||
#include <boost/enable_shared_from_this.hpp>
|
||||
#include <boost/make_shared.hpp>
|
||||
#include <boost/shared_ptr.hpp>
|
||||
|
||||
#include "ripple.pb.h"
|
||||
#include "PackedMessage.h"
|
||||
#include "Ledger.h"
|
||||
#include "Transaction.h"
|
||||
#include "InstanceCounter.h"
|
||||
|
||||
enum PeerPunish
|
||||
{
|
||||
PP_INVALID_REQUEST = 1, // The peer sent a request that makes no sense
|
||||
PP_UNKNOWN_REQUEST = 2, // The peer sent a request that might be garbage
|
||||
PP_UNWANTED_DATA = 3, // The peer sent us data we didn't want/need
|
||||
PP_BAD_SIGNATURE = 4, // Object had bad signature
|
||||
};
|
||||
|
||||
typedef std::pair<std::string,int> ipPort;
|
||||
|
||||
DEFINE_INSTANCE(Peer);
|
||||
|
||||
class Peer : public boost::enable_shared_from_this<Peer>, public IS_INSTANCE(Peer)
|
||||
{
|
||||
public:
|
||||
typedef boost::shared_ptr<Peer> pointer;
|
||||
typedef const boost::shared_ptr<Peer>& ref;
|
||||
|
||||
static const int psbGotHello = 0, psbSentHello = 1, psbInMap = 2, psbTrusted = 3;
|
||||
static const int psbNoLedgers = 4, psbNoTransactions = 5, psbDownLevel = 6;
|
||||
|
||||
void handleConnect(const boost::system::error_code& error, boost::asio::ip::tcp::resolver::iterator it);
|
||||
static void sHandleConnect(Peer::ref ptr, const boost::system::error_code& error,
|
||||
boost::asio::ip::tcp::resolver::iterator it)
|
||||
{ ptr->handleConnect(error, it); }
|
||||
|
||||
private:
|
||||
bool mClientConnect; // In process of connecting as client.
|
||||
bool mHelloed; // True, if hello accepted.
|
||||
bool mDetaching; // True, if detaching.
|
||||
RippleAddress mNodePublic; // Node public key of peer.
|
||||
ipPort mIpPort;
|
||||
ipPort mIpPortConnect;
|
||||
uint256 mCookieHash;
|
||||
uint64 mPeerId;
|
||||
|
||||
uint256 mClosedLedgerHash, mPreviousLedgerHash;
|
||||
std::list<uint256> mRecentLedgers;
|
||||
std::list<uint256> mRecentTxSets;
|
||||
|
||||
boost::asio::ssl::stream<boost::asio::ip::tcp::socket> mSocketSsl;
|
||||
|
||||
boost::asio::deadline_timer mVerifyTimer;
|
||||
|
||||
void handleStart(const boost::system::error_code& ecResult);
|
||||
static void sHandleStart(Peer::ref ptr, const boost::system::error_code& ecResult)
|
||||
{ ptr->handleStart(ecResult); }
|
||||
|
||||
void handleVerifyTimer(const boost::system::error_code& ecResult);
|
||||
static void sHandleVerifyTimer(Peer::ref ptr, const boost::system::error_code& ecResult)
|
||||
{ ptr->handleVerifyTimer(ecResult); }
|
||||
|
||||
protected:
|
||||
|
||||
std::vector<uint8_t> mReadbuf;
|
||||
std::list<PackedMessage::pointer> mSendQ;
|
||||
PackedMessage::pointer mSendingPacket;
|
||||
ripple::TMStatusChange mLastStatus;
|
||||
ripple::TMHello mHello;
|
||||
|
||||
Peer(boost::asio::io_service& io_service, boost::asio::ssl::context& ctx, uint64 peerId);
|
||||
|
||||
void handleShutdown(const boost::system::error_code& error) { ; }
|
||||
static void sHandleShutdown(Peer::ref ptr, const boost::system::error_code& error)
|
||||
{ ptr->handleShutdown(error); }
|
||||
|
||||
void handle_write(const boost::system::error_code& error, size_t bytes_transferred);
|
||||
static void sHandle_write(Peer::ref ptr, const boost::system::error_code& error, size_t bytes_transferred)
|
||||
{ ptr->handle_write(error, bytes_transferred); }
|
||||
|
||||
void handle_read_header(const boost::system::error_code& error);
|
||||
static void sHandle_read_header(Peer::ref ptr, const boost::system::error_code& error)
|
||||
{ ptr->handle_read_header(error); }
|
||||
|
||||
void handle_read_body(const boost::system::error_code& error);
|
||||
static void sHandle_read_body(Peer::ref ptr, const boost::system::error_code& error)
|
||||
{ ptr->handle_read_body(error); }
|
||||
|
||||
void processReadBuffer();
|
||||
void start_read_header();
|
||||
void start_read_body(unsigned msg_len);
|
||||
|
||||
void sendPacketForce(const PackedMessage::pointer& packet);
|
||||
|
||||
void sendHello();
|
||||
|
||||
void recvHello(ripple::TMHello& packet);
|
||||
void recvTransaction(ripple::TMTransaction& packet);
|
||||
void recvValidation(const boost::shared_ptr<ripple::TMValidation>& packet);
|
||||
void recvGetValidation(ripple::TMGetValidations& packet);
|
||||
void recvContact(ripple::TMContact& packet);
|
||||
void recvGetContacts(ripple::TMGetContacts& packet);
|
||||
void recvGetPeers(ripple::TMGetPeers& packet);
|
||||
void recvPeers(ripple::TMPeers& packet);
|
||||
void recvGetObjectByHash(ripple::TMGetObjectByHash& packet);
|
||||
void recvPing(ripple::TMPing& packet);
|
||||
void recvErrorMessage(ripple::TMErrorMsg& packet);
|
||||
void recvSearchTransaction(ripple::TMSearchTransaction& packet);
|
||||
void recvGetAccount(ripple::TMGetAccount& packet);
|
||||
void recvAccount(ripple::TMAccount& packet);
|
||||
void recvGetLedger(ripple::TMGetLedger& packet);
|
||||
void recvLedger(ripple::TMLedgerData& packet);
|
||||
void recvStatus(ripple::TMStatusChange& packet);
|
||||
void recvPropose(const boost::shared_ptr<ripple::TMProposeSet>& packet);
|
||||
void recvHaveTxSet(ripple::TMHaveTransactionSet& packet);
|
||||
|
||||
void getSessionCookie(std::string& strDst);
|
||||
|
||||
void addLedger(const uint256& ledger);
|
||||
void addTxSet(const uint256& TxSet);
|
||||
|
||||
public:
|
||||
|
||||
//bool operator == (const Peer& other);
|
||||
|
||||
std::string& getIP() { return mIpPort.first; }
|
||||
int getPort() { return mIpPort.second; }
|
||||
|
||||
void setIpPort(const std::string& strIP, int iPort);
|
||||
|
||||
static pointer create(boost::asio::io_service& io_service, boost::asio::ssl::context& ctx, uint64 id)
|
||||
{
|
||||
return pointer(new Peer(io_service, ctx, id));
|
||||
}
|
||||
|
||||
boost::asio::ssl::stream<boost::asio::ip::tcp::socket>::lowest_layer_type& getSocket()
|
||||
{
|
||||
return mSocketSsl.lowest_layer();
|
||||
}
|
||||
|
||||
void connect(const std::string& strIp, int iPort);
|
||||
void connected(const boost::system::error_code& error);
|
||||
void detach(const char *);
|
||||
bool samePeer(Peer::ref p) { return samePeer(*p); }
|
||||
bool samePeer(const Peer& p) { return this == &p; }
|
||||
|
||||
void sendPacket(const PackedMessage::pointer& packet);
|
||||
void sendLedgerProposal(Ledger::ref ledger);
|
||||
void sendFullLedger(Ledger::ref ledger);
|
||||
void sendGetFullLedger(uint256& hash);
|
||||
void sendGetPeers();
|
||||
|
||||
void punishPeer(PeerPunish pp);
|
||||
static void punishPeer(const boost::weak_ptr<Peer>&, PeerPunish);
|
||||
|
||||
Json::Value getJson();
|
||||
bool isConnected() const { return mHelloed && !mDetaching; }
|
||||
|
||||
uint256 getClosedLedgerHash() const { return mClosedLedgerHash; }
|
||||
bool hasLedger(const uint256& hash) const;
|
||||
bool hasTxSet(const uint256& hash) const;
|
||||
uint64 getPeerId() const { return mPeerId; }
|
||||
|
||||
RippleAddress getNodePublic() const { return mNodePublic; }
|
||||
void cycleStatus() { mPreviousLedgerHash = mClosedLedgerHash; mClosedLedgerHash.zero(); }
|
||||
};
|
||||
|
||||
#endif
|
||||
// vim:ts=4
|
||||
62
src/cpp/ripple/PeerDoor.cpp
Normal file
62
src/cpp/ripple/PeerDoor.cpp
Normal file
@@ -0,0 +1,62 @@
|
||||
|
||||
#include "PeerDoor.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include <boost/bind.hpp>
|
||||
#include <boost/mem_fn.hpp>
|
||||
|
||||
#include "Application.h"
|
||||
#include "Config.h"
|
||||
#include "utils.h"
|
||||
|
||||
using namespace std;
|
||||
using namespace boost::asio::ip;
|
||||
|
||||
// Generate DH for SSL connection.
|
||||
static DH* handleTmpDh(SSL* ssl, int is_export, int iKeyLength)
|
||||
{
|
||||
return 512 == iKeyLength ? theApp->getWallet().getDh512() : theApp->getWallet().getDh1024();
|
||||
}
|
||||
|
||||
PeerDoor::PeerDoor(boost::asio::io_service& io_service) :
|
||||
mAcceptor(io_service, tcp::endpoint(address().from_string(theConfig.PEER_IP), theConfig.PEER_PORT)),
|
||||
mCtx(boost::asio::ssl::context::sslv23)
|
||||
{
|
||||
mCtx.set_options(
|
||||
boost::asio::ssl::context::default_workarounds
|
||||
| boost::asio::ssl::context::no_sslv2
|
||||
| boost::asio::ssl::context::single_dh_use);
|
||||
|
||||
SSL_CTX_set_tmp_dh_callback(mCtx.native_handle(), handleTmpDh);
|
||||
if (1 != SSL_CTX_set_cipher_list(mCtx.native_handle(), theConfig.PEER_SSL_CIPHER_LIST.c_str()))
|
||||
std::runtime_error("Error setting cipher list (no valid ciphers).");
|
||||
|
||||
cerr << "Peer port: " << theConfig.PEER_IP << " " << theConfig.PEER_PORT << endl;
|
||||
|
||||
startListening();
|
||||
}
|
||||
|
||||
void PeerDoor::startListening()
|
||||
{
|
||||
Peer::pointer new_connection = Peer::create(mAcceptor.get_io_service(), mCtx,
|
||||
theApp->getConnectionPool().assignPeerId());
|
||||
|
||||
mAcceptor.async_accept(new_connection->getSocket(),
|
||||
boost::bind(&PeerDoor::handleConnect, this, new_connection,
|
||||
boost::asio::placeholders::error));
|
||||
}
|
||||
|
||||
void PeerDoor::handleConnect(Peer::pointer new_connection,
|
||||
const boost::system::error_code& error)
|
||||
{
|
||||
if (!error)
|
||||
{
|
||||
new_connection->connected(error);
|
||||
}
|
||||
else cout << "Error: " << error;
|
||||
|
||||
startListening();
|
||||
}
|
||||
|
||||
// vim:ts=4
|
||||
31
src/cpp/ripple/PeerDoor.h
Normal file
31
src/cpp/ripple/PeerDoor.h
Normal file
@@ -0,0 +1,31 @@
|
||||
#ifndef __PEERDOOR__
|
||||
#define __PEERDOOR__
|
||||
|
||||
#include <map>
|
||||
#include <set>
|
||||
|
||||
#include <boost/asio.hpp>
|
||||
#include <boost/asio/ssl.hpp>
|
||||
|
||||
#include "Peer.h"
|
||||
|
||||
/*
|
||||
Handles incoming connections from other Peers
|
||||
*/
|
||||
|
||||
class PeerDoor
|
||||
{
|
||||
private:
|
||||
boost::asio::ip::tcp::acceptor mAcceptor;
|
||||
boost::asio::ssl::context mCtx;
|
||||
|
||||
void startListening();
|
||||
void handleConnect(Peer::pointer new_connection, const boost::system::error_code& error);
|
||||
|
||||
public:
|
||||
PeerDoor(boost::asio::io_service& io_service);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
// vim:ts=4
|
||||
84
src/cpp/ripple/PlatRand.cpp
Normal file
84
src/cpp/ripple/PlatRand.cpp
Normal file
@@ -0,0 +1,84 @@
|
||||
|
||||
#ifdef WIN32
|
||||
|
||||
#include <windows.h>
|
||||
#include <wincrypt.h>
|
||||
|
||||
#include <openssl/rand.h>
|
||||
|
||||
bool AddSystemEntropy()
|
||||
{ // Get entropy from the Windows crypto provider
|
||||
RAND_screen(); // this isn't really that safe since it only works for end users not servers
|
||||
|
||||
/* TODO: you need the cryptoAPI installed I think for the below to work. I suppose we should require people to install this to build the windows version
|
||||
char name[512], rand[128];
|
||||
DWORD count = 500;
|
||||
HCRYPTOPROV cryptoHandle;
|
||||
|
||||
if (!CryptGetDefaultProvider(PROV_RSA_FULL, NULL, CRYPT_MACHINE_DEFAULT, name, &count))
|
||||
{
|
||||
#ifdef DEBUG
|
||||
std::cerr << "Unable to get default crypto provider" << std::endl;
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!CryptAcquireContext(&cryptoHandle, NULL, name, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT | CRYPT_SILENT))
|
||||
{
|
||||
#ifdef DEBUG
|
||||
std::cerr << "Unable to acquire crypto provider" << std::endl;
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
if(!CryptGenRandom(cryptoHandle, 128, reinterpret_cast<BYTE*>(rand)))
|
||||
{
|
||||
#ifdef DEBUG
|
||||
std::cerr << "Unable to get entropy from crypto provider" << std::endl;
|
||||
#endif
|
||||
CryptReleaseContext(cryptoHandle, 0);
|
||||
return false;
|
||||
}
|
||||
|
||||
CryptReleaseContext(cryptoHandle, 0);
|
||||
RAND_seed(rand, 128);
|
||||
|
||||
*/
|
||||
return true;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
|
||||
#include <openssl/rand.h>
|
||||
|
||||
bool AddSystemEntropy()
|
||||
{
|
||||
char rand[128];
|
||||
std::ifstream reader;
|
||||
|
||||
reader.open("/dev/urandom", std::ios::in | std::ios::binary);
|
||||
if (!reader.is_open())
|
||||
{
|
||||
#ifdef DEBUG
|
||||
std::cerr << "Unable to open random source" << std::endl;
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
reader.read(rand, 128);
|
||||
|
||||
int bytesRead = reader.gcount();
|
||||
if (bytesRead == 0)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
std::cerr << "Unable to read from random source" << std::endl;
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
RAND_seed(rand, bytesRead);
|
||||
return bytesRead >= 64;
|
||||
}
|
||||
|
||||
#endif
|
||||
71
src/cpp/ripple/PubKeyCache.cpp
Normal file
71
src/cpp/ripple/PubKeyCache.cpp
Normal file
@@ -0,0 +1,71 @@
|
||||
#include "PubKeyCache.h"
|
||||
#include "Application.h"
|
||||
|
||||
CKey::pointer PubKeyCache::locate(const RippleAddress& id)
|
||||
{
|
||||
{ // is it in cache
|
||||
boost::mutex::scoped_lock sl(mLock);
|
||||
std::map<RippleAddress, CKey::pointer>::iterator it(mCache.find(id));
|
||||
if(it!=mCache.end()) return it->second;
|
||||
}
|
||||
|
||||
std::string sql="SELECT * from PubKeys WHERE ID='";
|
||||
sql.append(id.humanAccountID());
|
||||
sql.append("';'");
|
||||
std::vector<unsigned char> data;
|
||||
data.resize(66); // our public keys are actually 33 bytes
|
||||
int pkSize;
|
||||
|
||||
{ // is it in the database
|
||||
ScopedLock sl(theApp->getTxnDB()->getDBLock());
|
||||
Database* db=theApp->getTxnDB()->getDB();
|
||||
if(!db->executeSQL(sql) || !db->startIterRows())
|
||||
return CKey::pointer();
|
||||
pkSize = db->getBinary("PubKey", &(data.front()), data.size());
|
||||
db->endIterRows();
|
||||
}
|
||||
data.resize(pkSize);
|
||||
CKey::pointer ckp(new CKey());
|
||||
if(!ckp->SetPubKey(data))
|
||||
{
|
||||
assert(false); // bad data in DB
|
||||
return CKey::pointer();
|
||||
}
|
||||
|
||||
{ // put it in cache (okay if we race with another retriever)
|
||||
boost::mutex::scoped_lock sl(mLock);
|
||||
mCache.insert(std::make_pair(id, ckp));
|
||||
}
|
||||
return ckp;
|
||||
}
|
||||
|
||||
CKey::pointer PubKeyCache::store(const RippleAddress& id, const CKey::pointer& key)
|
||||
{ // stored if needed, returns cached copy (possibly the original)
|
||||
{
|
||||
boost::mutex::scoped_lock sl(mLock);
|
||||
std::pair<std::map<RippleAddress,CKey::pointer>::iterator, bool> pit(mCache.insert(std::make_pair(id, key)));
|
||||
if(!pit.second) // there was an existing key
|
||||
return pit.first->second;
|
||||
}
|
||||
|
||||
std::vector<unsigned char> pk = key->GetPubKey();
|
||||
std::string encodedPK;
|
||||
theApp->getTxnDB()->getDB()->escape(&(pk.front()), pk.size(), encodedPK);
|
||||
|
||||
std::string sql = "INSERT INTO PubKeys (ID,PubKey) VALUES ('";
|
||||
sql += id.humanAccountID();
|
||||
sql += "',";
|
||||
sql += encodedPK;
|
||||
sql.append(");");
|
||||
|
||||
ScopedLock dbl(theApp->getTxnDB()->getDBLock());
|
||||
theApp->getTxnDB()->getDB()->executeSQL(sql, true);
|
||||
return key;
|
||||
}
|
||||
|
||||
void PubKeyCache::clear()
|
||||
{
|
||||
boost::mutex::scoped_lock sl(mLock);
|
||||
mCache.clear();
|
||||
}
|
||||
// vim:ts=4
|
||||
25
src/cpp/ripple/PubKeyCache.h
Normal file
25
src/cpp/ripple/PubKeyCache.h
Normal file
@@ -0,0 +1,25 @@
|
||||
#ifndef __PUBKEYCACHE__
|
||||
#define __PUBKEYCACHE__
|
||||
|
||||
#include <map>
|
||||
|
||||
#include <boost/thread/mutex.hpp>
|
||||
|
||||
#include "RippleAddress.h"
|
||||
#include "key.h"
|
||||
|
||||
class PubKeyCache
|
||||
{
|
||||
private:
|
||||
boost::mutex mLock;
|
||||
std::map<RippleAddress, CKey::pointer> mCache;
|
||||
|
||||
public:
|
||||
PubKeyCache() { ; }
|
||||
|
||||
CKey::pointer locate(const RippleAddress& id);
|
||||
CKey::pointer store(const RippleAddress& id, const CKey::pointer& key);
|
||||
void clear();
|
||||
};
|
||||
|
||||
#endif
|
||||
39
src/cpp/ripple/RPC.h
Normal file
39
src/cpp/ripple/RPC.h
Normal file
@@ -0,0 +1,39 @@
|
||||
#include <string>
|
||||
#include <map>
|
||||
|
||||
#include "../json/value.h"
|
||||
|
||||
enum http_status_type
|
||||
{
|
||||
ok = 200,
|
||||
created = 201,
|
||||
accepted = 202,
|
||||
no_content = 204,
|
||||
multiple_choices = 300,
|
||||
moved_permanently = 301,
|
||||
moved_temporarily = 302,
|
||||
not_modified = 304,
|
||||
bad_request = 400,
|
||||
unauthorized = 401,
|
||||
forbidden = 403,
|
||||
not_found = 404,
|
||||
internal_server_error = 500,
|
||||
not_implemented = 501,
|
||||
bad_gateway = 502,
|
||||
service_unavailable = 503
|
||||
};
|
||||
|
||||
extern std::string JSONRPCRequest(const std::string& strMethod, const Json::Value& params,
|
||||
const Json::Value& id);
|
||||
|
||||
extern std::string createHTTPPost(const std::string& strMsg,
|
||||
const std::map<std::string, std::string>& mapRequestHeaders);
|
||||
|
||||
extern int ReadHTTP(std::basic_istream<char>& stream,
|
||||
std::map<std::string, std::string>& mapHeadersRet, std::string& strMessageRet);
|
||||
|
||||
extern std::string HTTPReply(int nStatus, const std::string& strMsg);
|
||||
|
||||
extern std::string JSONRPCReply(const Json::Value& result, const Json::Value& error, const Json::Value& id);
|
||||
|
||||
extern Json::Value JSONRPCError(int code, const std::string& message);
|
||||
0
src/cpp/ripple/RPCCommands.cpp
Normal file
0
src/cpp/ripple/RPCCommands.cpp
Normal file
6
src/cpp/ripple/RPCCommands.h
Normal file
6
src/cpp/ripple/RPCCommands.h
Normal file
@@ -0,0 +1,6 @@
|
||||
|
||||
class RPCCommands
|
||||
{
|
||||
public:
|
||||
HttpReply& handleCommand();
|
||||
};
|
||||
56
src/cpp/ripple/RPCDoor.cpp
Normal file
56
src/cpp/ripple/RPCDoor.cpp
Normal file
@@ -0,0 +1,56 @@
|
||||
#include "RPCDoor.h"
|
||||
#include "Application.h"
|
||||
#include "Config.h"
|
||||
#include "Log.h"
|
||||
#include <boost/bind.hpp>
|
||||
#include <iostream>
|
||||
|
||||
using namespace std;
|
||||
using namespace boost::asio::ip;
|
||||
|
||||
RPCDoor::RPCDoor(boost::asio::io_service& io_service) :
|
||||
mAcceptor(io_service, tcp::endpoint(address::from_string(theConfig.RPC_IP), theConfig.RPC_PORT))
|
||||
{
|
||||
Log(lsINFO) << "RPC port: " << theConfig.RPC_IP << " " << theConfig.RPC_PORT << " allow remote: " << theConfig.RPC_ALLOW_REMOTE;
|
||||
startListening();
|
||||
}
|
||||
RPCDoor::~RPCDoor()
|
||||
{
|
||||
Log(lsINFO) << "RPC port: " << theConfig.RPC_IP << " " << theConfig.RPC_PORT << " allow remote: " << theConfig.RPC_ALLOW_REMOTE;
|
||||
}
|
||||
|
||||
void RPCDoor::startListening()
|
||||
{
|
||||
RPCServer::pointer new_connection = RPCServer::create(mAcceptor.get_io_service(), &theApp->getOPs());
|
||||
mAcceptor.set_option(boost::asio::ip::tcp::acceptor::reuse_address(true));
|
||||
|
||||
mAcceptor.async_accept(new_connection->getSocket(),
|
||||
boost::bind(&RPCDoor::handleConnect, this, new_connection,
|
||||
boost::asio::placeholders::error));
|
||||
}
|
||||
|
||||
bool RPCDoor::isClientAllowed(const std::string& ip)
|
||||
{
|
||||
if(theConfig.RPC_ALLOW_REMOTE) return(true);
|
||||
if(ip=="127.0.0.1") return(true);
|
||||
return(false);
|
||||
}
|
||||
|
||||
void RPCDoor::handleConnect(RPCServer::pointer new_connection,
|
||||
const boost::system::error_code& error)
|
||||
{
|
||||
if(!error)
|
||||
{
|
||||
// Restrict callers by IP
|
||||
if(!isClientAllowed(new_connection->getSocket().remote_endpoint().address().to_string()))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
new_connection->connected();
|
||||
}
|
||||
else Log(lsINFO) << "RPCDoor::handleConnect Error: " << error;
|
||||
|
||||
startListening();
|
||||
}
|
||||
// vim:ts=4
|
||||
19
src/cpp/ripple/RPCDoor.h
Normal file
19
src/cpp/ripple/RPCDoor.h
Normal file
@@ -0,0 +1,19 @@
|
||||
#include "RPCServer.h"
|
||||
#include <boost/asio.hpp>
|
||||
|
||||
/*
|
||||
Handles incoming connections from people making RPC Requests
|
||||
*/
|
||||
|
||||
class RPCDoor
|
||||
{
|
||||
boost::asio::ip::tcp::acceptor mAcceptor;
|
||||
void startListening();
|
||||
void handleConnect(RPCServer::pointer new_connection,
|
||||
const boost::system::error_code& error);
|
||||
|
||||
bool isClientAllowed(const std::string& ip);
|
||||
public:
|
||||
RPCDoor(boost::asio::io_service& io_service);
|
||||
~RPCDoor();
|
||||
};
|
||||
1933
src/cpp/ripple/RPCHandler.cpp
Normal file
1933
src/cpp/ripple/RPCHandler.cpp
Normal file
File diff suppressed because it is too large
Load Diff
163
src/cpp/ripple/RPCHandler.h
Normal file
163
src/cpp/ripple/RPCHandler.h
Normal file
@@ -0,0 +1,163 @@
|
||||
#ifndef RPCHANDLER__H
|
||||
#define RPCHANDLER__H
|
||||
|
||||
// used by the RPCServer or WSDoor to carry out these RPC commands
|
||||
class NetworkOPs;
|
||||
|
||||
class RPCHandler
|
||||
{
|
||||
NetworkOPs* mNetOps;
|
||||
|
||||
typedef Json::Value (RPCHandler::*doFuncPtr)(const Json::Value ¶ms);
|
||||
enum {
|
||||
optNetwork = 1, // Need network
|
||||
optCurrent = 2+optNetwork, // Need current ledger
|
||||
optClosed = 4+optNetwork, // Need closed ledger
|
||||
};
|
||||
|
||||
int getParamCount(const Json::Value& params);
|
||||
bool extractString(std::string& param, const Json::Value& params, int index);
|
||||
|
||||
Json::Value getMasterGenerator(const uint256& uLedger, const RippleAddress& naRegularSeed, RippleAddress& naMasterGenerator);
|
||||
Json::Value authorize(const uint256& uLedger, const RippleAddress& naRegularSeed, const RippleAddress& naSrcAccountID,
|
||||
RippleAddress& naAccountPublic, RippleAddress& naAccountPrivate,
|
||||
STAmount& saSrcBalance, const STAmount& saFee, AccountState::pointer& asSrc,
|
||||
const RippleAddress& naVerifyGenerator);
|
||||
Json::Value accounts(const uint256& uLedger, const RippleAddress& naMasterGenerator);
|
||||
|
||||
Json::Value accountFromString(const uint256& uLedger, RippleAddress& naAccount, bool& bIndex, const std::string& strIdent, const int iIndex);
|
||||
|
||||
Json::Value doAcceptLedger(const Json::Value ¶ms);
|
||||
|
||||
Json::Value doAccountInfo(const Json::Value& params);
|
||||
Json::Value doAccountTransactions(const Json::Value& params);
|
||||
Json::Value doConnect(const Json::Value& params);
|
||||
Json::Value doDataDelete(const Json::Value& params);
|
||||
Json::Value doDataFetch(const Json::Value& params);
|
||||
Json::Value doDataStore(const Json::Value& params);
|
||||
Json::Value doGetCounts(const Json::Value& params);
|
||||
Json::Value doLedger(const Json::Value& params);
|
||||
Json::Value doLogRotate(const Json::Value& params);
|
||||
Json::Value doNicknameInfo(const Json::Value& params);
|
||||
|
||||
Json::Value doOwnerInfo(const Json::Value& params);
|
||||
|
||||
Json::Value doProfile(const Json::Value& params);
|
||||
Json::Value doPeers(const Json::Value& params);
|
||||
|
||||
Json::Value doRippleLinesGet(const Json::Value ¶ms);
|
||||
Json::Value doServerInfo(const Json::Value& params);
|
||||
Json::Value doSessionClose(const Json::Value& params);
|
||||
Json::Value doSessionOpen(const Json::Value& params);
|
||||
Json::Value doLogLevel(const Json::Value& params);
|
||||
Json::Value doStop(const Json::Value& params);
|
||||
Json::Value doSubmit(const Json::Value& params);
|
||||
Json::Value doSubmitJson(const Json::Value& jvRequest);
|
||||
Json::Value doTx(const Json::Value& params);
|
||||
Json::Value doTxHistory(const Json::Value& params);
|
||||
|
||||
|
||||
Json::Value doUnlAdd(const Json::Value& params);
|
||||
Json::Value doUnlDelete(const Json::Value& params);
|
||||
Json::Value doUnlFetch(const Json::Value& params);
|
||||
Json::Value doUnlList(const Json::Value& params);
|
||||
Json::Value doUnlLoad(const Json::Value& params);
|
||||
Json::Value doUnlNetwork(const Json::Value& params);
|
||||
Json::Value doUnlReset(const Json::Value& params);
|
||||
Json::Value doUnlScore(const Json::Value& params);
|
||||
|
||||
Json::Value doValidationCreate(const Json::Value& params);
|
||||
Json::Value doValidationSeed(const Json::Value& params);
|
||||
|
||||
Json::Value doWalletAccounts(const Json::Value& params);
|
||||
Json::Value doWalletLock(const Json::Value& params);
|
||||
Json::Value doWalletPropose(const Json::Value& params);
|
||||
Json::Value doWalletSeed(const Json::Value& params);
|
||||
Json::Value doWalletUnlock(const Json::Value& params);
|
||||
Json::Value doWalletVerify(const Json::Value& params);
|
||||
|
||||
Json::Value doLogin(const Json::Value& params);
|
||||
|
||||
Json::Value doLedgerAccept(const Json::Value& params);
|
||||
Json::Value doLedgerClosed(const Json::Value& params);
|
||||
Json::Value doLedgerCurrent(const Json::Value& params);
|
||||
Json::Value doLedgerEntry(const Json::Value& params);
|
||||
Json::Value doTransactionEntry(const Json::Value& params);
|
||||
|
||||
|
||||
void addSubmitPath(Json::Value& txJSON);
|
||||
|
||||
public:
|
||||
|
||||
enum {
|
||||
rpcSUCCESS,
|
||||
|
||||
// Misc failure
|
||||
rpcLOAD_FAILED,
|
||||
rpcNO_PERMISSION,
|
||||
rpcNOT_STANDALONE,
|
||||
|
||||
// Networking
|
||||
rpcNO_CLOSED,
|
||||
rpcNO_CURRENT,
|
||||
rpcNO_NETWORK,
|
||||
|
||||
// Ledger state
|
||||
rpcACT_EXISTS,
|
||||
rpcACT_NOT_FOUND,
|
||||
rpcINSUF_FUNDS,
|
||||
rpcLGR_NOT_FOUND,
|
||||
rpcNICKNAME_MISSING,
|
||||
rpcNO_ACCOUNT,
|
||||
rpcPASSWD_CHANGED,
|
||||
rpcSRC_MISSING,
|
||||
rpcSRC_UNCLAIMED,
|
||||
rpcTXN_NOT_FOUND,
|
||||
rpcWRONG_SEED,
|
||||
|
||||
// Malformed command
|
||||
rpcINVALID_PARAMS,
|
||||
rpcUNKNOWN_COMMAND,
|
||||
|
||||
// Bad parameter
|
||||
rpcACT_MALFORMED,
|
||||
rpcQUALITY_MALFORMED,
|
||||
rpcBAD_SEED,
|
||||
rpcDST_ACT_MALFORMED,
|
||||
rpcDST_ACT_MISSING,
|
||||
rpcDST_AMT_MALFORMED,
|
||||
rpcGETS_ACT_MALFORMED,
|
||||
rpcGETS_AMT_MALFORMED,
|
||||
rpcHOST_IP_MALFORMED,
|
||||
rpcLGR_IDXS_INVALID,
|
||||
rpcLGR_IDX_MALFORMED,
|
||||
rpcNICKNAME_MALFORMED,
|
||||
rpcNICKNAME_PERM,
|
||||
rpcPAYS_ACT_MALFORMED,
|
||||
rpcPAYS_AMT_MALFORMED,
|
||||
rpcPORT_MALFORMED,
|
||||
rpcPUBLIC_MALFORMED,
|
||||
rpcSRC_ACT_MALFORMED,
|
||||
rpcSRC_ACT_MISSING,
|
||||
rpcSRC_AMT_MALFORMED,
|
||||
|
||||
// Internal error (should never happen)
|
||||
rpcINTERNAL, // Generic internal error.
|
||||
rpcFAIL_GEN_DECRPYT,
|
||||
rpcNOT_IMPL,
|
||||
rpcNO_GEN_DECRPYT,
|
||||
};
|
||||
|
||||
enum { GUEST, USER, ADMIN };
|
||||
|
||||
RPCHandler(NetworkOPs* netOps);
|
||||
|
||||
Json::Value doCommand(const std::string& command, Json::Value& params,int role);
|
||||
Json::Value rpcError(int iError);
|
||||
|
||||
Json::Value handleJSONSubmit(const Json::Value& jvRequest);
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
// vim:ts=4
|
||||
191
src/cpp/ripple/RPCServer.cpp
Normal file
191
src/cpp/ripple/RPCServer.cpp
Normal file
@@ -0,0 +1,191 @@
|
||||
#include "RPCServer.h"
|
||||
#include "Log.h"
|
||||
|
||||
#include "HttpsClient.h"
|
||||
#include "RPC.h"
|
||||
#include "utils.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include <boost/bind.hpp>
|
||||
#include <boost/foreach.hpp>
|
||||
#include <boost/lexical_cast.hpp>
|
||||
#include <boost/algorithm/string.hpp>
|
||||
#include <boost/date_time/posix_time/posix_time.hpp>
|
||||
#include <boost/asio/read_until.hpp>
|
||||
|
||||
|
||||
|
||||
#include "../json/reader.h"
|
||||
#include "../json/writer.h"
|
||||
|
||||
SETUP_LOG();
|
||||
|
||||
#ifndef RPC_MAXIMUM_QUERY
|
||||
#define RPC_MAXIMUM_QUERY (1024*1024)
|
||||
#endif
|
||||
|
||||
RPCServer::RPCServer(boost::asio::io_service& io_service , NetworkOPs* nopNetwork)
|
||||
: mNetOps(nopNetwork), mRPCHandler(nopNetwork), mSocket(io_service)
|
||||
{
|
||||
|
||||
mRole = RPCHandler::GUEST;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void RPCServer::connected()
|
||||
{
|
||||
//std::cout << "RPC request" << std::endl;
|
||||
if (mSocket.remote_endpoint().address().to_string()=="127.0.0.1") mRole = RPCHandler::ADMIN;
|
||||
else mRole = RPCHandler::GUEST;
|
||||
|
||||
boost::asio::async_read_until(mSocket, mLineBuffer, "\r\n",
|
||||
boost::bind(&RPCServer::handle_read_line, shared_from_this(), boost::asio::placeholders::error));
|
||||
}
|
||||
|
||||
void RPCServer::handle_read_req(const boost::system::error_code& e)
|
||||
{
|
||||
std::string req;
|
||||
|
||||
if (mLineBuffer.size())
|
||||
{
|
||||
req.assign(boost::asio::buffer_cast<const char*>(mLineBuffer.data()), mLineBuffer.size());
|
||||
mLineBuffer.consume(mLineBuffer.size());
|
||||
}
|
||||
|
||||
req += strCopy(mQueryVec);
|
||||
mReplyStr = handleRequest(req);
|
||||
boost::asio::async_write(mSocket, boost::asio::buffer(mReplyStr),
|
||||
boost::bind(&RPCServer::handle_write, shared_from_this(), boost::asio::placeholders::error));
|
||||
}
|
||||
|
||||
void RPCServer::handle_read_line(const boost::system::error_code& e)
|
||||
{
|
||||
if (e)
|
||||
return;
|
||||
|
||||
HTTPRequestAction action = mHTTPRequest.consume(mLineBuffer);
|
||||
|
||||
if (action == haDO_REQUEST)
|
||||
{ // request with no body
|
||||
cLog(lsWARNING) << "RPC HTTP request with no body";
|
||||
boost::system::error_code ignore_ec;
|
||||
mSocket.shutdown(boost::asio::ip::tcp::socket::shutdown_both, ignore_ec);
|
||||
return;
|
||||
}
|
||||
else if (action == haREAD_LINE)
|
||||
{
|
||||
boost::asio::async_read_until(mSocket, mLineBuffer, "\r\n",
|
||||
boost::bind(&RPCServer::handle_read_line, shared_from_this(),
|
||||
boost::asio::placeholders::error));
|
||||
}
|
||||
else if (action == haREAD_RAW)
|
||||
{
|
||||
int rLen = mHTTPRequest.getDataSize();
|
||||
if ((rLen < 0) || (rLen > RPC_MAXIMUM_QUERY))
|
||||
{
|
||||
cLog(lsWARNING) << "Illegal RPC request length " << rLen;
|
||||
boost::system::error_code ignore_ec;
|
||||
mSocket.shutdown(boost::asio::ip::tcp::socket::shutdown_both, ignore_ec);
|
||||
return;
|
||||
}
|
||||
|
||||
int alreadyHave = mLineBuffer.size();
|
||||
|
||||
if (alreadyHave < rLen)
|
||||
{
|
||||
mQueryVec.resize(rLen - alreadyHave);
|
||||
boost::asio::async_read(mSocket, boost::asio::buffer(mQueryVec),
|
||||
boost::bind(&RPCServer::handle_read_req, shared_from_this(), boost::asio::placeholders::error));
|
||||
cLog(lsTRACE) << "Waiting for completed request: " << rLen;
|
||||
}
|
||||
else
|
||||
{ // we have the whole thing
|
||||
mQueryVec.resize(0);
|
||||
handle_read_req(e);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
boost::system::error_code ignore_ec;
|
||||
mSocket.shutdown(boost::asio::ip::tcp::socket::shutdown_both, ignore_ec);
|
||||
}
|
||||
}
|
||||
|
||||
std::string RPCServer::handleRequest(const std::string& requestStr)
|
||||
{
|
||||
cLog(lsTRACE) << "handleRequest " << requestStr;
|
||||
Json::Value id;
|
||||
|
||||
// Parse request
|
||||
Json::Value valRequest;
|
||||
Json::Reader reader;
|
||||
if (!reader.parse(requestStr, valRequest) || valRequest.isNull() || !valRequest.isObject())
|
||||
return(HTTPReply(400, "unable to parse request"));
|
||||
|
||||
// Parse id now so errors from here on will have the id
|
||||
id = valRequest["id"];
|
||||
|
||||
// Parse method
|
||||
Json::Value valMethod = valRequest["method"];
|
||||
if (valMethod.isNull())
|
||||
return(HTTPReply(400, "null method"));
|
||||
if (!valMethod.isString())
|
||||
return(HTTPReply(400, "method is not string"));
|
||||
std::string strMethod = valMethod.asString();
|
||||
|
||||
// Parse params
|
||||
Json::Value valParams = valRequest["params"];
|
||||
if (valParams.isNull())
|
||||
valParams = Json::Value(Json::arrayValue);
|
||||
else if (!valParams.isArray())
|
||||
return(HTTPReply(400, "params unparseable"));
|
||||
|
||||
cLog(lsTRACE) << valParams;
|
||||
Json::Value result = mRPCHandler.doCommand(strMethod, valParams,mRole);
|
||||
cLog(lsTRACE) << result;
|
||||
|
||||
std::string strReply = JSONRPCReply(result, Json::Value(), id);
|
||||
return HTTPReply(200, strReply);
|
||||
}
|
||||
|
||||
|
||||
#if 0
|
||||
// now, expire, n
|
||||
bool RPCServer::parseAcceptRate(const std::string& sAcceptRate)
|
||||
{
|
||||
if (!sAcceptRate.compare("expire"))
|
||||
0;
|
||||
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
void RPCServer::handle_write(const boost::system::error_code& e)
|
||||
{
|
||||
//std::cout << "async_write complete " << e << std::endl;
|
||||
|
||||
if (!e)
|
||||
{
|
||||
HTTPRequestAction action = mHTTPRequest.requestDone(false);
|
||||
if (action == haCLOSE_CONN)
|
||||
{
|
||||
boost::system::error_code ignored_ec;
|
||||
mSocket.shutdown(boost::asio::ip::tcp::socket::shutdown_both, ignored_ec);
|
||||
}
|
||||
else
|
||||
{
|
||||
boost::asio::async_read_until(mSocket, mLineBuffer, "\r\n",
|
||||
boost::bind(&RPCServer::handle_read_line, shared_from_this(), boost::asio::placeholders::error));
|
||||
}
|
||||
}
|
||||
|
||||
if (e != boost::asio::error::operation_aborted)
|
||||
{
|
||||
//connection_manager_.stop(shared_from_this());
|
||||
}
|
||||
}
|
||||
|
||||
// vim:ts=4
|
||||
67
src/cpp/ripple/RPCServer.h
Normal file
67
src/cpp/ripple/RPCServer.h
Normal file
@@ -0,0 +1,67 @@
|
||||
#ifndef __RPCSERVER__
|
||||
#define __RPCSERVER__
|
||||
|
||||
#include <boost/array.hpp>
|
||||
#include <boost/shared_ptr.hpp>
|
||||
#include <boost/enable_shared_from_this.hpp>
|
||||
#include <boost/asio.hpp>
|
||||
|
||||
#include "../json/value.h"
|
||||
|
||||
#include "HTTPRequest.h"
|
||||
#include "RippleAddress.h"
|
||||
#include "NetworkOPs.h"
|
||||
#include "SerializedLedger.h"
|
||||
#include "RPCHandler.h"
|
||||
|
||||
class RPCServer : public boost::enable_shared_from_this<RPCServer>
|
||||
{
|
||||
public:
|
||||
|
||||
typedef boost::shared_ptr<RPCServer> pointer;
|
||||
|
||||
private:
|
||||
|
||||
|
||||
NetworkOPs* mNetOps;
|
||||
RPCHandler mRPCHandler;
|
||||
|
||||
boost::asio::ip::tcp::socket mSocket;
|
||||
|
||||
boost::asio::streambuf mLineBuffer;
|
||||
std::vector<unsigned char> mQueryVec;
|
||||
std::string mReplyStr;
|
||||
|
||||
HTTPRequest mHTTPRequest;
|
||||
|
||||
|
||||
int mRole;
|
||||
|
||||
RPCServer(boost::asio::io_service& io_service, NetworkOPs* nopNetwork);
|
||||
|
||||
RPCServer(const RPCServer&); // no implementation
|
||||
RPCServer& operator=(const RPCServer&); // no implementation
|
||||
|
||||
void handle_write(const boost::system::error_code& ec);
|
||||
void handle_read_line(const boost::system::error_code& ec);
|
||||
void handle_read_req(const boost::system::error_code& ec);
|
||||
|
||||
std::string handleRequest(const std::string& requestStr);
|
||||
|
||||
public:
|
||||
static pointer create(boost::asio::io_service& io_service, NetworkOPs* mNetOps)
|
||||
{
|
||||
return pointer(new RPCServer(io_service, mNetOps));
|
||||
}
|
||||
|
||||
boost::asio::ip::tcp::socket& getSocket()
|
||||
{
|
||||
return mSocket;
|
||||
}
|
||||
|
||||
void connected();
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
// vim:ts=4
|
||||
145
src/cpp/ripple/RangeSet.cpp
Normal file
145
src/cpp/ripple/RangeSet.cpp
Normal file
@@ -0,0 +1,145 @@
|
||||
|
||||
#include "RangeSet.h"
|
||||
|
||||
#include <boost/test/unit_test.hpp>
|
||||
#include <boost/lexical_cast.hpp>
|
||||
|
||||
#include "Log.h"
|
||||
|
||||
SETUP_LOG();
|
||||
|
||||
inline uint32 min(uint32 x, uint32 y) { return (x < y) ? x : y; }
|
||||
inline uint32 max(uint32 x, uint32 y) { return (x > y) ? x : y; }
|
||||
|
||||
bool RangeSet::hasValue(uint32 v) const
|
||||
{
|
||||
return mRanges.find(v) != mRanges.end();
|
||||
}
|
||||
|
||||
uint32 RangeSet::getFirst() const
|
||||
{
|
||||
const_iterator it = begin();
|
||||
if (it == end())
|
||||
return RangeSetAbsent;
|
||||
return lower(it);
|
||||
}
|
||||
|
||||
uint32 RangeSet::getNext(uint32 v) const
|
||||
{
|
||||
for (const_iterator it = begin(); it != end(); ++it)
|
||||
{
|
||||
if (upper(it) > v)
|
||||
return max(v + 1, lower(it));
|
||||
}
|
||||
return RangeSetAbsent;
|
||||
}
|
||||
|
||||
uint32 RangeSet::getLast() const
|
||||
{
|
||||
const_reverse_iterator it = rbegin();
|
||||
if (it == rend())
|
||||
return RangeSetAbsent;
|
||||
return upper(it);
|
||||
}
|
||||
|
||||
uint32 RangeSet::getPrev(uint32 v) const
|
||||
{
|
||||
for (const_reverse_iterator it = rbegin(); it != rend(); ++it)
|
||||
{
|
||||
if (lower(it) < v)
|
||||
return min(v - 1, upper(it));
|
||||
}
|
||||
return RangeSetAbsent;
|
||||
}
|
||||
|
||||
uint32 RangeSet::prevMissing(uint32 v) const
|
||||
{ // largest number not in the set that is less than the given number
|
||||
cLog(lsTRACE) << "prevMissing(" << v << ") " << toString();
|
||||
for (const_reverse_iterator it = rbegin(); it != rend(); ++it)
|
||||
{
|
||||
if ((upper(it) + 1) < v)
|
||||
return upper(it) + 1;
|
||||
if (lower(it) == 0)
|
||||
return RangeSetAbsent;
|
||||
if ((lower(it) - 1) < v)
|
||||
return lower(it) - 1;
|
||||
}
|
||||
if (v > 0)
|
||||
return v - 1;
|
||||
return RangeSetAbsent;
|
||||
}
|
||||
|
||||
void RangeSet::setValue(uint32 v)
|
||||
{
|
||||
setRange(v, v);
|
||||
}
|
||||
|
||||
void RangeSet::setRange(uint32 minV, uint32 maxV)
|
||||
{
|
||||
mRanges.add(boost::icl::discrete_interval<uint32>(minV, maxV + 1));
|
||||
}
|
||||
|
||||
void RangeSet::clearValue(uint32 v)
|
||||
{
|
||||
clearRange(v, v);
|
||||
}
|
||||
|
||||
void RangeSet::clearRange(uint32 minV, uint32 maxV)
|
||||
{
|
||||
mRanges.erase(boost::icl::discrete_interval<uint32>(minV, maxV + 1));
|
||||
}
|
||||
|
||||
std::string RangeSet::toString() const
|
||||
{
|
||||
std::string ret;
|
||||
for (const_iterator it = begin(); it != end(); ++it)
|
||||
{
|
||||
if (!ret.empty())
|
||||
ret += ",";
|
||||
if (lower(it) == upper(it))
|
||||
ret += boost::lexical_cast<std::string>(lower(it));
|
||||
else
|
||||
ret += boost::lexical_cast<std::string>(lower(it)) + "-"
|
||||
+ boost::lexical_cast<std::string>(upper(it));
|
||||
}
|
||||
if (ret.empty())
|
||||
return "empty";
|
||||
return ret;
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE(RangeSet_suite)
|
||||
|
||||
BOOST_AUTO_TEST_CASE(RangeSet_test)
|
||||
{
|
||||
cLog(lsTRACE) << "RangeSet test begins";
|
||||
|
||||
RangeSet r1, r2;
|
||||
|
||||
if (r1 != r2) BOOST_FAIL("RangeSet fail");
|
||||
|
||||
r1.setValue(1);
|
||||
if (r1 == r2) BOOST_FAIL("RangeSet fail");
|
||||
r2.setRange(1, 1);
|
||||
if (r1 != r2) BOOST_FAIL("RangeSet fail");
|
||||
|
||||
r1.clear();
|
||||
r1.setRange(1,10);
|
||||
r1.clearValue(5);
|
||||
r1.setRange(11, 20);
|
||||
|
||||
r2.clear();
|
||||
r2.setRange(1, 4);
|
||||
r2.setRange(6, 10);
|
||||
r2.setRange(10, 20);
|
||||
if (r1 != r2) BOOST_FAIL("RangeSet fail");
|
||||
if (r1.hasValue(5)) BOOST_FAIL("RangeSet fail");
|
||||
if (!r2.hasValue(9)) BOOST_FAIL("RangeSet fail");
|
||||
|
||||
// TODO: Traverse functions must be tested
|
||||
|
||||
cLog(lsTRACE) << "RangeSet test complete";
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
||||
|
||||
// vim:ts=4
|
||||
89
src/cpp/ripple/RangeSet.h
Normal file
89
src/cpp/ripple/RangeSet.h
Normal file
@@ -0,0 +1,89 @@
|
||||
#ifndef RANGESET__H
|
||||
#define RANGESET__H
|
||||
|
||||
#include <list>
|
||||
#include <string>
|
||||
|
||||
#include <boost/foreach.hpp>
|
||||
#include <boost/icl/interval_set.hpp>
|
||||
|
||||
#include "types.h"
|
||||
|
||||
class RangeSet
|
||||
{
|
||||
|
||||
public:
|
||||
|
||||
typedef boost::icl::interval_set<uint32> iRangeSet;
|
||||
typedef iRangeSet::iterator iterator;
|
||||
typedef iRangeSet::const_iterator const_iterator;
|
||||
typedef iRangeSet::reverse_iterator reverse_iterator;
|
||||
typedef iRangeSet::const_reverse_iterator const_reverse_iterator;
|
||||
static const uint32 RangeSetAbsent = static_cast<uint32>(-1);
|
||||
|
||||
protected:
|
||||
|
||||
iRangeSet mRanges;
|
||||
|
||||
public:
|
||||
|
||||
RangeSet() { ; }
|
||||
|
||||
bool hasValue(uint32) const;
|
||||
uint32 getFirst() const;
|
||||
uint32 getNext(uint32) const;
|
||||
uint32 getLast() const;
|
||||
uint32 getPrev(uint32) const;
|
||||
|
||||
uint32 prevMissing(uint32) const; // largest number not in the set that is less than the given number
|
||||
|
||||
void setValue(uint32);
|
||||
void setRange(uint32, uint32);
|
||||
void clearValue(uint32);
|
||||
void clearRange(uint32, uint32);
|
||||
|
||||
|
||||
void clear() { mRanges.clear(); }
|
||||
|
||||
// iterator stuff
|
||||
iterator begin() { return mRanges.begin(); }
|
||||
iterator end() { return mRanges.end(); }
|
||||
const_iterator begin() const { return mRanges.begin(); }
|
||||
const_iterator end() const { return mRanges.end(); }
|
||||
reverse_iterator rbegin() { return mRanges.rbegin(); }
|
||||
reverse_iterator rend() { return mRanges.rend(); }
|
||||
const_reverse_iterator rbegin() const { return mRanges.rbegin(); }
|
||||
const_reverse_iterator rend() const { return mRanges.rend(); }
|
||||
|
||||
static uint32 lower(const_iterator& it) { return it->lower(); }
|
||||
static uint32 upper(const_iterator& it) { return it->upper() - 1; }
|
||||
static uint32 lower(const_reverse_iterator& it) { return it->lower(); }
|
||||
static uint32 upper(const_reverse_iterator& it) { return it->upper() - 1; }
|
||||
|
||||
|
||||
bool operator!=(const RangeSet& r) const { return mRanges != r.mRanges; }
|
||||
bool operator==(const RangeSet& r) const { return mRanges == r.mRanges; }
|
||||
|
||||
std::string toString() const;
|
||||
};
|
||||
|
||||
inline RangeSet::const_iterator range_begin(const RangeSet& r) { return r.begin(); }
|
||||
inline RangeSet::iterator range_begin(RangeSet& r) { return r.begin(); }
|
||||
inline RangeSet::const_iterator range_end(const RangeSet& r) { return r.end(); }
|
||||
inline RangeSet::iterator range_end(RangeSet& r) { return r.end(); }
|
||||
|
||||
namespace boost
|
||||
{
|
||||
template<> struct range_mutable_iterator<RangeSet>
|
||||
{
|
||||
typedef RangeSet::iterator type;
|
||||
};
|
||||
template<> struct range_const_iterator<RangeSet>
|
||||
{
|
||||
typedef RangeSet::const_iterator type;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
// vim:ts=4
|
||||
884
src/cpp/ripple/RippleAddress.cpp
Normal file
884
src/cpp/ripple/RippleAddress.cpp
Normal file
@@ -0,0 +1,884 @@
|
||||
#include "RippleAddress.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <iostream>
|
||||
|
||||
#include <boost/format.hpp>
|
||||
#include <boost/functional/hash.hpp>
|
||||
#include <boost/test/unit_test.hpp>
|
||||
|
||||
#include <openssl/rand.h>
|
||||
|
||||
#include "key.h"
|
||||
#include "Config.h"
|
||||
#include "BitcoinUtil.h"
|
||||
#include "rfc1751.h"
|
||||
#include "utils.h"
|
||||
#include "Log.h"
|
||||
|
||||
SETUP_LOG();
|
||||
|
||||
|
||||
RippleAddress::RippleAddress()
|
||||
{
|
||||
nVersion = VER_NONE;
|
||||
}
|
||||
|
||||
bool RippleAddress::isValid() const
|
||||
{
|
||||
bool bValid = false;
|
||||
|
||||
if (!vchData.empty())
|
||||
{
|
||||
CKey key;
|
||||
|
||||
switch (nVersion) {
|
||||
case VER_NODE_PUBLIC:
|
||||
bValid = key.SetPubKey(getNodePublic());
|
||||
break;
|
||||
|
||||
case VER_ACCOUNT_PUBLIC:
|
||||
bValid = key.SetPubKey(getAccountPublic());
|
||||
break;
|
||||
|
||||
case VER_ACCOUNT_PRIVATE:
|
||||
bValid = key.SetPrivateKeyU(getAccountPrivate());
|
||||
break;
|
||||
|
||||
default:
|
||||
bValid = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return bValid;
|
||||
}
|
||||
|
||||
void RippleAddress::clear()
|
||||
{
|
||||
nVersion = VER_NONE;
|
||||
vchData.clear();
|
||||
}
|
||||
|
||||
std::string RippleAddress::humanAddressType() const
|
||||
{
|
||||
switch (nVersion)
|
||||
{
|
||||
case VER_NONE: return "VER_NONE";
|
||||
case VER_NODE_PUBLIC: return "VER_NODE_PUBLIC";
|
||||
case VER_NODE_PRIVATE: return "VER_NODE_PRIVATE";
|
||||
case VER_ACCOUNT_ID: return "VER_ACCOUNT_ID";
|
||||
case VER_ACCOUNT_PUBLIC: return "VER_ACCOUNT_PUBLIC";
|
||||
case VER_ACCOUNT_PRIVATE: return "VER_ACCOUNT_PRIVATE";
|
||||
case VER_FAMILY_GENERATOR: return "VER_FAMILY_GENERATOR";
|
||||
case VER_FAMILY_SEED: return "VER_FAMILY_SEED";
|
||||
}
|
||||
|
||||
return "unknown";
|
||||
}
|
||||
|
||||
//
|
||||
// NodePublic
|
||||
//
|
||||
|
||||
RippleAddress RippleAddress::createNodePublic(const RippleAddress& naSeed)
|
||||
{
|
||||
CKey ckSeed(naSeed.getSeed());
|
||||
RippleAddress naNew;
|
||||
|
||||
// YYY Should there be a GetPubKey() equiv that returns a uint256?
|
||||
naNew.setNodePublic(ckSeed.GetPubKey());
|
||||
|
||||
return naNew;
|
||||
}
|
||||
|
||||
RippleAddress RippleAddress::createNodePublic(const std::vector<unsigned char>& vPublic)
|
||||
{
|
||||
RippleAddress naNew;
|
||||
|
||||
naNew.setNodePublic(vPublic);
|
||||
|
||||
return naNew;
|
||||
}
|
||||
|
||||
RippleAddress RippleAddress::createNodePublic(const std::string& strPublic)
|
||||
{
|
||||
RippleAddress naNew;
|
||||
|
||||
naNew.setNodePublic(strPublic);
|
||||
|
||||
return naNew;
|
||||
}
|
||||
|
||||
uint160 RippleAddress::getNodeID() const
|
||||
{
|
||||
switch (nVersion) {
|
||||
case VER_NONE:
|
||||
throw std::runtime_error("unset source");
|
||||
|
||||
case VER_NODE_PUBLIC:
|
||||
// Note, we are encoding the left.
|
||||
return Hash160(vchData);
|
||||
|
||||
default:
|
||||
throw std::runtime_error(str(boost::format("bad source: %d") % int(nVersion)));
|
||||
}
|
||||
}
|
||||
const std::vector<unsigned char>& RippleAddress::getNodePublic() const
|
||||
{
|
||||
switch (nVersion) {
|
||||
case VER_NONE:
|
||||
throw std::runtime_error("unset source");
|
||||
|
||||
case VER_NODE_PUBLIC:
|
||||
return vchData;
|
||||
|
||||
default:
|
||||
throw std::runtime_error(str(boost::format("bad source: %d") % int(nVersion)));
|
||||
}
|
||||
}
|
||||
|
||||
std::string RippleAddress::humanNodePublic() const
|
||||
{
|
||||
switch (nVersion) {
|
||||
case VER_NONE:
|
||||
throw std::runtime_error("unset source");
|
||||
|
||||
case VER_NODE_PUBLIC:
|
||||
return ToString();
|
||||
|
||||
default:
|
||||
throw std::runtime_error(str(boost::format("bad source: %d") % int(nVersion)));
|
||||
}
|
||||
}
|
||||
|
||||
bool RippleAddress::setNodePublic(const std::string& strPublic)
|
||||
{
|
||||
return SetString(strPublic.c_str(), VER_NODE_PUBLIC);
|
||||
}
|
||||
|
||||
void RippleAddress::setNodePublic(const std::vector<unsigned char>& vPublic)
|
||||
{
|
||||
SetData(VER_NODE_PUBLIC, vPublic);
|
||||
}
|
||||
|
||||
bool RippleAddress::verifyNodePublic(const uint256& hash, const std::vector<unsigned char>& vchSig) const
|
||||
{
|
||||
CKey pubkey = CKey();
|
||||
bool bVerified;
|
||||
|
||||
if (!pubkey.SetPubKey(getNodePublic()))
|
||||
{
|
||||
// Failed to set public key.
|
||||
bVerified = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
bVerified = pubkey.Verify(hash, vchSig);
|
||||
}
|
||||
|
||||
return bVerified;
|
||||
}
|
||||
|
||||
bool RippleAddress::verifyNodePublic(const uint256& hash, const std::string& strSig) const
|
||||
{
|
||||
std::vector<unsigned char> vchSig(strSig.begin(), strSig.end());
|
||||
|
||||
return verifyNodePublic(hash, vchSig);
|
||||
}
|
||||
|
||||
//
|
||||
// NodePrivate
|
||||
//
|
||||
|
||||
RippleAddress RippleAddress::createNodePrivate(const RippleAddress& naSeed)
|
||||
{
|
||||
uint256 uPrivKey;
|
||||
RippleAddress naNew;
|
||||
CKey ckSeed(naSeed.getSeed());
|
||||
|
||||
ckSeed.GetPrivateKeyU(uPrivKey);
|
||||
|
||||
naNew.setNodePrivate(uPrivKey);
|
||||
|
||||
return naNew;
|
||||
}
|
||||
|
||||
const std::vector<unsigned char>& RippleAddress::getNodePrivateData() const
|
||||
{
|
||||
switch (nVersion) {
|
||||
case VER_NONE:
|
||||
throw std::runtime_error("unset source");
|
||||
|
||||
case VER_NODE_PRIVATE:
|
||||
return vchData;
|
||||
|
||||
default:
|
||||
throw std::runtime_error(str(boost::format("bad source: %d") % int(nVersion)));
|
||||
}
|
||||
}
|
||||
|
||||
uint256 RippleAddress::getNodePrivate() const
|
||||
{
|
||||
switch (nVersion) {
|
||||
case VER_NONE:
|
||||
throw std::runtime_error("unset source");
|
||||
|
||||
case VER_NODE_PRIVATE:
|
||||
return uint256(vchData);
|
||||
|
||||
default:
|
||||
throw std::runtime_error(str(boost::format("bad source: %d") % int(nVersion)));
|
||||
}
|
||||
}
|
||||
|
||||
std::string RippleAddress::humanNodePrivate() const
|
||||
{
|
||||
switch (nVersion) {
|
||||
case VER_NONE:
|
||||
throw std::runtime_error("unset source");
|
||||
|
||||
case VER_NODE_PRIVATE:
|
||||
return ToString();
|
||||
|
||||
default:
|
||||
throw std::runtime_error(str(boost::format("bad source: %d") % int(nVersion)));
|
||||
}
|
||||
}
|
||||
|
||||
bool RippleAddress::setNodePrivate(const std::string& strPrivate)
|
||||
{
|
||||
return SetString(strPrivate.c_str(), VER_NODE_PRIVATE);
|
||||
}
|
||||
|
||||
void RippleAddress::setNodePrivate(const std::vector<unsigned char>& vPrivate) {
|
||||
SetData(VER_NODE_PRIVATE, vPrivate);
|
||||
}
|
||||
|
||||
void RippleAddress::setNodePrivate(uint256 hash256)
|
||||
{
|
||||
SetData(VER_NODE_PRIVATE, hash256.begin(), 32);
|
||||
}
|
||||
|
||||
void RippleAddress::signNodePrivate(const uint256& hash, std::vector<unsigned char>& vchSig) const
|
||||
{
|
||||
CKey ckPrivKey;
|
||||
|
||||
ckPrivKey.SetPrivateKeyU(getNodePrivate());
|
||||
|
||||
if (!ckPrivKey.Sign(hash, vchSig))
|
||||
throw std::runtime_error("Signing failed.");
|
||||
}
|
||||
|
||||
//
|
||||
// AccountID
|
||||
//
|
||||
|
||||
uint160 RippleAddress::getAccountID() const
|
||||
{
|
||||
switch (nVersion) {
|
||||
case VER_NONE:
|
||||
throw std::runtime_error("unset source");
|
||||
|
||||
case VER_ACCOUNT_ID:
|
||||
return uint160(vchData);
|
||||
|
||||
case VER_ACCOUNT_PUBLIC:
|
||||
// Note, we are encoding the left.
|
||||
return Hash160(vchData);
|
||||
|
||||
default:
|
||||
throw std::runtime_error(str(boost::format("bad source: %d") % int(nVersion)));
|
||||
}
|
||||
}
|
||||
|
||||
std::string RippleAddress::humanAccountID() const
|
||||
{
|
||||
switch (nVersion) {
|
||||
case VER_NONE:
|
||||
throw std::runtime_error("unset source");
|
||||
|
||||
case VER_ACCOUNT_ID:
|
||||
return ToString();
|
||||
|
||||
case VER_ACCOUNT_PUBLIC:
|
||||
{
|
||||
RippleAddress accountID;
|
||||
|
||||
(void) accountID.setAccountID(getAccountID());
|
||||
|
||||
return accountID.ToString();
|
||||
}
|
||||
|
||||
default:
|
||||
throw std::runtime_error(str(boost::format("bad source: %d") % int(nVersion)));
|
||||
}
|
||||
}
|
||||
|
||||
bool RippleAddress::setAccountID(const std::string& strAccountID)
|
||||
{
|
||||
if (strAccountID.empty())
|
||||
{
|
||||
setAccountID(uint160());
|
||||
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return SetString(strAccountID.c_str(), VER_ACCOUNT_ID);
|
||||
}
|
||||
}
|
||||
|
||||
void RippleAddress::setAccountID(const uint160& hash160)
|
||||
{
|
||||
SetData(VER_ACCOUNT_ID, hash160.begin(), 20);
|
||||
}
|
||||
|
||||
//
|
||||
// AccountPublic
|
||||
//
|
||||
|
||||
RippleAddress RippleAddress::createAccountPublic(const RippleAddress& naGenerator, int iSeq)
|
||||
{
|
||||
CKey ckPub(naGenerator, iSeq);
|
||||
RippleAddress naNew;
|
||||
|
||||
naNew.setAccountPublic(ckPub.GetPubKey());
|
||||
|
||||
return naNew;
|
||||
}
|
||||
|
||||
const std::vector<unsigned char>& RippleAddress::getAccountPublic() const
|
||||
{
|
||||
switch (nVersion) {
|
||||
case VER_NONE:
|
||||
throw std::runtime_error("unset source");
|
||||
|
||||
case VER_ACCOUNT_ID:
|
||||
throw std::runtime_error("public not available from account id");
|
||||
break;
|
||||
|
||||
case VER_ACCOUNT_PUBLIC:
|
||||
return vchData;
|
||||
|
||||
default:
|
||||
throw std::runtime_error(str(boost::format("bad source: %d") % int(nVersion)));
|
||||
}
|
||||
}
|
||||
|
||||
std::string RippleAddress::humanAccountPublic() const
|
||||
{
|
||||
switch (nVersion) {
|
||||
case VER_NONE:
|
||||
throw std::runtime_error("unset source");
|
||||
|
||||
case VER_ACCOUNT_ID:
|
||||
throw std::runtime_error("public not available from account id");
|
||||
|
||||
case VER_ACCOUNT_PUBLIC:
|
||||
return ToString();
|
||||
|
||||
default:
|
||||
throw std::runtime_error(str(boost::format("bad source: %d") % int(nVersion)));
|
||||
}
|
||||
}
|
||||
|
||||
bool RippleAddress::setAccountPublic(const std::string& strPublic)
|
||||
{
|
||||
return SetString(strPublic.c_str(), VER_ACCOUNT_PUBLIC);
|
||||
}
|
||||
|
||||
void RippleAddress::setAccountPublic(const std::vector<unsigned char>& vPublic)
|
||||
{
|
||||
SetData(VER_ACCOUNT_PUBLIC, vPublic);
|
||||
}
|
||||
|
||||
void RippleAddress::setAccountPublic(const RippleAddress& generator, int seq)
|
||||
{
|
||||
CKey pubkey = CKey(generator, seq);
|
||||
|
||||
setAccountPublic(pubkey.GetPubKey());
|
||||
}
|
||||
|
||||
bool RippleAddress::accountPublicVerify(const uint256& uHash, const std::vector<unsigned char>& vucSig) const
|
||||
{
|
||||
CKey ckPublic;
|
||||
bool bVerified;
|
||||
|
||||
if (!ckPublic.SetPubKey(getAccountPublic()))
|
||||
{
|
||||
// Bad private key.
|
||||
cLog(lsWARNING) << "accountPublicVerify: Bad private key.";
|
||||
bVerified = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
bVerified = ckPublic.Verify(uHash, vucSig);
|
||||
}
|
||||
|
||||
return bVerified;
|
||||
}
|
||||
|
||||
RippleAddress RippleAddress::createAccountID(const uint160& uiAccountID)
|
||||
{
|
||||
RippleAddress na;
|
||||
|
||||
na.setAccountID(uiAccountID);
|
||||
|
||||
return na;
|
||||
}
|
||||
|
||||
//
|
||||
// AccountPrivate
|
||||
//
|
||||
|
||||
RippleAddress RippleAddress::createAccountPrivate(const RippleAddress& naGenerator, const RippleAddress& naSeed, int iSeq)
|
||||
{
|
||||
RippleAddress naNew;
|
||||
|
||||
naNew.setAccountPrivate(naGenerator, naSeed, iSeq);
|
||||
|
||||
return naNew;
|
||||
}
|
||||
|
||||
uint256 RippleAddress::getAccountPrivate() const
|
||||
{
|
||||
switch (nVersion) {
|
||||
case VER_NONE:
|
||||
throw std::runtime_error("unset source");
|
||||
|
||||
case VER_ACCOUNT_PRIVATE:
|
||||
return uint256(vchData);
|
||||
|
||||
default:
|
||||
throw std::runtime_error(str(boost::format("bad source: %d") % int(nVersion)));
|
||||
}
|
||||
}
|
||||
|
||||
std::string RippleAddress::humanAccountPrivate() const
|
||||
{
|
||||
switch (nVersion) {
|
||||
case VER_NONE:
|
||||
throw std::runtime_error("unset source");
|
||||
|
||||
case VER_ACCOUNT_PRIVATE:
|
||||
return ToString();
|
||||
|
||||
default:
|
||||
throw std::runtime_error(str(boost::format("bad source: %d") % int(nVersion)));
|
||||
}
|
||||
}
|
||||
|
||||
bool RippleAddress::setAccountPrivate(const std::string& strPrivate)
|
||||
{
|
||||
return SetString(strPrivate.c_str(), VER_ACCOUNT_PRIVATE);
|
||||
}
|
||||
|
||||
void RippleAddress::setAccountPrivate(const std::vector<unsigned char>& vPrivate)
|
||||
{
|
||||
SetData(VER_ACCOUNT_PRIVATE, vPrivate);
|
||||
}
|
||||
|
||||
void RippleAddress::setAccountPrivate(uint256 hash256)
|
||||
{
|
||||
SetData(VER_ACCOUNT_PRIVATE, hash256.begin(), 32);
|
||||
}
|
||||
|
||||
void RippleAddress::setAccountPrivate(const RippleAddress& naGenerator, const RippleAddress& naSeed, int seq)
|
||||
{
|
||||
CKey ckPubkey = CKey(naSeed.getSeed());
|
||||
CKey ckPrivkey = CKey(naGenerator, ckPubkey.GetSecretBN(), seq);
|
||||
uint256 uPrivKey;
|
||||
|
||||
ckPrivkey.GetPrivateKeyU(uPrivKey);
|
||||
|
||||
setAccountPrivate(uPrivKey);
|
||||
}
|
||||
|
||||
bool RippleAddress::accountPrivateSign(const uint256& uHash, std::vector<unsigned char>& vucSig) const
|
||||
{
|
||||
CKey ckPrivate;
|
||||
bool bResult;
|
||||
|
||||
if (!ckPrivate.SetPrivateKeyU(getAccountPrivate()))
|
||||
{
|
||||
// Bad private key.
|
||||
cLog(lsWARNING) << "accountPrivateSign: Bad private key.";
|
||||
bResult = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
bResult = ckPrivate.Sign(uHash, vucSig);
|
||||
tLog(!bResult, lsWARNING) << "accountPrivateSign: Signing failed.";
|
||||
}
|
||||
|
||||
return bResult;
|
||||
}
|
||||
|
||||
#if 0
|
||||
bool RippleAddress::accountPrivateVerify(const uint256& uHash, const std::vector<unsigned char>& vucSig) const
|
||||
{
|
||||
CKey ckPrivate;
|
||||
bool bVerified;
|
||||
|
||||
if (!ckPrivate.SetPrivateKeyU(getAccountPrivate()))
|
||||
{
|
||||
// Bad private key.
|
||||
cLog(lsWARNING) << "accountPrivateVerify: Bad private key.";
|
||||
bVerified = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
bVerified = ckPrivate.Verify(uHash, vucSig);
|
||||
}
|
||||
|
||||
return bVerified;
|
||||
}
|
||||
#endif
|
||||
|
||||
std::vector<unsigned char> RippleAddress::accountPrivateEncrypt(const RippleAddress& naPublicTo, const std::vector<unsigned char>& vucPlainText) const
|
||||
{
|
||||
CKey ckPrivate;
|
||||
CKey ckPublic;
|
||||
std::vector<unsigned char> vucCipherText;
|
||||
|
||||
if (!ckPublic.SetPubKey(naPublicTo.getAccountPublic()))
|
||||
{
|
||||
// Bad public key.
|
||||
cLog(lsWARNING) << "accountPrivateEncrypt: Bad public key.";
|
||||
}
|
||||
else if (!ckPrivate.SetPrivateKeyU(getAccountPrivate()))
|
||||
{
|
||||
// Bad private key.
|
||||
cLog(lsWARNING) << "accountPrivateEncrypt: Bad private key.";
|
||||
}
|
||||
else
|
||||
{
|
||||
try
|
||||
{
|
||||
vucCipherText = ckPrivate.encryptECIES(ckPublic, vucPlainText);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
nothing();
|
||||
}
|
||||
}
|
||||
|
||||
return vucCipherText;
|
||||
}
|
||||
|
||||
std::vector<unsigned char> RippleAddress::accountPrivateDecrypt(const RippleAddress& naPublicFrom, const std::vector<unsigned char>& vucCipherText) const
|
||||
{
|
||||
CKey ckPrivate;
|
||||
CKey ckPublic;
|
||||
std::vector<unsigned char> vucPlainText;
|
||||
|
||||
if (!ckPublic.SetPubKey(naPublicFrom.getAccountPublic()))
|
||||
{
|
||||
// Bad public key.
|
||||
cLog(lsWARNING) << "accountPrivateDecrypt: Bad public key.";
|
||||
}
|
||||
else if (!ckPrivate.SetPrivateKeyU(getAccountPrivate()))
|
||||
{
|
||||
// Bad private key.
|
||||
cLog(lsWARNING) << "accountPrivateDecrypt: Bad private key.";
|
||||
}
|
||||
else
|
||||
{
|
||||
try {
|
||||
vucPlainText = ckPrivate.decryptECIES(ckPublic, vucCipherText);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
nothing();
|
||||
}
|
||||
}
|
||||
|
||||
return vucPlainText;
|
||||
}
|
||||
|
||||
//
|
||||
// Generators
|
||||
//
|
||||
|
||||
BIGNUM* RippleAddress::getGeneratorBN() const
|
||||
{ // returns the public generator
|
||||
switch (nVersion) {
|
||||
case VER_NONE:
|
||||
throw std::runtime_error("unset source");
|
||||
|
||||
case VER_FAMILY_GENERATOR:
|
||||
// Do nothing.
|
||||
break;
|
||||
|
||||
default:
|
||||
throw std::runtime_error(str(boost::format("bad source: %d") % int(nVersion)));
|
||||
}
|
||||
|
||||
BIGNUM* ret = BN_bin2bn(&vchData[0], vchData.size(), NULL);
|
||||
assert(ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
const std::vector<unsigned char>& RippleAddress::getGenerator() const
|
||||
{ // returns the public generator
|
||||
switch (nVersion) {
|
||||
case VER_NONE:
|
||||
throw std::runtime_error("unset source");
|
||||
|
||||
case VER_FAMILY_GENERATOR:
|
||||
// Do nothing.
|
||||
return vchData;
|
||||
|
||||
default:
|
||||
throw std::runtime_error(str(boost::format("bad source: %d") % int(nVersion)));
|
||||
}
|
||||
}
|
||||
|
||||
std::string RippleAddress::humanGenerator() const
|
||||
{
|
||||
switch (nVersion) {
|
||||
case VER_NONE:
|
||||
throw std::runtime_error("unset source");
|
||||
|
||||
case VER_FAMILY_GENERATOR:
|
||||
return ToString();
|
||||
|
||||
default:
|
||||
throw std::runtime_error(str(boost::format("bad source: %d") % int(nVersion)));
|
||||
}
|
||||
}
|
||||
|
||||
bool RippleAddress::setGenerator(const std::string& strGenerator)
|
||||
{
|
||||
return SetString(strGenerator.c_str(), VER_FAMILY_GENERATOR);
|
||||
}
|
||||
|
||||
void RippleAddress::setGenerator(const std::vector<unsigned char>& vPublic)
|
||||
{
|
||||
SetData(VER_FAMILY_GENERATOR, vPublic);
|
||||
}
|
||||
|
||||
RippleAddress RippleAddress::createGeneratorPublic(const RippleAddress& naSeed)
|
||||
{
|
||||
CKey ckSeed(naSeed.getSeed());
|
||||
RippleAddress naNew;
|
||||
|
||||
naNew.setGenerator(ckSeed.GetPubKey());
|
||||
|
||||
return naNew;
|
||||
}
|
||||
|
||||
//
|
||||
// Seed
|
||||
//
|
||||
|
||||
uint128 RippleAddress::getSeed() const
|
||||
{
|
||||
switch (nVersion) {
|
||||
case VER_NONE:
|
||||
throw std::runtime_error("unset source");
|
||||
|
||||
case VER_FAMILY_SEED:
|
||||
return uint128(vchData);
|
||||
|
||||
default:
|
||||
throw std::runtime_error(str(boost::format("bad source: %d") % int(nVersion)));
|
||||
}
|
||||
}
|
||||
|
||||
std::string RippleAddress::humanSeed1751() const
|
||||
{
|
||||
switch (nVersion) {
|
||||
case VER_NONE:
|
||||
throw std::runtime_error("unset source");
|
||||
|
||||
case VER_FAMILY_SEED:
|
||||
{
|
||||
std::string strHuman;
|
||||
std::string strLittle;
|
||||
std::string strBig;
|
||||
uint128 uSeed = getSeed();
|
||||
|
||||
strLittle.assign(uSeed.begin(), uSeed.end());
|
||||
|
||||
strBig.assign(strLittle.rbegin(), strLittle.rend());
|
||||
|
||||
key2eng(strHuman, strBig);
|
||||
|
||||
return strHuman;
|
||||
}
|
||||
|
||||
default:
|
||||
throw std::runtime_error(str(boost::format("bad source: %d") % int(nVersion)));
|
||||
}
|
||||
}
|
||||
|
||||
std::string RippleAddress::humanSeed() const
|
||||
{
|
||||
switch (nVersion) {
|
||||
case VER_NONE:
|
||||
throw std::runtime_error("unset source");
|
||||
|
||||
case VER_FAMILY_SEED:
|
||||
return ToString();
|
||||
|
||||
default:
|
||||
throw std::runtime_error(str(boost::format("bad source: %d") % int(nVersion)));
|
||||
}
|
||||
}
|
||||
|
||||
int RippleAddress::setSeed1751(const std::string& strHuman1751)
|
||||
{
|
||||
std::string strKey;
|
||||
int iResult = eng2key(strKey, strHuman1751);
|
||||
|
||||
if (1 == iResult)
|
||||
{
|
||||
std::vector<unsigned char> vchLittle(strKey.rbegin(), strKey.rend());
|
||||
uint128 uSeed(vchLittle);
|
||||
|
||||
setSeed(uSeed);
|
||||
}
|
||||
|
||||
return iResult;
|
||||
}
|
||||
|
||||
bool RippleAddress::setSeed(const std::string& strSeed)
|
||||
{
|
||||
return SetString(strSeed.c_str(), VER_FAMILY_SEED);
|
||||
}
|
||||
|
||||
bool RippleAddress::setSeedGeneric(const std::string& strText)
|
||||
{
|
||||
RippleAddress naTemp;
|
||||
bool bResult = true;
|
||||
|
||||
if (strText.empty()
|
||||
|| naTemp.setAccountID(strText)
|
||||
|| naTemp.setAccountPublic(strText)
|
||||
|| naTemp.setAccountPrivate(strText)
|
||||
|| naTemp.setNodePublic(strText)
|
||||
|| naTemp.setNodePrivate(strText))
|
||||
{
|
||||
bResult = false;
|
||||
}
|
||||
else if (setSeed(strText))
|
||||
{
|
||||
// std::cerr << "Recognized seed." << std::endl;
|
||||
nothing();
|
||||
}
|
||||
else if (1 == setSeed1751(strText))
|
||||
{
|
||||
// std::cerr << "Recognized 1751 seed." << std::endl;
|
||||
nothing();
|
||||
}
|
||||
else
|
||||
{
|
||||
// std::cerr << "Creating seed from pass phrase." << std::endl;
|
||||
setSeed(CKey::PassPhraseToKey(strText));
|
||||
}
|
||||
|
||||
return bResult;
|
||||
}
|
||||
|
||||
void RippleAddress::setSeed(uint128 hash128) {
|
||||
SetData(VER_FAMILY_SEED, hash128.begin(), 16);
|
||||
}
|
||||
|
||||
void RippleAddress::setSeedRandom()
|
||||
{
|
||||
// XXX Maybe we should call MakeNewKey
|
||||
uint128 key;
|
||||
|
||||
RAND_bytes((unsigned char *) &key, sizeof(key));
|
||||
|
||||
RippleAddress::setSeed(key);
|
||||
}
|
||||
|
||||
RippleAddress RippleAddress::createSeedRandom()
|
||||
{
|
||||
RippleAddress naNew;
|
||||
|
||||
naNew.setSeedRandom();
|
||||
|
||||
return naNew;
|
||||
}
|
||||
|
||||
RippleAddress RippleAddress::createSeedGeneric(const std::string& strText)
|
||||
{
|
||||
RippleAddress naNew;
|
||||
|
||||
naNew.setSeedGeneric(strText);
|
||||
|
||||
return naNew;
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE(ripple_address)
|
||||
|
||||
BOOST_AUTO_TEST_CASE( check_crypto )
|
||||
{
|
||||
// Construct a seed.
|
||||
RippleAddress naSeed;
|
||||
|
||||
BOOST_CHECK(naSeed.setSeedGeneric("masterpassphrase"));
|
||||
BOOST_CHECK_MESSAGE(naSeed.humanSeed() == "snoPBrXtMeMyMHUVTgbuqAfg1SUTb", naSeed.humanSeed());
|
||||
|
||||
// Create node public/private key pair
|
||||
RippleAddress naNodePublic = RippleAddress::createNodePublic(naSeed);
|
||||
RippleAddress naNodePrivate = RippleAddress::createNodePrivate(naSeed);
|
||||
|
||||
BOOST_CHECK_MESSAGE(naNodePublic.humanNodePublic() == "n94a1u4jAz288pZLtw6yFWVbi89YamiC6JBXPVUj5zmExe5fTVg9", naNodePublic.humanNodePublic());
|
||||
BOOST_CHECK_MESSAGE(naNodePrivate.humanNodePrivate() == "pnen77YEeUd4fFKG7iycBWcwKpTaeFRkW2WFostaATy1DSupwXe", naNodePrivate.humanNodePrivate());
|
||||
|
||||
// Check node signing.
|
||||
std::vector<unsigned char> vucTextSrc = strCopy("Hello, nurse!");
|
||||
uint256 uHash = Serializer::getSHA512Half(vucTextSrc);
|
||||
std::vector<unsigned char> vucTextSig;
|
||||
|
||||
naNodePrivate.signNodePrivate(uHash, vucTextSig);
|
||||
BOOST_CHECK_MESSAGE(naNodePublic.verifyNodePublic(uHash, vucTextSig), "Verify failed.");
|
||||
|
||||
// Construct a public generator from the seed.
|
||||
RippleAddress naGenerator = RippleAddress::createGeneratorPublic(naSeed);
|
||||
|
||||
BOOST_CHECK_MESSAGE(naGenerator.humanGenerator() == "fhuJKrhSDzV2SkjLn9qbwm5AaRmrxDPfFsHDCP6yfDZWcxDFz4mt", naGenerator.humanGenerator());
|
||||
|
||||
// Create account #0 public/private key pair.
|
||||
RippleAddress naAccountPublic0 = RippleAddress::createAccountPublic(naGenerator, 0);
|
||||
RippleAddress naAccountPrivate0 = RippleAddress::createAccountPrivate(naGenerator, naSeed, 0);
|
||||
|
||||
BOOST_CHECK_MESSAGE(naAccountPublic0.humanAccountID() == "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh", naAccountPublic0.humanAccountID());
|
||||
BOOST_CHECK_MESSAGE(naAccountPublic0.humanAccountPublic() == "aBQG8RQAzjs1eTKFEAQXr2gS4utcDiEC9wmi7pfUPTi27VCahwgw", naAccountPublic0.humanAccountPublic());
|
||||
BOOST_CHECK_MESSAGE(naAccountPrivate0.humanAccountPrivate() == "p9JfM6HHi64m6mvB6v5k7G2b1cXzGmYiCNJf6GHPKvFTWdeRVjh", naAccountPrivate0.humanAccountPrivate());
|
||||
|
||||
// Create account #1 public/private key pair.
|
||||
RippleAddress naAccountPublic1 = RippleAddress::createAccountPublic(naGenerator, 1);
|
||||
RippleAddress naAccountPrivate1 = RippleAddress::createAccountPrivate(naGenerator, naSeed, 1);
|
||||
|
||||
BOOST_CHECK_MESSAGE(naAccountPublic1.humanAccountID() == "r4bYF7SLUMD7QgSLLpgJx38WJSY12ViRjP", naAccountPublic1.humanAccountID());
|
||||
BOOST_CHECK_MESSAGE(naAccountPublic1.humanAccountPublic() == "aBPXpTfuLy1Bhk3HnGTTAqnovpKWQ23NpFMNkAF6F1Atg5vDyPrw", naAccountPublic1.humanAccountPublic());
|
||||
BOOST_CHECK_MESSAGE(naAccountPrivate1.humanAccountPrivate() == "p9JEm822LMrzJii1k7TvdphfENTp6G5jr253Xa5rkzUWVr8ogQt", naAccountPrivate1.humanAccountPrivate());
|
||||
|
||||
// Check account signing.
|
||||
BOOST_CHECK_MESSAGE(naAccountPrivate0.accountPrivateSign(uHash, vucTextSig), "Signing failed.");
|
||||
BOOST_CHECK_MESSAGE(naAccountPublic0.accountPublicVerify(uHash, vucTextSig), "Verify failed.");
|
||||
BOOST_CHECK_MESSAGE(!naAccountPublic1.accountPublicVerify(uHash, vucTextSig), "Anti-verify failed.");
|
||||
|
||||
BOOST_CHECK_MESSAGE(naAccountPrivate1.accountPrivateSign(uHash, vucTextSig), "Signing failed.");
|
||||
BOOST_CHECK_MESSAGE(naAccountPublic1.accountPublicVerify(uHash, vucTextSig), "Verify failed.");
|
||||
BOOST_CHECK_MESSAGE(!naAccountPublic0.accountPublicVerify(uHash, vucTextSig), "Anti-verify failed.");
|
||||
|
||||
// Check account encryption.
|
||||
std::vector<unsigned char> vucTextCipher
|
||||
= naAccountPrivate0.accountPrivateEncrypt(naAccountPublic1, vucTextSrc);
|
||||
std::vector<unsigned char> vucTextRecovered
|
||||
= naAccountPrivate1.accountPrivateDecrypt(naAccountPublic0, vucTextCipher);
|
||||
|
||||
BOOST_CHECK_MESSAGE(vucTextSrc == vucTextRecovered, "Encrypt-decrypt failed.");
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
||||
|
||||
// vim:ts=4
|
||||
187
src/cpp/ripple/RippleAddress.h
Normal file
187
src/cpp/ripple/RippleAddress.h
Normal file
@@ -0,0 +1,187 @@
|
||||
#ifndef __NEWCOIN_ADDRESS__
|
||||
#define __NEWCOIN_ADDRESS__
|
||||
|
||||
#include "base58.h"
|
||||
#include "uint256.h"
|
||||
|
||||
//
|
||||
// Used to hold addresses and parse and produce human formats.
|
||||
//
|
||||
// XXX This needs to be reworked to store data in uint160 and uint256. Conversion to CBase58Data should happen as needed.
|
||||
class RippleAddress : public CBase58Data
|
||||
{
|
||||
private:
|
||||
typedef enum {
|
||||
VER_NONE = 1,
|
||||
VER_NODE_PUBLIC = 28,
|
||||
VER_NODE_PRIVATE = 32,
|
||||
VER_ACCOUNT_ID = 0,
|
||||
VER_ACCOUNT_PUBLIC = 35,
|
||||
VER_ACCOUNT_PRIVATE = 34,
|
||||
VER_FAMILY_GENERATOR = 41,
|
||||
VER_FAMILY_SEED = 33,
|
||||
} VersionEncoding;
|
||||
|
||||
public:
|
||||
RippleAddress();
|
||||
|
||||
// For public and private key, checks if they are legal.
|
||||
bool isValid() const;
|
||||
void clear();
|
||||
|
||||
std::string humanAddressType() const;
|
||||
|
||||
//
|
||||
// Node Public - Also used for Validators
|
||||
//
|
||||
uint160 getNodeID() const;
|
||||
const std::vector<unsigned char>& getNodePublic() const;
|
||||
|
||||
std::string humanNodePublic() const;
|
||||
|
||||
bool setNodePublic(const std::string& strPublic);
|
||||
void setNodePublic(const std::vector<unsigned char>& vPublic);
|
||||
bool verifyNodePublic(const uint256& hash, const std::vector<unsigned char>& vchSig) const;
|
||||
bool verifyNodePublic(const uint256& hash, const std::string& strSig) const;
|
||||
|
||||
static RippleAddress createNodePublic(const RippleAddress& naSeed);
|
||||
static RippleAddress createNodePublic(const std::vector<unsigned char>& vPublic);
|
||||
static RippleAddress createNodePublic(const std::string& strPublic);
|
||||
|
||||
//
|
||||
// Node Private
|
||||
//
|
||||
const std::vector<unsigned char>& getNodePrivateData() const;
|
||||
uint256 getNodePrivate() const;
|
||||
|
||||
std::string humanNodePrivate() const;
|
||||
|
||||
bool setNodePrivate(const std::string& strPrivate);
|
||||
void setNodePrivate(const std::vector<unsigned char>& vPrivate);
|
||||
void setNodePrivate(uint256 hash256);
|
||||
void signNodePrivate(const uint256& hash, std::vector<unsigned char>& vchSig) const;
|
||||
|
||||
static RippleAddress createNodePrivate(const RippleAddress& naSeed);
|
||||
|
||||
//
|
||||
// Accounts IDs
|
||||
//
|
||||
uint160 getAccountID() const;
|
||||
|
||||
std::string humanAccountID() const;
|
||||
|
||||
bool setAccountID(const std::string& strAccountID);
|
||||
void setAccountID(const uint160& hash160In);
|
||||
|
||||
static RippleAddress createAccountID(const std::string& strAccountID)
|
||||
{ RippleAddress na; na.setAccountID(strAccountID); return na; }
|
||||
|
||||
static RippleAddress createAccountID(const uint160& uiAccountID);
|
||||
|
||||
static std::string createHumanAccountID(const uint160& uiAccountID)
|
||||
{ return createAccountID(uiAccountID).humanAccountID(); }
|
||||
|
||||
static std::string createHumanAccountID(const std::vector<unsigned char>& vPrivate)
|
||||
{ return createAccountPrivate(vPrivate).humanAccountID(); }
|
||||
|
||||
//
|
||||
// Accounts Public
|
||||
//
|
||||
const std::vector<unsigned char>& getAccountPublic() const;
|
||||
|
||||
std::string humanAccountPublic() const;
|
||||
|
||||
bool setAccountPublic(const std::string& strPublic);
|
||||
void setAccountPublic(const std::vector<unsigned char>& vPublic);
|
||||
void setAccountPublic(const RippleAddress& generator, int seq);
|
||||
|
||||
bool accountPublicVerify(const uint256& uHash, const std::vector<unsigned char>& vucSig) const;
|
||||
|
||||
static RippleAddress createAccountPublic(const std::vector<unsigned char>& vPublic)
|
||||
{
|
||||
RippleAddress naNew;
|
||||
|
||||
naNew.setAccountPublic(vPublic);
|
||||
|
||||
return naNew;
|
||||
}
|
||||
|
||||
static std::string createHumanAccountPublic(const std::vector<unsigned char>& vPublic) {
|
||||
return createAccountPublic(vPublic).humanAccountPublic();
|
||||
}
|
||||
|
||||
// Create a deterministic public key from a public generator.
|
||||
static RippleAddress createAccountPublic(const RippleAddress& naGenerator, int iSeq);
|
||||
|
||||
//
|
||||
// Accounts Private
|
||||
//
|
||||
uint256 getAccountPrivate() const;
|
||||
|
||||
std::string humanAccountPrivate() const;
|
||||
|
||||
bool setAccountPrivate(const std::string& strPrivate);
|
||||
void setAccountPrivate(const std::vector<unsigned char>& vPrivate);
|
||||
void setAccountPrivate(uint256 hash256);
|
||||
void setAccountPrivate(const RippleAddress& naGenerator, const RippleAddress& naSeed, int seq);
|
||||
|
||||
bool accountPrivateSign(const uint256& uHash, std::vector<unsigned char>& vucSig) const;
|
||||
// bool accountPrivateVerify(const uint256& uHash, const std::vector<unsigned char>& vucSig) const;
|
||||
|
||||
// Encrypt a message.
|
||||
std::vector<unsigned char> accountPrivateEncrypt(const RippleAddress& naPublicTo, const std::vector<unsigned char>& vucPlainText) const;
|
||||
|
||||
// Decrypt a message.
|
||||
std::vector<unsigned char> accountPrivateDecrypt(const RippleAddress& naPublicFrom, const std::vector<unsigned char>& vucCipherText) const;
|
||||
|
||||
static RippleAddress createAccountPrivate(const RippleAddress& naGenerator, const RippleAddress& naSeed, int iSeq);
|
||||
|
||||
static RippleAddress createAccountPrivate(const std::vector<unsigned char>& vPrivate)
|
||||
{
|
||||
RippleAddress naNew;
|
||||
|
||||
naNew.setAccountPrivate(vPrivate);
|
||||
|
||||
return naNew;
|
||||
}
|
||||
|
||||
static std::string createHumanAccountPrivate(const std::vector<unsigned char>& vPrivate) {
|
||||
return createAccountPrivate(vPrivate).humanAccountPrivate();
|
||||
}
|
||||
|
||||
//
|
||||
// Generators
|
||||
// Use to generate a master or regular family.
|
||||
//
|
||||
BIGNUM* getGeneratorBN() const; // DEPRECATED
|
||||
const std::vector<unsigned char>& getGenerator() const;
|
||||
|
||||
std::string humanGenerator() const;
|
||||
|
||||
bool setGenerator(const std::string& strGenerator);
|
||||
void setGenerator(const std::vector<unsigned char>& vPublic);
|
||||
// void setGenerator(const RippleAddress& seed);
|
||||
|
||||
// Create generator for making public deterministic keys.
|
||||
static RippleAddress createGeneratorPublic(const RippleAddress& naSeed);
|
||||
|
||||
//
|
||||
// Seeds
|
||||
// Clients must disallow reconizable entries from being seeds.
|
||||
uint128 getSeed() const;
|
||||
|
||||
std::string humanSeed() const;
|
||||
std::string humanSeed1751() const;
|
||||
|
||||
bool setSeed(const std::string& strSeed);
|
||||
int setSeed1751(const std::string& strHuman1751);
|
||||
bool setSeedGeneric(const std::string& strText);
|
||||
void setSeed(uint128 hash128);
|
||||
void setSeedRandom();
|
||||
|
||||
static RippleAddress createSeedRandom();
|
||||
static RippleAddress createSeedGeneric(const std::string& strText);
|
||||
};
|
||||
|
||||
#endif
|
||||
// vim:ts=4
|
||||
2636
src/cpp/ripple/RippleCalc.cpp
Normal file
2636
src/cpp/ripple/RippleCalc.cpp
Normal file
File diff suppressed because it is too large
Load Diff
194
src/cpp/ripple/RippleCalc.h
Normal file
194
src/cpp/ripple/RippleCalc.h
Normal file
@@ -0,0 +1,194 @@
|
||||
#ifndef __RIPPLE_CALC__
|
||||
#define __RIPPLE_CALC__
|
||||
|
||||
#include <boost/unordered_set.hpp>
|
||||
|
||||
#include "LedgerEntrySet.h"
|
||||
|
||||
class PaymentNode {
|
||||
protected:
|
||||
friend class RippleCalc;
|
||||
friend class PathState;
|
||||
|
||||
uint16 uFlags; // --> From path.
|
||||
|
||||
uint160 uAccountID; // --> Accounts: Recieving/sending account.
|
||||
uint160 uCurrencyID; // --> Accounts: Receive and send, Offers: send.
|
||||
// --- For offer's next has currency out.
|
||||
uint160 uIssuerID; // --> Currency's issuer
|
||||
|
||||
STAmount saTransferRate; // Transfer rate for uIssuerID.
|
||||
|
||||
// Computed by Reverse.
|
||||
STAmount saRevRedeem; // <-- Amount to redeem to next.
|
||||
STAmount saRevIssue; // <-- Amount to issue to next limited by credit and outstanding IOUs.
|
||||
// Issue isn't used by offers.
|
||||
STAmount saRevDeliver; // <-- Amount to deliver to next regardless of fee.
|
||||
|
||||
// Computed by forward.
|
||||
STAmount saFwdRedeem; // <-- Amount node will redeem to next.
|
||||
STAmount saFwdIssue; // <-- Amount node will issue to next.
|
||||
// Issue isn't used by offers.
|
||||
STAmount saFwdDeliver; // <-- Amount to deliver to next regardless of fee.
|
||||
|
||||
// For offers:
|
||||
|
||||
STAmount saRateMax; // XXX Should rate be sticky for forward too?
|
||||
|
||||
// Directory
|
||||
uint256 uDirectTip; // Current directory.
|
||||
uint256 uDirectEnd; // Next order book.
|
||||
bool bDirectAdvance; // Need to advance directory.
|
||||
SLE::pointer sleDirectDir;
|
||||
STAmount saOfrRate; // For correct ratio.
|
||||
|
||||
// Node
|
||||
bool bEntryAdvance; // Need to advance entry.
|
||||
unsigned int uEntry;
|
||||
uint256 uOfferIndex;
|
||||
SLE::pointer sleOffer;
|
||||
uint160 uOfrOwnerID;
|
||||
bool bFundsDirty; // Need to refresh saOfferFunds, saTakerPays, & saTakerGets.
|
||||
STAmount saOfferFunds;
|
||||
STAmount saTakerPays;
|
||||
STAmount saTakerGets;
|
||||
};
|
||||
|
||||
// account id, currency id, issuer id :: node
|
||||
typedef boost::tuple<uint160, uint160, uint160> aciSource;
|
||||
typedef boost::unordered_map<aciSource, unsigned int> curIssuerNode; // Map of currency, issuer to node index.
|
||||
typedef boost::unordered_map<aciSource, unsigned int>::const_iterator curIssuerNodeConstIterator;
|
||||
|
||||
extern std::size_t hash_value(const aciSource& asValue);
|
||||
|
||||
// Holds a path state under incremental application.
|
||||
class PathState
|
||||
{
|
||||
protected:
|
||||
Ledger::ref mLedger;
|
||||
|
||||
TER pushNode(const int iType, const uint160& uAccountID, const uint160& uCurrencyID, const uint160& uIssuerID);
|
||||
TER pushImply(const uint160& uAccountID, const uint160& uCurrencyID, const uint160& uIssuerID);
|
||||
|
||||
public:
|
||||
typedef boost::shared_ptr<PathState> pointer;
|
||||
typedef const boost::shared_ptr<PathState>& ref;
|
||||
|
||||
TER terStatus;
|
||||
std::vector<PaymentNode> vpnNodes;
|
||||
|
||||
// When processing, don't want to complicate directory walking with deletion.
|
||||
std::vector<uint256> vUnfundedBecame; // Offers that became unfunded or were completely consumed.
|
||||
|
||||
// First time scanning foward, as part of path contruction, a funding source was mentioned for accounts. Source may only be
|
||||
// used there.
|
||||
curIssuerNode umForward; // Map of currency, issuer to node index.
|
||||
|
||||
// First time working in reverse a funding source was used.
|
||||
// Source may only be used there if not mentioned by an account.
|
||||
curIssuerNode umReverse; // Map of currency, issuer to node index.
|
||||
|
||||
LedgerEntrySet lesEntries;
|
||||
|
||||
int mIndex; // Index/rank amoung siblings.
|
||||
uint64 uQuality; // 0 = no quality/liquity left.
|
||||
const STAmount& saInReq; // --> Max amount to spend by sender.
|
||||
STAmount saInAct; // --> Amount spent by sender so far.
|
||||
STAmount saInPass; // <-- Amount spent by sender.
|
||||
const STAmount& saOutReq; // --> Amount to send.
|
||||
STAmount saOutAct; // --> Amount actually sent so far.
|
||||
STAmount saOutPass; // <-- Amount actually sent.
|
||||
bool bConsumed; // If true, use consumes full liquidity. False, may or may not.
|
||||
|
||||
PathState(
|
||||
const int iIndex,
|
||||
const LedgerEntrySet& lesSource,
|
||||
const STPath& spSourcePath,
|
||||
const uint160& uReceiverID,
|
||||
const uint160& uSenderID,
|
||||
const STAmount& saSend,
|
||||
const STAmount& saSendMax
|
||||
);
|
||||
|
||||
Json::Value getJson() const;
|
||||
|
||||
static PathState::pointer createPathState(
|
||||
const int iIndex,
|
||||
const LedgerEntrySet& lesSource,
|
||||
const STPath& spSourcePath,
|
||||
const uint160& uReceiverID,
|
||||
const uint160& uSenderID,
|
||||
const STAmount& saSend,
|
||||
const STAmount& saSendMax
|
||||
)
|
||||
{
|
||||
return boost::make_shared<PathState>(iIndex, lesSource, spSourcePath, uReceiverID, uSenderID, saSend, saSendMax);
|
||||
}
|
||||
|
||||
static bool lessPriority(PathState::ref lhs, PathState::ref rhs);
|
||||
};
|
||||
|
||||
class RippleCalc
|
||||
{
|
||||
protected:
|
||||
LedgerEntrySet& lesActive;
|
||||
|
||||
public:
|
||||
// First time working in reverse a funding source was mentioned. Source may only be used there.
|
||||
curIssuerNode mumSource; // Map of currency, issuer to node index.
|
||||
|
||||
// If the transaction fails to meet some constraint, still need to delete unfunded offers.
|
||||
boost::unordered_set<uint256> musUnfundedFound; // Offers that were found unfunded.
|
||||
|
||||
PathState::pointer pathCreate(const STPath& spPath);
|
||||
void pathNext(PathState::ref pspCur, const int iPaths, const LedgerEntrySet& lesCheckpoint, LedgerEntrySet& lesCurrent);
|
||||
TER calcNode(const unsigned int uIndex, PathState::ref pspCur, const bool bMultiQuality);
|
||||
TER calcNodeRev(const unsigned int uIndex, PathState::ref pspCur, const bool bMultiQuality);
|
||||
TER calcNodeFwd(const unsigned int uIndex, PathState::ref pspCur, const bool bMultiQuality);
|
||||
TER calcNodeOfferRev(const unsigned int uIndex, PathState::ref pspCur, const bool bMultiQuality);
|
||||
TER calcNodeOfferFwd(const unsigned int uIndex, PathState::ref pspCur, const bool bMultiQuality);
|
||||
TER calcNodeAccountRev(const unsigned int uIndex, PathState::ref pspCur, const bool bMultiQuality);
|
||||
TER calcNodeAccountFwd(const unsigned int uIndex, PathState::ref pspCur, const bool bMultiQuality);
|
||||
TER calcNodeAdvance(const unsigned int uIndex, PathState::ref pspCur, const bool bMultiQuality, const bool bReverse);
|
||||
TER calcNodeDeliverRev(
|
||||
const unsigned int uIndex,
|
||||
PathState::ref pspCur,
|
||||
const bool bMultiQuality,
|
||||
const uint160& uOutAccountID,
|
||||
const STAmount& saOutReq,
|
||||
STAmount& saOutAct);
|
||||
|
||||
TER calcNodeDeliverFwd(
|
||||
const unsigned int uIndex,
|
||||
PathState::ref pspCur,
|
||||
const bool bMultiQuality,
|
||||
const uint160& uInAccountID,
|
||||
const STAmount& saInFunds,
|
||||
const STAmount& saInReq,
|
||||
STAmount& saInAct,
|
||||
STAmount& saInFees);
|
||||
|
||||
void calcNodeRipple(const uint32 uQualityIn, const uint32 uQualityOut,
|
||||
const STAmount& saPrvReq, const STAmount& saCurReq,
|
||||
STAmount& saPrvAct, STAmount& saCurAct,
|
||||
uint64& uRateMax);
|
||||
|
||||
RippleCalc(LedgerEntrySet& lesNodes) : lesActive(lesNodes) { ; }
|
||||
|
||||
static TER rippleCalc(
|
||||
LedgerEntrySet& lesActive,
|
||||
STAmount& saMaxAmountAct,
|
||||
STAmount& saDstAmountAct,
|
||||
const STAmount& saDstAmountReq,
|
||||
const STAmount& saMaxAmountReq,
|
||||
const uint160& uDstAccountID,
|
||||
const uint160& uSrcAccountID,
|
||||
const STPathSet& spsPaths,
|
||||
const bool bPartialPayment,
|
||||
const bool bLimitQuality,
|
||||
const bool bNoRippleDirect
|
||||
);
|
||||
};
|
||||
|
||||
#endif
|
||||
// vim:ts=4
|
||||
66
src/cpp/ripple/RippleLines.cpp
Normal file
66
src/cpp/ripple/RippleLines.cpp
Normal file
@@ -0,0 +1,66 @@
|
||||
#include "RippleLines.h"
|
||||
|
||||
#include <boost/foreach.hpp>
|
||||
|
||||
#include "Application.h"
|
||||
#include "Log.h"
|
||||
|
||||
SETUP_LOG();
|
||||
|
||||
RippleLines::RippleLines(const uint160& accountID, Ledger::ref ledger)
|
||||
{
|
||||
fillLines(accountID, ledger);
|
||||
}
|
||||
|
||||
void RippleLines::printRippleLines()
|
||||
{
|
||||
for (unsigned int i =0; i < mLines.size(); i++) {
|
||||
std::cout << i << ": " << mLines[i]->getAccountID().humanAccountID() << std::endl;
|
||||
}
|
||||
std::cout << std::endl;
|
||||
}
|
||||
|
||||
RippleLines::RippleLines(const uint160& accountID )
|
||||
{
|
||||
fillLines(accountID,theApp->getMasterLedger().getCurrentLedger());
|
||||
}
|
||||
|
||||
void RippleLines::fillLines(const uint160& accountID, Ledger::ref ledger)
|
||||
{
|
||||
uint256 rootIndex = Ledger::getOwnerDirIndex(accountID);
|
||||
uint256 currentIndex = rootIndex;
|
||||
|
||||
LedgerStateParms lspNode = lepNONE;
|
||||
|
||||
while (1)
|
||||
{
|
||||
SLE::pointer rippleDir=ledger->getDirNode(lspNode, currentIndex);
|
||||
if (!rippleDir) return;
|
||||
|
||||
STVector256 svOwnerNodes = rippleDir->getFieldV256(sfIndexes);
|
||||
BOOST_FOREACH(uint256& uNode, svOwnerNodes.peekValue())
|
||||
{
|
||||
SLE::pointer sleCur = ledger->getSLE(uNode);
|
||||
|
||||
if (ltRIPPLE_STATE == sleCur->getType())
|
||||
{
|
||||
RippleState::pointer rsLine = ledger->accessRippleState(uNode);
|
||||
if (rsLine)
|
||||
{
|
||||
rsLine->setViewAccount(accountID);
|
||||
mLines.push_back(rsLine);
|
||||
}
|
||||
else
|
||||
{
|
||||
cLog(lsWARNING) << "doRippleLinesGet: Bad index: " << uNode.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint64 uNodeNext = rippleDir->getFieldU64(sfIndexNext);
|
||||
if (!uNodeNext) return;
|
||||
|
||||
currentIndex = Ledger::getDirNodeIndex(rootIndex, uNodeNext);
|
||||
}
|
||||
}
|
||||
// vim:ts=4
|
||||
20
src/cpp/ripple/RippleLines.h
Normal file
20
src/cpp/ripple/RippleLines.h
Normal file
@@ -0,0 +1,20 @@
|
||||
#include "Ledger.h"
|
||||
#include "RippleState.h"
|
||||
|
||||
/*
|
||||
This pulls all the ripple lines of a given account out of the ledger.
|
||||
It provides a vector so you to easily iterate through them
|
||||
*/
|
||||
|
||||
class RippleLines
|
||||
{
|
||||
std::vector<RippleState::pointer> mLines;
|
||||
void fillLines(const uint160& accountID, Ledger::ref ledger);
|
||||
public:
|
||||
|
||||
RippleLines(const uint160& accountID, Ledger::ref ledger);
|
||||
RippleLines(const uint160& accountID ); // looks in the current ledger
|
||||
|
||||
std::vector<RippleState::pointer>& getLines() { return(mLines); }
|
||||
void printRippleLines();
|
||||
};
|
||||
38
src/cpp/ripple/RippleState.cpp
Normal file
38
src/cpp/ripple/RippleState.cpp
Normal file
@@ -0,0 +1,38 @@
|
||||
#include "RippleState.h"
|
||||
|
||||
RippleState::RippleState(SerializedLedgerEntry::pointer ledgerEntry) :
|
||||
mLedgerEntry(ledgerEntry),
|
||||
mValid(false),
|
||||
mViewLowest(true)
|
||||
{
|
||||
if (!mLedgerEntry || mLedgerEntry->getType() != ltRIPPLE_STATE) return;
|
||||
|
||||
mLowLimit = mLedgerEntry->getFieldAmount(sfLowLimit);
|
||||
mHighLimit = mLedgerEntry->getFieldAmount(sfHighLimit);
|
||||
|
||||
mLowID = RippleAddress::createAccountID(mLowLimit.getIssuer());
|
||||
mHighID = RippleAddress::createAccountID(mHighLimit.getIssuer());
|
||||
|
||||
mLowQualityIn = mLedgerEntry->getFieldU32(sfLowQualityIn);
|
||||
mLowQualityOut = mLedgerEntry->getFieldU32(sfLowQualityOut);
|
||||
|
||||
mHighQualityIn = mLedgerEntry->getFieldU32(sfHighQualityIn);
|
||||
mHighQualityOut = mLedgerEntry->getFieldU32(sfHighQualityOut);
|
||||
|
||||
mBalance = mLedgerEntry->getFieldAmount(sfBalance);
|
||||
|
||||
mValid = true;
|
||||
}
|
||||
|
||||
void RippleState::setViewAccount(const uint160& accountID)
|
||||
{
|
||||
bool bViewLowestNew = mLowID.getAccountID() == accountID;
|
||||
|
||||
if (bViewLowestNew != mViewLowest)
|
||||
{
|
||||
mViewLowest = bViewLowestNew;
|
||||
mBalance.negate();
|
||||
}
|
||||
}
|
||||
|
||||
// vim:ts=4
|
||||
60
src/cpp/ripple/RippleState.h
Normal file
60
src/cpp/ripple/RippleState.h
Normal file
@@ -0,0 +1,60 @@
|
||||
#ifndef __RIPPLESTATE__
|
||||
#define __RIPPLESTATE__
|
||||
|
||||
//
|
||||
// A ripple line's state.
|
||||
// - Isolate ledger entry format.
|
||||
//
|
||||
|
||||
#include "SerializedLedger.h"
|
||||
|
||||
#include <boost/shared_ptr.hpp>
|
||||
|
||||
class RippleState
|
||||
{
|
||||
public:
|
||||
typedef boost::shared_ptr<RippleState> pointer;
|
||||
|
||||
private:
|
||||
SerializedLedgerEntry::pointer mLedgerEntry;
|
||||
|
||||
RippleAddress mLowID;
|
||||
RippleAddress mHighID;
|
||||
|
||||
STAmount mLowLimit;
|
||||
STAmount mHighLimit;
|
||||
|
||||
uint64 mLowQualityIn;
|
||||
uint64 mLowQualityOut;
|
||||
uint64 mHighQualityIn;
|
||||
uint64 mHighQualityOut;
|
||||
|
||||
STAmount mBalance;
|
||||
|
||||
bool mValid;
|
||||
bool mViewLowest;
|
||||
|
||||
public:
|
||||
RippleState(SerializedLedgerEntry::pointer ledgerEntry); // For accounts in a ledger
|
||||
|
||||
void setViewAccount(const uint160& accountID);
|
||||
|
||||
const RippleAddress getAccountID() const { return mViewLowest ? mLowID : mHighID; }
|
||||
const RippleAddress getAccountIDPeer() const { return mViewLowest ? mHighID : mLowID; }
|
||||
|
||||
STAmount getBalance() const { return mBalance; }
|
||||
|
||||
STAmount getLimit() const { return mViewLowest ? mLowLimit : mHighLimit; }
|
||||
STAmount getLimitPeer() const { return mViewLowest ? mHighLimit : mLowLimit; }
|
||||
|
||||
uint32 getQualityIn() const { return((uint32) (mViewLowest ? mLowQualityIn : mHighQualityIn)); }
|
||||
uint32 getQualityOut() const { return((uint32) (mViewLowest ? mLowQualityOut : mHighQualityOut)); }
|
||||
|
||||
SerializedLedgerEntry::pointer getSLE() { return mLedgerEntry; }
|
||||
const SerializedLedgerEntry& peekSLE() const { return *mLedgerEntry; }
|
||||
SerializedLedgerEntry& peekSLE() { return *mLedgerEntry; }
|
||||
|
||||
std::vector<unsigned char> getRaw() const;
|
||||
};
|
||||
#endif
|
||||
// vim:ts=4
|
||||
937
src/cpp/ripple/SHAMap.cpp
Normal file
937
src/cpp/ripple/SHAMap.cpp
Normal file
@@ -0,0 +1,937 @@
|
||||
|
||||
#include "SHAMap.h"
|
||||
|
||||
#include <stack>
|
||||
|
||||
#include <boost/foreach.hpp>
|
||||
#include <boost/lexical_cast.hpp>
|
||||
#include <boost/smart_ptr/make_shared.hpp>
|
||||
#include <boost/test/unit_test.hpp>
|
||||
#include <iostream>
|
||||
|
||||
#include "Serializer.h"
|
||||
#include "BitcoinUtil.h"
|
||||
#include "Log.h"
|
||||
#include "SHAMap.h"
|
||||
#include "Application.h"
|
||||
|
||||
SETUP_LOG();
|
||||
|
||||
DECLARE_INSTANCE(SHAMap);
|
||||
DECLARE_INSTANCE(SHAMapItem);
|
||||
DECLARE_INSTANCE(SHAMapTreeNode);
|
||||
|
||||
std::size_t hash_value(const SHAMapNode& mn)
|
||||
{
|
||||
std::size_t seed = theApp->getNonceST();
|
||||
|
||||
boost::hash_combine(seed, mn.getDepth());
|
||||
|
||||
return mn.getNodeID().hash_combine(seed);
|
||||
}
|
||||
|
||||
std::size_t hash_value(const uint256& u)
|
||||
{
|
||||
std::size_t seed = theApp->getNonceST();
|
||||
|
||||
return u.hash_combine(seed);
|
||||
}
|
||||
|
||||
std::size_t hash_value(const uint160& u)
|
||||
{
|
||||
std::size_t seed = theApp->getNonceST();
|
||||
|
||||
return u.hash_combine(seed);
|
||||
}
|
||||
|
||||
|
||||
SHAMap::SHAMap(SHAMapType t, uint32 seq) : mSeq(seq), mState(smsModifying), mType(t)
|
||||
{
|
||||
root = boost::make_shared<SHAMapTreeNode>(mSeq, SHAMapNode(0, uint256()));
|
||||
root->makeInner();
|
||||
mTNByID[*root] = root;
|
||||
}
|
||||
|
||||
SHAMap::SHAMap(SHAMapType t, const uint256& hash) : mSeq(1), mState(smsSynching), mType(t)
|
||||
{ // FIXME: Need to acquire root node
|
||||
root = boost::make_shared<SHAMapTreeNode>(mSeq, SHAMapNode(0, uint256()));
|
||||
root->makeInner();
|
||||
mTNByID[*root] = root;
|
||||
}
|
||||
|
||||
SHAMap::pointer SHAMap::snapShot(bool isMutable)
|
||||
{ // Return a new SHAMap that is an immutable snapshot of this one
|
||||
// Initially nodes are shared, but CoW is forced on both ledgers
|
||||
SHAMap::pointer ret = boost::make_shared<SHAMap>(mType);
|
||||
SHAMap& newMap = *ret;
|
||||
newMap.mSeq = ++mSeq;
|
||||
newMap.mTNByID = mTNByID;
|
||||
newMap.root = root;
|
||||
if (!isMutable)
|
||||
newMap.mState = smsImmutable;
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::stack<SHAMapTreeNode::pointer> SHAMap::getStack(const uint256& id, bool include_nonmatching_leaf, bool partialOk)
|
||||
{
|
||||
// Walk the tree as far as possible to the specified identifier
|
||||
// produce a stack of nodes along the way, with the terminal node at the top
|
||||
std::stack<SHAMapTreeNode::pointer> stack;
|
||||
SHAMapTreeNode::pointer node = root;
|
||||
while (!node->isLeaf())
|
||||
{
|
||||
stack.push(node);
|
||||
|
||||
int branch = node->selectBranch(id);
|
||||
assert(branch >= 0);
|
||||
|
||||
uint256 hash = node->getChildHash(branch);
|
||||
if (hash.isZero())
|
||||
return stack;
|
||||
|
||||
try
|
||||
{
|
||||
node = getNode(node->getChildNodeID(branch), hash, false);
|
||||
}
|
||||
catch (SHAMapMissingNode& mn)
|
||||
{
|
||||
if (partialOk)
|
||||
return stack;
|
||||
mn.setTargetNode(id);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
if (include_nonmatching_leaf || (node->peekItem()->getTag() == id))
|
||||
stack.push(node);
|
||||
|
||||
return stack;
|
||||
}
|
||||
|
||||
void SHAMap::dirtyUp(std::stack<SHAMapTreeNode::pointer>& stack, const uint256& target, uint256 prevHash)
|
||||
{ // walk the tree up from through the inner nodes to the root
|
||||
// update linking hashes and add nodes to dirty list
|
||||
|
||||
assert((mState != smsSynching) && (mState != smsImmutable));
|
||||
|
||||
while (!stack.empty())
|
||||
{
|
||||
SHAMapTreeNode::pointer node = stack.top();
|
||||
stack.pop();
|
||||
assert(node->isInnerNode());
|
||||
|
||||
int branch = node->selectBranch(target);
|
||||
assert(branch >= 0);
|
||||
|
||||
returnNode(node, true);
|
||||
|
||||
if (!node->setChildHash(branch, prevHash))
|
||||
{
|
||||
cLog(lsFATAL) << "dirtyUp terminates early";
|
||||
assert(false);
|
||||
return;
|
||||
}
|
||||
#ifdef ST_DEBUG
|
||||
cLog(lsTRACE) << "dirtyUp sets branch " << branch << " to " << prevHash;
|
||||
#endif
|
||||
prevHash = node->getNodeHash();
|
||||
assert(prevHash.isNonZero());
|
||||
}
|
||||
}
|
||||
|
||||
SHAMapTreeNode::pointer SHAMap::checkCacheNode(const SHAMapNode& iNode)
|
||||
{
|
||||
boost::unordered_map<SHAMapNode, SHAMapTreeNode::pointer>::iterator it = mTNByID.find(iNode);
|
||||
if (it == mTNByID.end())
|
||||
return SHAMapTreeNode::pointer();
|
||||
return it->second;
|
||||
}
|
||||
|
||||
SHAMapTreeNode::pointer SHAMap::walkTo(const uint256& id, bool modify)
|
||||
{ // walk down to the terminal node for this ID
|
||||
|
||||
SHAMapTreeNode::pointer inNode = root;
|
||||
|
||||
while (!inNode->isLeaf())
|
||||
{
|
||||
int branch = inNode->selectBranch(id);
|
||||
if (inNode->isEmptyBranch(branch))
|
||||
return inNode;
|
||||
uint256 childHash = inNode->getChildHash(branch);
|
||||
|
||||
try
|
||||
{
|
||||
inNode = getNode(inNode->getChildNodeID(branch), childHash, false);
|
||||
}
|
||||
catch (SHAMapMissingNode& mn)
|
||||
{
|
||||
mn.setTargetNode(id);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
if (inNode->getTag() != id)
|
||||
return SHAMapTreeNode::pointer();
|
||||
if (modify)
|
||||
returnNode(inNode, true);
|
||||
return inNode;
|
||||
}
|
||||
|
||||
SHAMapTreeNode* SHAMap::walkToPointer(const uint256& id)
|
||||
{
|
||||
SHAMapTreeNode* inNode = root.get();
|
||||
while (!inNode->isLeaf())
|
||||
{
|
||||
int branch = inNode->selectBranch(id);
|
||||
const uint256& nextHash = inNode->getChildHash(branch);
|
||||
if (nextHash.isZero()) return NULL;
|
||||
inNode = getNodePointer(inNode->getChildNodeID(branch), nextHash);
|
||||
if (!inNode)
|
||||
throw SHAMapMissingNode(mType, inNode->getChildNodeID(branch), nextHash, id);
|
||||
}
|
||||
return (inNode->getTag() == id) ? inNode : NULL;
|
||||
}
|
||||
|
||||
SHAMapTreeNode::pointer SHAMap::getNode(const SHAMapNode& id, const uint256& hash, bool modify)
|
||||
{ // retrieve a node whose node hash is known
|
||||
SHAMapTreeNode::pointer node = checkCacheNode(id);
|
||||
if (node)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
if (node->getNodeHash() != hash)
|
||||
{
|
||||
std::cerr << "Attempt to get node, hash not in tree" << std::endl;
|
||||
std::cerr << "ID: " << id << std::endl;
|
||||
std::cerr << "TgtHash " << hash << std::endl;
|
||||
std::cerr << "NodHash " << node->getNodeHash() << std::endl;
|
||||
dump();
|
||||
throw std::runtime_error("invalid node");
|
||||
}
|
||||
#endif
|
||||
returnNode(node, modify);
|
||||
return node;
|
||||
}
|
||||
|
||||
node = fetchNodeExternal(id, hash);
|
||||
if (!mTNByID.insert(std::make_pair(id, node)).second)
|
||||
assert(false);
|
||||
trackNewNode(node);
|
||||
return node;
|
||||
}
|
||||
|
||||
SHAMapTreeNode* SHAMap::getNodePointer(const SHAMapNode& id, const uint256& hash)
|
||||
{ // fast, but you do not hold a reference
|
||||
boost::unordered_map<SHAMapNode, SHAMapTreeNode::pointer>::iterator it = mTNByID.find(id);
|
||||
if (it != mTNByID.end())
|
||||
return it->second.get();
|
||||
|
||||
SHAMapTreeNode::pointer node = fetchNodeExternal(id, hash);
|
||||
if (!mTNByID.insert(std::make_pair(id, node)).second)
|
||||
assert(false);
|
||||
trackNewNode(node);
|
||||
return node.get();
|
||||
}
|
||||
|
||||
void SHAMap::returnNode(SHAMapTreeNode::pointer& node, bool modify)
|
||||
{ // make sure the node is suitable for the intended operation (copy on write)
|
||||
assert(node->isValid());
|
||||
assert(node->getSeq() <= mSeq);
|
||||
if (node && modify && (node->getSeq() != mSeq))
|
||||
{ // have a CoW
|
||||
assert(node->getSeq() < mSeq);
|
||||
|
||||
node = boost::make_shared<SHAMapTreeNode>(*node, mSeq); // here's to the new node, same as the old node
|
||||
assert(node->isValid());
|
||||
|
||||
mTNByID[*node] = node;
|
||||
if (node->isRoot())
|
||||
root = node;
|
||||
if (mDirtyNodes)
|
||||
(*mDirtyNodes)[*node] = node;
|
||||
}
|
||||
}
|
||||
|
||||
void SHAMap::trackNewNode(SHAMapTreeNode::pointer& node)
|
||||
{
|
||||
if (mDirtyNodes)
|
||||
(*mDirtyNodes)[*node] = node;
|
||||
}
|
||||
|
||||
SHAMapItem::SHAMapItem(const uint256& tag, const std::vector<unsigned char>& data)
|
||||
: mTag(tag), mData(data)
|
||||
{ ; }
|
||||
|
||||
SHAMapItem::SHAMapItem(const uint256& tag, const Serializer& data)
|
||||
: mTag(tag), mData(data.peekData())
|
||||
{ ; }
|
||||
|
||||
SHAMapTreeNode* SHAMap::firstBelow(SHAMapTreeNode* node)
|
||||
{
|
||||
// Return the first item below this node
|
||||
#ifdef ST_DEBUG
|
||||
std::cerr << "firstBelow(" << *node << ")" << std::endl;
|
||||
#endif
|
||||
do
|
||||
{ // Walk down the tree
|
||||
if (node->hasItem()) return node;
|
||||
|
||||
bool foundNode = false;
|
||||
for (int i = 0; i < 16; ++i)
|
||||
if (!node->isEmptyBranch(i))
|
||||
{
|
||||
#ifdef ST_DEBUG
|
||||
std::cerr << " FB: node " << *node << std::endl;
|
||||
std::cerr << " has non-empty branch " << i << " : " <<
|
||||
node->getChildNodeID(i) << ", " << node->getChildHash(i) << std::endl;
|
||||
#endif
|
||||
node = getNodePointer(node->getChildNodeID(i), node->getChildHash(i));
|
||||
foundNode = true;
|
||||
break;
|
||||
}
|
||||
if (!foundNode)
|
||||
return NULL;
|
||||
} while (true);
|
||||
}
|
||||
|
||||
SHAMapTreeNode* SHAMap::lastBelow(SHAMapTreeNode* node)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
std::cerr << "lastBelow(" << *node << ")" << std::endl;
|
||||
#endif
|
||||
|
||||
do
|
||||
{ // Walk down the tree
|
||||
if (node->hasItem())
|
||||
return node;
|
||||
|
||||
bool foundNode = false;
|
||||
for (int i = 15; i >= 0; ++i)
|
||||
if (!node->isEmptyBranch(i))
|
||||
{
|
||||
node = getNodePointer(node->getChildNodeID(i), node->getChildHash(i));
|
||||
foundNode = true;
|
||||
break;
|
||||
}
|
||||
if (!foundNode)
|
||||
return NULL;
|
||||
} while (true);
|
||||
}
|
||||
|
||||
SHAMapItem::pointer SHAMap::onlyBelow(SHAMapTreeNode* node)
|
||||
{
|
||||
// If there is only one item below this node, return it
|
||||
bool found;
|
||||
while (!node->isLeaf())
|
||||
{
|
||||
found = false;
|
||||
SHAMapTreeNode* nextNode;
|
||||
|
||||
for (int i = 0; i < 16; ++i)
|
||||
if (!node->isEmptyBranch(i))
|
||||
{
|
||||
if (found) return SHAMapItem::pointer(); // two leaves below
|
||||
nextNode = getNodePointer(node->getChildNodeID(i), node->getChildHash(i));
|
||||
found = true;
|
||||
}
|
||||
|
||||
if (!found)
|
||||
{
|
||||
std::cerr << *node << std::endl;
|
||||
assert(false);
|
||||
return SHAMapItem::pointer();
|
||||
}
|
||||
node = nextNode;
|
||||
}
|
||||
assert(node->hasItem());
|
||||
return node->peekItem();
|
||||
}
|
||||
|
||||
void SHAMap::eraseChildren(SHAMapTreeNode::pointer node)
|
||||
{ // this node has only one item below it, erase its children
|
||||
bool erase = false;
|
||||
while (node->isInner())
|
||||
{
|
||||
for (int i = 0; i < 16; ++i)
|
||||
if (!node->isEmptyBranch(i))
|
||||
{
|
||||
SHAMapTreeNode::pointer nextNode = getNode(node->getChildNodeID(i), node->getChildHash(i), false);
|
||||
if (erase)
|
||||
{
|
||||
returnNode(node, true);
|
||||
if (mTNByID.erase(*node))
|
||||
assert(false);
|
||||
}
|
||||
erase = true;
|
||||
node = nextNode;
|
||||
break;
|
||||
}
|
||||
}
|
||||
returnNode(node, true);
|
||||
if (mTNByID.erase(*node) == 0)
|
||||
assert(false);
|
||||
return;
|
||||
}
|
||||
|
||||
SHAMapItem::pointer SHAMap::peekFirstItem()
|
||||
{
|
||||
boost::recursive_mutex::scoped_lock sl(mLock);
|
||||
SHAMapTreeNode *node = firstBelow(root.get());
|
||||
if (!node)
|
||||
return SHAMapItem::pointer();
|
||||
return node->peekItem();
|
||||
}
|
||||
|
||||
SHAMapItem::pointer SHAMap::peekFirstItem(SHAMapTreeNode::TNType& type)
|
||||
{
|
||||
boost::recursive_mutex::scoped_lock sl(mLock);
|
||||
SHAMapTreeNode *node = firstBelow(root.get());
|
||||
if (!node)
|
||||
return SHAMapItem::pointer();
|
||||
type = node->getType();
|
||||
return node->peekItem();
|
||||
}
|
||||
|
||||
SHAMapItem::pointer SHAMap::peekLastItem()
|
||||
{
|
||||
boost::recursive_mutex::scoped_lock sl(mLock);
|
||||
SHAMapTreeNode *node = lastBelow(root.get());
|
||||
if (!node)
|
||||
return SHAMapItem::pointer();
|
||||
return node->peekItem();
|
||||
}
|
||||
|
||||
SHAMapItem::pointer SHAMap::peekNextItem(const uint256& id)
|
||||
{
|
||||
SHAMapTreeNode::TNType type;
|
||||
return peekNextItem(id, type);
|
||||
}
|
||||
|
||||
|
||||
SHAMapItem::pointer SHAMap::peekNextItem(const uint256& id, SHAMapTreeNode::TNType& type)
|
||||
{ // Get a pointer to the next item in the tree after a given item - item must be in tree
|
||||
boost::recursive_mutex::scoped_lock sl(mLock);
|
||||
|
||||
std::stack<SHAMapTreeNode::pointer> stack = getStack(id, true, false);
|
||||
while (!stack.empty())
|
||||
{
|
||||
SHAMapTreeNode::pointer node = stack.top();
|
||||
stack.pop();
|
||||
|
||||
if (node->isLeaf())
|
||||
{
|
||||
if (node->peekItem()->getTag() > id)
|
||||
{
|
||||
type = node->getType();
|
||||
return node->peekItem();
|
||||
}
|
||||
}
|
||||
else
|
||||
for (int i = node->selectBranch(id) + 1; i < 16; ++i)
|
||||
if (!node->isEmptyBranch(i))
|
||||
{
|
||||
SHAMapTreeNode *firstNode = getNodePointer(node->getChildNodeID(i), node->getChildHash(i));
|
||||
if (!firstNode)
|
||||
throw std::runtime_error("missing node");
|
||||
firstNode = firstBelow(firstNode);
|
||||
if (!firstNode)
|
||||
throw std::runtime_error("missing node");
|
||||
type = firstNode->getType();
|
||||
return firstNode->peekItem();
|
||||
}
|
||||
}
|
||||
// must be last item
|
||||
return SHAMapItem::pointer();
|
||||
}
|
||||
|
||||
SHAMapItem::pointer SHAMap::peekPrevItem(const uint256& id)
|
||||
{ // Get a pointer to the previous item in the tree after a given item - item must be in tree
|
||||
boost::recursive_mutex::scoped_lock sl(mLock);
|
||||
|
||||
std::stack<SHAMapTreeNode::pointer> stack = getStack(id, true, false);
|
||||
while (!stack.empty())
|
||||
{
|
||||
SHAMapTreeNode::pointer node = stack.top();
|
||||
stack.pop();
|
||||
|
||||
if (node->isLeaf())
|
||||
{
|
||||
if (node->peekItem()->getTag() < id)
|
||||
return node->peekItem();
|
||||
}
|
||||
else for (int i = node->selectBranch(id) - 1; i >= 0; --i)
|
||||
if (!node->isEmptyBranch(i))
|
||||
{
|
||||
node = getNode(node->getChildNodeID(i), node->getChildHash(i), false);
|
||||
SHAMapTreeNode* item = firstBelow(node.get());
|
||||
if (!item)
|
||||
throw std::runtime_error("missing node");
|
||||
return item->peekItem();
|
||||
}
|
||||
}
|
||||
// must be last item
|
||||
return SHAMapItem::pointer();
|
||||
}
|
||||
|
||||
SHAMapItem::pointer SHAMap::peekItem(const uint256& id)
|
||||
{
|
||||
boost::recursive_mutex::scoped_lock sl(mLock);
|
||||
SHAMapTreeNode* leaf = walkToPointer(id);
|
||||
if (!leaf)
|
||||
return SHAMapItem::pointer();
|
||||
return leaf->peekItem();
|
||||
}
|
||||
|
||||
SHAMapItem::pointer SHAMap::peekItem(const uint256& id, SHAMapTreeNode::TNType& type)
|
||||
{
|
||||
boost::recursive_mutex::scoped_lock sl(mLock);
|
||||
SHAMapTreeNode* leaf = walkToPointer(id);
|
||||
if (!leaf)
|
||||
return SHAMapItem::pointer();
|
||||
type = leaf->getType();
|
||||
return leaf->peekItem();
|
||||
}
|
||||
|
||||
bool SHAMap::hasItem(const uint256& id)
|
||||
{ // does the tree have an item with this ID
|
||||
boost::recursive_mutex::scoped_lock sl(mLock);
|
||||
|
||||
SHAMapTreeNode* leaf = walkToPointer(id);
|
||||
return (leaf != NULL);
|
||||
}
|
||||
|
||||
bool SHAMap::delItem(const uint256& id)
|
||||
{ // delete the item with this ID
|
||||
boost::recursive_mutex::scoped_lock sl(mLock);
|
||||
assert(mState != smsImmutable);
|
||||
|
||||
std::stack<SHAMapTreeNode::pointer> stack = getStack(id, true, false);
|
||||
if (stack.empty())
|
||||
throw std::runtime_error("missing node");
|
||||
|
||||
SHAMapTreeNode::pointer leaf=stack.top();
|
||||
stack.pop();
|
||||
if (!leaf || !leaf->hasItem() || (leaf->peekItem()->getTag() != id))
|
||||
return false;
|
||||
|
||||
SHAMapTreeNode::TNType type=leaf->getType();
|
||||
returnNode(leaf, true);
|
||||
if (mTNByID.erase(*leaf) == 0)
|
||||
assert(false);
|
||||
|
||||
uint256 prevHash;
|
||||
while (!stack.empty())
|
||||
{
|
||||
SHAMapTreeNode::pointer node=stack.top();
|
||||
stack.pop();
|
||||
returnNode(node, true);
|
||||
assert(node->isInner());
|
||||
|
||||
if (!node->setChildHash(node->selectBranch(id), prevHash))
|
||||
{
|
||||
assert(false);
|
||||
return true;
|
||||
}
|
||||
if (!node->isRoot())
|
||||
{ // we may have made this a node with 1 or 0 children
|
||||
int bc = node->getBranchCount();
|
||||
if (bc == 0)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
std::cerr << "delItem makes empty node" << std::endl;
|
||||
#endif
|
||||
prevHash=uint256();
|
||||
if (!mTNByID.erase(*node))
|
||||
assert(false);
|
||||
}
|
||||
else if (bc == 1)
|
||||
{ // pull up on the thread
|
||||
SHAMapItem::pointer item = onlyBelow(node.get());
|
||||
if (item)
|
||||
{
|
||||
eraseChildren(node);
|
||||
#ifdef ST_DEBUG
|
||||
std::cerr << "Making item node " << *node << std::endl;
|
||||
#endif
|
||||
node->setItem(item, type);
|
||||
}
|
||||
prevHash = node->getNodeHash();
|
||||
assert(prevHash.isNonZero());
|
||||
}
|
||||
else
|
||||
{
|
||||
prevHash = node->getNodeHash();
|
||||
assert(prevHash.isNonZero());
|
||||
}
|
||||
}
|
||||
else assert(stack.empty());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SHAMap::addGiveItem(SHAMapItem::ref item, bool isTransaction, bool hasMeta)
|
||||
{ // add the specified item, does not update
|
||||
#ifdef ST_DEBUG
|
||||
std::cerr << "aGI " << item->getTag() << std::endl;
|
||||
#endif
|
||||
|
||||
uint256 tag = item->getTag();
|
||||
SHAMapTreeNode::TNType type = !isTransaction ? SHAMapTreeNode::tnACCOUNT_STATE :
|
||||
(hasMeta ? SHAMapTreeNode::tnTRANSACTION_MD : SHAMapTreeNode::tnTRANSACTION_NM);
|
||||
|
||||
boost::recursive_mutex::scoped_lock sl(mLock);
|
||||
assert(mState != smsImmutable);
|
||||
|
||||
std::stack<SHAMapTreeNode::pointer> stack = getStack(tag, true, false);
|
||||
if (stack.empty())
|
||||
throw std::runtime_error("missing node");
|
||||
|
||||
SHAMapTreeNode::pointer node = stack.top();
|
||||
stack.pop();
|
||||
|
||||
if (node->isLeaf() && (node->peekItem()->getTag() == tag))
|
||||
throw std::runtime_error("addGiveItem ends on leaf with same tag");
|
||||
|
||||
uint256 prevHash;
|
||||
returnNode(node, true);
|
||||
|
||||
if (node->isInner())
|
||||
{ // easy case, we end on an inner node
|
||||
#ifdef ST_DEBUG
|
||||
std::cerr << "aGI inner " << *node << std::endl;
|
||||
#endif
|
||||
int branch = node->selectBranch(tag);
|
||||
assert(node->isEmptyBranch(branch));
|
||||
SHAMapTreeNode::pointer newNode =
|
||||
boost::make_shared<SHAMapTreeNode>(node->getChildNodeID(branch), item, type, mSeq);
|
||||
if (!mTNByID.insert(std::make_pair(SHAMapNode(*newNode), newNode)).second)
|
||||
{
|
||||
std::cerr << "Node: " << *node << std::endl;
|
||||
std::cerr << "NewNode: " << *newNode << std::endl;
|
||||
dump();
|
||||
assert(false);
|
||||
throw std::runtime_error("invalid inner node");
|
||||
}
|
||||
trackNewNode(newNode);
|
||||
node->setChildHash(branch, newNode->getNodeHash());
|
||||
}
|
||||
else
|
||||
{ // this is a leaf node that has to be made an inner node holding two items
|
||||
#ifdef ST_DEBUG
|
||||
std::cerr << "aGI leaf " << *node << std::endl;
|
||||
std::cerr << "Existing: " << node->peekItem()->getTag() << std::endl;
|
||||
#endif
|
||||
SHAMapItem::pointer otherItem = node->peekItem();
|
||||
assert(otherItem && (tag != otherItem->getTag()));
|
||||
|
||||
node->makeInner();
|
||||
|
||||
int b1, b2;
|
||||
|
||||
while ((b1 = node->selectBranch(tag)) == (b2 = node->selectBranch(otherItem->getTag())))
|
||||
{ // we need a new inner node, since both go on same branch at this level
|
||||
#ifdef ST_DEBUG
|
||||
std::cerr << "need new inner node at " << node->getDepth() << ", "
|
||||
<< b1 << "==" << b2 << std::endl;
|
||||
#endif
|
||||
SHAMapTreeNode::pointer newNode =
|
||||
boost::make_shared<SHAMapTreeNode>(mSeq, node->getChildNodeID(b1));
|
||||
newNode->makeInner();
|
||||
if (!mTNByID.insert(std::make_pair(SHAMapNode(*newNode), newNode)).second)
|
||||
assert(false);
|
||||
stack.push(node);
|
||||
node = newNode;
|
||||
trackNewNode(node);
|
||||
}
|
||||
|
||||
// we can add the two leaf nodes here
|
||||
assert(node->isInner());
|
||||
SHAMapTreeNode::pointer newNode =
|
||||
boost::make_shared<SHAMapTreeNode>(node->getChildNodeID(b1), item, type, mSeq);
|
||||
assert(newNode->isValid() && newNode->isLeaf());
|
||||
if (!mTNByID.insert(std::make_pair(SHAMapNode(*newNode), newNode)).second)
|
||||
assert(false);
|
||||
node->setChildHash(b1, newNode->getNodeHash()); // OPTIMIZEME hash op not needed
|
||||
trackNewNode(newNode);
|
||||
|
||||
newNode = boost::make_shared<SHAMapTreeNode>(node->getChildNodeID(b2), otherItem, type, mSeq);
|
||||
assert(newNode->isValid() && newNode->isLeaf());
|
||||
if (!mTNByID.insert(std::make_pair(SHAMapNode(*newNode), newNode)).second)
|
||||
assert(false);
|
||||
node->setChildHash(b2, newNode->getNodeHash());
|
||||
trackNewNode(newNode);
|
||||
}
|
||||
|
||||
dirtyUp(stack, tag, node->getNodeHash());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SHAMap::addItem(const SHAMapItem& i, bool isTransaction, bool hasMetaData)
|
||||
{
|
||||
return addGiveItem(boost::make_shared<SHAMapItem>(i), isTransaction, hasMetaData);
|
||||
}
|
||||
|
||||
bool SHAMap::updateGiveItem(SHAMapItem::ref item, bool isTransaction, bool hasMeta)
|
||||
{ // can't change the tag but can change the hash
|
||||
uint256 tag = item->getTag();
|
||||
|
||||
boost::recursive_mutex::scoped_lock sl(mLock);
|
||||
assert(mState != smsImmutable);
|
||||
|
||||
std::stack<SHAMapTreeNode::pointer> stack = getStack(tag, true, false);
|
||||
if (stack.empty()) throw std::runtime_error("missing node");
|
||||
|
||||
SHAMapTreeNode::pointer node = stack.top();
|
||||
stack.pop();
|
||||
|
||||
if (!node->isLeaf() || (node->peekItem()->getTag() != tag))
|
||||
{
|
||||
assert(false);
|
||||
return false;
|
||||
}
|
||||
|
||||
returnNode(node, true);
|
||||
if (!node->setItem(item, !isTransaction ? SHAMapTreeNode::tnACCOUNT_STATE :
|
||||
(hasMeta ? SHAMapTreeNode::tnTRANSACTION_MD : SHAMapTreeNode::tnTRANSACTION_NM)))
|
||||
{
|
||||
cLog(lsWARNING) << "SHAMap setItem, no change";
|
||||
return true;
|
||||
}
|
||||
|
||||
dirtyUp(stack, tag, node->getNodeHash());
|
||||
return true;
|
||||
}
|
||||
|
||||
void SHAMapItem::dump()
|
||||
{
|
||||
std::cerr << "SHAMapItem(" << mTag << ") " << mData.size() << "bytes" << std::endl;
|
||||
}
|
||||
|
||||
SHAMapTreeNode::pointer SHAMap::fetchNodeExternal(const SHAMapNode& id, const uint256& hash)
|
||||
{
|
||||
if (!theApp->running())
|
||||
throw SHAMapMissingNode(mType, id, hash);
|
||||
|
||||
HashedObject::pointer obj(theApp->getHashedObjectStore().retrieve(hash));
|
||||
if (!obj)
|
||||
{
|
||||
// Log(lsTRACE) << "fetchNodeExternal: missing " << hash;
|
||||
throw SHAMapMissingNode(mType, id, hash);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
SHAMapTreeNode::pointer ret = boost::make_shared<SHAMapTreeNode>(id, obj->getData(), mSeq - 1, snfPREFIX);
|
||||
if (id != *ret)
|
||||
{
|
||||
Log(lsFATAL) << "id:" << id << ", got:" << *ret;
|
||||
assert(false);
|
||||
return SHAMapTreeNode::pointer();
|
||||
}
|
||||
if (ret->getNodeHash() != hash)
|
||||
{
|
||||
Log(lsFATAL) << "Hashes don't match";
|
||||
assert(false);
|
||||
return SHAMapTreeNode::pointer();
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
cLog(lsWARNING) << "fetchNodeExternal gets an invalid node: " << hash;
|
||||
throw SHAMapMissingNode(mType, id, hash);
|
||||
}
|
||||
}
|
||||
|
||||
void SHAMap::fetchRoot(const uint256& hash)
|
||||
{
|
||||
if (sLog(lsTRACE))
|
||||
{
|
||||
if (mType == smtTRANSACTION)
|
||||
Log(lsTRACE) << "Fetch root TXN node " << hash;
|
||||
else if (mType == smtSTATE)
|
||||
Log(lsTRACE) << "Fetch root STATE node " << hash;
|
||||
else
|
||||
Log(lsTRACE) << "Fetch root SHAMap node " << hash;
|
||||
}
|
||||
root = fetchNodeExternal(SHAMapNode(), hash);
|
||||
mTNByID[*root] = root;
|
||||
assert(root->getNodeHash() == hash);
|
||||
}
|
||||
|
||||
int SHAMap::armDirty()
|
||||
{ // begin saving dirty nodes
|
||||
mDirtyNodes = boost::make_shared< boost::unordered_map<SHAMapNode, SHAMapTreeNode::pointer> >();
|
||||
return ++mSeq;
|
||||
}
|
||||
|
||||
int SHAMap::flushDirty(SHADirtyMap& map, int maxNodes, HashedObjectType t, uint32 seq)
|
||||
{
|
||||
int flushed = 0;
|
||||
Serializer s;
|
||||
|
||||
for(SHADirtyMap::iterator it = map.begin(); it != map.end(); it = map.erase(it))
|
||||
{
|
||||
// tLog(t == hotTRANSACTION_NODE, lsDEBUG) << "TX node write " << it->first;
|
||||
// tLog(t == hotACCOUNT_NODE, lsDEBUG) << "STATE node write " << it->first;
|
||||
s.erase();
|
||||
it->second->addRaw(s, snfPREFIX);
|
||||
if (s.getSHA512Half() != it->second->getNodeHash())
|
||||
{
|
||||
cLog(lsFATAL) << *(it->second);
|
||||
cLog(lsFATAL) << lexical_cast_i(s.getDataLength());
|
||||
cLog(lsFATAL) << s.getSHA512Half() << " != " << it->second->getNodeHash();
|
||||
assert(false);
|
||||
}
|
||||
theApp->getHashedObjectStore().store(t, seq, s.peekData(), it->second->getNodeHash());
|
||||
if (flushed++ >= maxNodes)
|
||||
return flushed;
|
||||
}
|
||||
|
||||
return flushed;
|
||||
}
|
||||
|
||||
boost::shared_ptr<SHAMap::SHADirtyMap> SHAMap::disarmDirty()
|
||||
{ // stop saving dirty nodes
|
||||
boost::shared_ptr<SHADirtyMap> ret;
|
||||
ret.swap(mDirtyNodes);
|
||||
return ret;
|
||||
}
|
||||
|
||||
SHAMapTreeNode::pointer SHAMap::getNode(const SHAMapNode& nodeID)
|
||||
{
|
||||
boost::recursive_mutex::scoped_lock sl(mLock);
|
||||
|
||||
SHAMapTreeNode::pointer node = checkCacheNode(nodeID);
|
||||
if (node) return node;
|
||||
|
||||
node = root;
|
||||
while (nodeID != *node)
|
||||
{
|
||||
int branch = node->selectBranch(nodeID.getNodeID());
|
||||
assert(branch >= 0);
|
||||
if ((branch < 0) || node->isEmptyBranch(branch))
|
||||
return SHAMapTreeNode::pointer();
|
||||
|
||||
node = getNode(node->getChildNodeID(branch), node->getChildHash(branch), false);
|
||||
if (!node) throw std::runtime_error("missing node");
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
||||
bool SHAMap::getPath(const uint256& index, std::vector< std::vector<unsigned char> >& nodes, SHANodeFormat format)
|
||||
{
|
||||
// Return the path of nodes to the specified index in the specified format
|
||||
// Return value: true = node present, false = node not present
|
||||
|
||||
boost::recursive_mutex::scoped_lock sl(mLock);
|
||||
SHAMapTreeNode* inNode = root.get();
|
||||
|
||||
while (!inNode->isLeaf())
|
||||
{
|
||||
Serializer s;
|
||||
inNode->addRaw(s, format);
|
||||
nodes.push_back(s.peekData());
|
||||
|
||||
int branch = inNode->selectBranch(index);
|
||||
if (inNode->isEmptyBranch(branch)) // paths leads to empty branch
|
||||
return false;
|
||||
inNode = getNodePointer(inNode->getChildNodeID(branch), inNode->getChildHash(branch));
|
||||
if (!inNode)
|
||||
throw SHAMapMissingNode(mType, inNode->getChildNodeID(branch), inNode->getChildHash(branch), index);
|
||||
}
|
||||
|
||||
if (inNode->getTag() != index) // path leads to different leaf
|
||||
return false;
|
||||
|
||||
// path lead to the requested leaf
|
||||
Serializer s;
|
||||
inNode->addRaw(s, format);
|
||||
nodes.push_back(s.peekData());
|
||||
return true;
|
||||
}
|
||||
|
||||
void SHAMap::dump(bool hash)
|
||||
{
|
||||
#if 0
|
||||
std::cerr << "SHAMap::dump" << std::endl;
|
||||
SHAMapItem::pointer i=peekFirstItem();
|
||||
while (i)
|
||||
{
|
||||
std::cerr << "Item: id=" << i->getTag() << std::endl;
|
||||
i = peekNextItem(i->getTag());
|
||||
}
|
||||
std::cerr << "SHAMap::dump done" << std::endl;
|
||||
#endif
|
||||
|
||||
std::cerr << " MAP Contains" << std::endl;
|
||||
boost::recursive_mutex::scoped_lock sl(mLock);
|
||||
for(boost::unordered_map<SHAMapNode, SHAMapTreeNode::pointer>::iterator it = mTNByID.begin();
|
||||
it != mTNByID.end(); ++it)
|
||||
{
|
||||
std::cerr << it->second->getString() << std::endl;
|
||||
if (hash)
|
||||
std::cerr << " " << it->second->getNodeHash() << std::endl;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static std::vector<unsigned char>IntToVUC(int v)
|
||||
{
|
||||
std::vector<unsigned char> vuc;
|
||||
for (int i = 0; i < 32; ++i)
|
||||
vuc.push_back(static_cast<unsigned char>(v));
|
||||
return vuc;
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE(SHAMap_suite)
|
||||
|
||||
BOOST_AUTO_TEST_CASE( SHAMap_test )
|
||||
{ // h3 and h4 differ only in the leaf, same terminal node (level 19)
|
||||
cLog(lsTRACE) << "SHAMap test";
|
||||
uint256 h1, h2, h3, h4, h5;
|
||||
h1.SetHex("092891fe4ef6cee585fdc6fda0e09eb4d386363158ec3321b8123e5a772c6ca7");
|
||||
h2.SetHex("436ccbac3347baa1f1e53baeef1f43334da88f1f6d70d963b833afd6dfa289fe");
|
||||
h3.SetHex("b92891fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e5a772c6ca8");
|
||||
h4.SetHex("b92891fe4ef6cee585fdc6fda2e09eb4d386363158ec3321b8123e5a772c6ca8");
|
||||
h5.SetHex("a92891fe4ef6cee585fdc6fda0e09eb4d386363158ec3321b8123e5a772c6ca7");
|
||||
|
||||
SHAMap sMap(smtFREE);
|
||||
SHAMapItem i1(h1, IntToVUC(1)), i2(h2, IntToVUC(2)), i3(h3, IntToVUC(3)), i4(h4, IntToVUC(4)), i5(h5, IntToVUC(5));
|
||||
|
||||
if (!sMap.addItem(i2, true, false)) BOOST_FAIL("no add");
|
||||
if (!sMap.addItem(i1, true, false)) BOOST_FAIL("no add");
|
||||
|
||||
SHAMapItem::pointer i;
|
||||
|
||||
i = sMap.peekFirstItem();
|
||||
if (!i || (*i != i1)) BOOST_FAIL("bad traverse");
|
||||
i = sMap.peekNextItem(i->getTag());
|
||||
if (!i || (*i != i2)) BOOST_FAIL("bad traverse");
|
||||
i = sMap.peekNextItem(i->getTag());
|
||||
if (i) BOOST_FAIL("bad traverse");
|
||||
|
||||
sMap.addItem(i4, true, false);
|
||||
sMap.delItem(i2.getTag());
|
||||
sMap.addItem(i3, true, false);
|
||||
|
||||
i = sMap.peekFirstItem();
|
||||
if (!i || (*i != i1)) BOOST_FAIL("bad traverse");
|
||||
i = sMap.peekNextItem(i->getTag());
|
||||
if (!i || (*i != i3)) BOOST_FAIL("bad traverse");
|
||||
i = sMap.peekNextItem(i->getTag());
|
||||
if (!i || (*i != i4)) BOOST_FAIL("bad traverse");
|
||||
i = sMap.peekNextItem(i->getTag());
|
||||
if (i) BOOST_FAIL("bad traverse");
|
||||
|
||||
cLog(lsTRACE) << "SHAMap snap test";
|
||||
uint256 mapHash = sMap.getHash();
|
||||
SHAMap::pointer map2 = sMap.snapShot(false);
|
||||
if (sMap.getHash() != mapHash) BOOST_FAIL("bad snapshot");
|
||||
if (map2->getHash() != mapHash) BOOST_FAIL("bad snapshot");
|
||||
if (!sMap.delItem(sMap.peekFirstItem()->getTag())) BOOST_FAIL("bad mod");
|
||||
if (sMap.getHash() == mapHash) BOOST_FAIL("bad snapshot");
|
||||
if (map2->getHash() != mapHash) BOOST_FAIL("bad snapshot");
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END();
|
||||
|
||||
// vim:ts=4
|
||||
423
src/cpp/ripple/SHAMap.h
Normal file
423
src/cpp/ripple/SHAMap.h
Normal file
@@ -0,0 +1,423 @@
|
||||
#ifndef __SHAMAP__
|
||||
#define __SHAMAP__
|
||||
|
||||
#include <list>
|
||||
#include <map>
|
||||
#include <stack>
|
||||
|
||||
#include <boost/shared_ptr.hpp>
|
||||
#include <boost/enable_shared_from_this.hpp>
|
||||
#include <boost/unordered/unordered_map.hpp>
|
||||
|
||||
#include "types.h"
|
||||
#include "uint256.h"
|
||||
#include "ScopedLock.h"
|
||||
#include "Serializer.h"
|
||||
#include "HashedObject.h"
|
||||
#include "InstanceCounter.h"
|
||||
|
||||
DEFINE_INSTANCE(SHAMap);
|
||||
DEFINE_INSTANCE(SHAMapItem);
|
||||
DEFINE_INSTANCE(SHAMapTreeNode);
|
||||
|
||||
class SHAMap;
|
||||
|
||||
// A tree-like map of SHA256 hashes
|
||||
// The trees are designed for rapid synchronization and compression of differences
|
||||
|
||||
|
||||
class SHAMapNode
|
||||
{ // Identifies a node in a SHA256 hash
|
||||
private:
|
||||
static uint256 smMasks[65]; // AND with hash to get node id
|
||||
|
||||
uint256 mNodeID;
|
||||
int mDepth;
|
||||
|
||||
public:
|
||||
|
||||
static const int rootDepth = 0;
|
||||
|
||||
SHAMapNode() : mDepth(0) { ; }
|
||||
SHAMapNode(int depth, const uint256& hash);
|
||||
virtual ~SHAMapNode() { ; }
|
||||
int getDepth() const { return mDepth; }
|
||||
const uint256& getNodeID() const { return mNodeID; }
|
||||
bool isValid() const { return (mDepth >= 0) && (mDepth < 64); }
|
||||
|
||||
virtual bool isPopulated() const { return false; }
|
||||
|
||||
SHAMapNode getParentNodeID() const
|
||||
{
|
||||
assert(mDepth);
|
||||
return SHAMapNode(mDepth - 1, mNodeID);
|
||||
}
|
||||
SHAMapNode getChildNodeID(int m) const;
|
||||
int selectBranch(const uint256& hash) const;
|
||||
|
||||
bool operator<(const SHAMapNode&) const;
|
||||
bool operator>(const SHAMapNode&) const;
|
||||
bool operator==(const SHAMapNode&) const;
|
||||
bool operator==(const uint256&) const;
|
||||
bool operator!=(const SHAMapNode&) const;
|
||||
bool operator!=(const uint256&) const;
|
||||
bool operator<=(const SHAMapNode&) const;
|
||||
bool operator>=(const SHAMapNode&) const;
|
||||
bool isRoot() const { return mDepth == 0; }
|
||||
|
||||
virtual std::string getString() const;
|
||||
void dump() const;
|
||||
|
||||
static bool ClassInit();
|
||||
static uint256 getNodeID(int depth, const uint256& hash);
|
||||
|
||||
// Convert to/from wire format (256-bit nodeID, 1-byte depth)
|
||||
void addIDRaw(Serializer &s) const;
|
||||
std::string getRawString() const;
|
||||
static int getRawIDLength(void) { return 33; }
|
||||
SHAMapNode(const void *ptr, int len);
|
||||
};
|
||||
|
||||
extern std::size_t hash_value(const SHAMapNode& mn);
|
||||
|
||||
inline std::ostream& operator<<(std::ostream& out, const SHAMapNode& node) { return out << node.getString(); }
|
||||
|
||||
class SHAMapItem : public IS_INSTANCE(SHAMapItem)
|
||||
{ // an item stored in a SHAMap
|
||||
public:
|
||||
typedef boost::shared_ptr<SHAMapItem> pointer;
|
||||
typedef const boost::shared_ptr<SHAMapItem>& ref;
|
||||
|
||||
private:
|
||||
uint256 mTag;
|
||||
Serializer mData;
|
||||
|
||||
public:
|
||||
|
||||
SHAMapItem(const uint256& tag) : mTag(tag) { ; }
|
||||
SHAMapItem(const uint256& tag, const std::vector<unsigned char>& data);
|
||||
SHAMapItem(const uint256& tag, const Serializer& s);
|
||||
SHAMapItem(const std::vector<unsigned char>& data); // tag by hash
|
||||
|
||||
const uint256& getTag() const { return mTag; }
|
||||
std::vector<unsigned char> getData() const { return mData.getData(); }
|
||||
const std::vector<unsigned char>& peekData() const { return mData.peekData(); }
|
||||
Serializer& peekSerializer() { return mData; }
|
||||
void addRaw(std::vector<unsigned char>& s) const { s.insert(s.end(), mData.begin(), mData.end()); }
|
||||
|
||||
void updateData(const std::vector<unsigned char>& data) { mData=data; }
|
||||
|
||||
bool operator==(const SHAMapItem& i) const { return mTag == i.mTag; }
|
||||
bool operator!=(const SHAMapItem& i) const { return mTag != i.mTag; }
|
||||
bool operator==(const uint256& i) const { return mTag == i; }
|
||||
bool operator!=(const uint256& i) const { return mTag != i; }
|
||||
#if 0
|
||||
// This code is comment out because it is unused. It could work.
|
||||
bool operator<(const SHAMapItem& i) const { return mTag < i.mTag; }
|
||||
bool operator>(const SHAMapItem& i) const { return mTag > i.mTag; }
|
||||
bool operator<=(const SHAMapItem& i) const { return mTag <= i.mTag; }
|
||||
bool operator>=(const SHAMapItem& i) const { return mTag >= i.mTag; }
|
||||
|
||||
bool operator<(const uint256& i) const { return mTag < i; }
|
||||
bool operator>(const uint256& i) const { return mTag > i; }
|
||||
bool operator<=(const uint256& i) const { return mTag <= i; }
|
||||
bool operator>=(const uint256& i) const { return mTag >= i; }
|
||||
#endif
|
||||
virtual void dump();
|
||||
};
|
||||
|
||||
enum SHANodeFormat
|
||||
{
|
||||
snfPREFIX = 1, // Form that hashes to its official hash
|
||||
snfWIRE = 2, // Compressed form used on the wire
|
||||
snfHASH = 3, // just the hash
|
||||
};
|
||||
|
||||
enum SHAMapType
|
||||
{
|
||||
smtTRANSACTION =1, // A tree of transactions
|
||||
smtSTATE =2, // A tree of state nodes
|
||||
smtFREE =3, // A tree not part of a ledger
|
||||
};
|
||||
|
||||
class SHAMapTreeNode : public SHAMapNode, public IS_INSTANCE(SHAMapTreeNode)
|
||||
{
|
||||
friend class SHAMap;
|
||||
|
||||
public:
|
||||
typedef boost::shared_ptr<SHAMapTreeNode> pointer;
|
||||
typedef const boost::shared_ptr<SHAMapTreeNode>& ref;
|
||||
|
||||
enum TNType
|
||||
{
|
||||
tnERROR = 0,
|
||||
tnINNER = 1,
|
||||
tnTRANSACTION_NM = 2, // transaction, no metadata
|
||||
tnTRANSACTION_MD = 3, // transaction, with metadata
|
||||
tnACCOUNT_STATE = 4
|
||||
};
|
||||
|
||||
private:
|
||||
uint256 mHash;
|
||||
uint256 mHashes[16];
|
||||
SHAMapItem::pointer mItem;
|
||||
uint32 mSeq;
|
||||
TNType mType;
|
||||
bool mFullBelow;
|
||||
|
||||
bool updateHash();
|
||||
|
||||
SHAMapTreeNode(const SHAMapTreeNode&); // no implementation
|
||||
SHAMapTreeNode& operator=(const SHAMapTreeNode&); // no implementation
|
||||
|
||||
public:
|
||||
SHAMapTreeNode(uint32 seq, const SHAMapNode& nodeID); // empty node
|
||||
SHAMapTreeNode(const SHAMapTreeNode& node, uint32 seq); // copy node from older tree
|
||||
SHAMapTreeNode(const SHAMapNode& nodeID, SHAMapItem::ref item, TNType type, uint32 seq);
|
||||
|
||||
// raw node functions
|
||||
SHAMapTreeNode(const SHAMapNode& id, const std::vector<unsigned char>& data, uint32 seq, SHANodeFormat format);
|
||||
void addRaw(Serializer &, SHANodeFormat format);
|
||||
|
||||
virtual bool isPopulated() const { return true; }
|
||||
|
||||
// node functions
|
||||
uint32 getSeq() const { return mSeq; }
|
||||
void setSeq(uint32 s) { mSeq = s; }
|
||||
const uint256& getNodeHash() const { return mHash; }
|
||||
TNType getType() const { return mType; }
|
||||
|
||||
// type functions
|
||||
bool isLeaf() const { return (mType == tnTRANSACTION_NM) || (mType == tnTRANSACTION_MD) ||
|
||||
(mType == tnACCOUNT_STATE); }
|
||||
bool isInner() const { return mType == tnINNER; }
|
||||
bool isValid() const { return mType != tnERROR; }
|
||||
bool isTransaction() const { return (mType == tnTRANSACTION_NM) || (mType == tnTRANSACTION_MD); }
|
||||
bool hasMetaData() const { return mType == tnTRANSACTION_MD; }
|
||||
bool isAccountState() const { return mType == tnACCOUNT_STATE; }
|
||||
|
||||
// inner node functions
|
||||
bool isInnerNode() const { return !mItem; }
|
||||
bool setChildHash(int m, const uint256& hash);
|
||||
bool isEmptyBranch(int m) const { return !mHashes[m]; }
|
||||
bool isEmpty() const;
|
||||
int getBranchCount() const;
|
||||
void makeInner();
|
||||
const uint256& getChildHash(int m) const
|
||||
{
|
||||
assert((m >= 0) && (m < 16) && (mType == tnINNER));
|
||||
return mHashes[m];
|
||||
}
|
||||
|
||||
// item node function
|
||||
bool hasItem() const { return !!mItem; }
|
||||
SHAMapItem::ref peekItem() { return mItem; }
|
||||
SHAMapItem::pointer getItem() const;
|
||||
bool setItem(SHAMapItem::ref i, TNType type);
|
||||
const uint256& getTag() const { return mItem->getTag(); }
|
||||
const std::vector<unsigned char>& peekData() { return mItem->peekData(); }
|
||||
std::vector<unsigned char> getData() const { return mItem->getData(); }
|
||||
|
||||
// sync functions
|
||||
bool isFullBelow(void) const { return mFullBelow; }
|
||||
void setFullBelow(void) { mFullBelow = true; }
|
||||
|
||||
virtual void dump();
|
||||
virtual std::string getString() const;
|
||||
};
|
||||
|
||||
enum SHAMapState
|
||||
{
|
||||
smsModifying = 0, // Objects can be added and removed (like an open ledger)
|
||||
smsImmutable = 1, // Map cannot be changed (like a closed ledger)
|
||||
smsSynching = 2, // Map's hash is locked in, valid nodes can be added (like a peer's closing ledger)
|
||||
smsFloating = 3, // Map is free to change hash (like a synching open ledger)
|
||||
smsInvalid = 4, // Map is known not to be valid (usually synching a corrupt ledger)
|
||||
};
|
||||
|
||||
class SHAMapSyncFilter
|
||||
{
|
||||
public:
|
||||
SHAMapSyncFilter() { ; }
|
||||
virtual ~SHAMapSyncFilter() { ; }
|
||||
|
||||
virtual void gotNode(const SHAMapNode& id, const uint256& nodeHash,
|
||||
const std::vector<unsigned char>& nodeData, SHAMapTreeNode::TNType type)
|
||||
{ ; }
|
||||
|
||||
virtual bool haveNode(const SHAMapNode& id, const uint256& nodeHash, std::vector<unsigned char>& nodeData)
|
||||
{ return false; }
|
||||
};
|
||||
|
||||
class SHAMapMissingNode : public std::runtime_error
|
||||
{
|
||||
protected:
|
||||
SHAMapType mType;
|
||||
SHAMapNode mNodeID;
|
||||
uint256 mNodeHash;
|
||||
uint256 mTargetIndex;
|
||||
|
||||
public:
|
||||
SHAMapMissingNode(SHAMapType t, const SHAMapNode& nodeID, const uint256& nodeHash) :
|
||||
std::runtime_error("SHAMapMissingNode"), mType(t), mNodeID(nodeID), mNodeHash(nodeHash)
|
||||
{ ; }
|
||||
|
||||
SHAMapMissingNode(SHAMapType t, const SHAMapNode& nodeID, const uint256& nodeHash, const uint256& targetIndex) :
|
||||
std::runtime_error(nodeID.getString()), mType(t),
|
||||
mNodeID(nodeID), mNodeHash(nodeHash), mTargetIndex(targetIndex)
|
||||
{ ; }
|
||||
|
||||
virtual ~SHAMapMissingNode() throw()
|
||||
{ ; }
|
||||
|
||||
void setTargetNode(const uint256& tn) { mTargetIndex = tn; }
|
||||
|
||||
SHAMapType getMapType() const { return mType; }
|
||||
const SHAMapNode& getNodeID() const { return mNodeID; }
|
||||
const uint256& getNodeHash() const { return mNodeHash; }
|
||||
const uint256& getTargetIndex() const { return mTargetIndex; }
|
||||
bool hasTargetIndex() const { return !mTargetIndex.isZero(); }
|
||||
};
|
||||
|
||||
extern std::ostream& operator<<(std::ostream&, const SHAMapMissingNode&);
|
||||
|
||||
class SHAMap : public IS_INSTANCE(SHAMap)
|
||||
{
|
||||
public:
|
||||
typedef boost::shared_ptr<SHAMap> pointer;
|
||||
typedef const boost::shared_ptr<SHAMap>& ref;
|
||||
|
||||
typedef std::pair<SHAMapItem::pointer, SHAMapItem::pointer> SHAMapDiffItem;
|
||||
typedef std::map<uint256, SHAMapDiffItem> SHAMapDiff;
|
||||
typedef boost::unordered_map<SHAMapNode, SHAMapTreeNode::pointer> SHADirtyMap;
|
||||
|
||||
private:
|
||||
uint32 mSeq;
|
||||
mutable boost::recursive_mutex mLock;
|
||||
boost::unordered_map<SHAMapNode, SHAMapTreeNode::pointer> mTNByID;
|
||||
|
||||
boost::shared_ptr<SHADirtyMap> mDirtyNodes;
|
||||
|
||||
SHAMapTreeNode::pointer root;
|
||||
|
||||
SHAMapState mState;
|
||||
|
||||
SHAMapType mType;
|
||||
|
||||
protected:
|
||||
|
||||
void dirtyUp(std::stack<SHAMapTreeNode::pointer>& stack, const uint256& target, uint256 prevHash);
|
||||
std::stack<SHAMapTreeNode::pointer> getStack(const uint256& id, bool include_nonmatching_leaf, bool partialOk);
|
||||
SHAMapTreeNode::pointer walkTo(const uint256& id, bool modify);
|
||||
SHAMapTreeNode* walkToPointer(const uint256& id);
|
||||
SHAMapTreeNode::pointer checkCacheNode(const SHAMapNode&);
|
||||
void returnNode(SHAMapTreeNode::pointer&, bool modify);
|
||||
void trackNewNode(SHAMapTreeNode::pointer&);
|
||||
|
||||
SHAMapTreeNode::pointer getNode(const SHAMapNode& id);
|
||||
SHAMapTreeNode::pointer getNode(const SHAMapNode& id, const uint256& hash, bool modify);
|
||||
SHAMapTreeNode* getNodePointer(const SHAMapNode& id, const uint256& hash);
|
||||
SHAMapTreeNode* firstBelow(SHAMapTreeNode*);
|
||||
SHAMapTreeNode* lastBelow(SHAMapTreeNode*);
|
||||
|
||||
SHAMapItem::pointer onlyBelow(SHAMapTreeNode*);
|
||||
void eraseChildren(SHAMapTreeNode::pointer);
|
||||
|
||||
bool walkBranch(SHAMapTreeNode* node, SHAMapItem::ref otherMapItem, bool isFirstMap,
|
||||
SHAMapDiff& differences, int& maxCount);
|
||||
|
||||
public:
|
||||
|
||||
// build new map
|
||||
SHAMap(SHAMapType t, uint32 seq = 1);
|
||||
SHAMap(SHAMapType t, const uint256& hash);
|
||||
|
||||
~SHAMap() { mState = smsInvalid; }
|
||||
|
||||
// Returns a new map that's a snapshot of this one. Force CoW
|
||||
SHAMap::pointer snapShot(bool isMutable);
|
||||
|
||||
// hold the map stable across operations
|
||||
ScopedLock Lock() const { return ScopedLock(mLock); }
|
||||
|
||||
bool hasNode(const SHAMapNode& id);
|
||||
void fetchRoot(const uint256& hash);
|
||||
|
||||
// normal hash access functions
|
||||
bool hasItem(const uint256& id);
|
||||
bool delItem(const uint256& id);
|
||||
bool addItem(const SHAMapItem& i, bool isTransaction, bool hasMeta);
|
||||
bool updateItem(const SHAMapItem& i, bool isTransaction, bool hasMeta);
|
||||
SHAMapItem getItem(const uint256& id);
|
||||
uint256 getHash() const { return root->getNodeHash(); }
|
||||
uint256 getHash() { return root->getNodeHash(); }
|
||||
|
||||
// save a copy if you have a temporary anyway
|
||||
bool updateGiveItem(SHAMapItem::ref, bool isTransaction, bool hasMeta);
|
||||
bool addGiveItem(SHAMapItem::ref, bool isTransaction, bool hasMeta);
|
||||
|
||||
// save a copy if you only need a temporary
|
||||
SHAMapItem::pointer peekItem(const uint256& id);
|
||||
SHAMapItem::pointer peekItem(const uint256& id, SHAMapTreeNode::TNType& type);
|
||||
|
||||
// traverse functions
|
||||
SHAMapItem::pointer peekFirstItem();
|
||||
SHAMapItem::pointer peekFirstItem(SHAMapTreeNode::TNType& type);
|
||||
SHAMapItem::pointer peekLastItem();
|
||||
SHAMapItem::pointer peekNextItem(const uint256&);
|
||||
SHAMapItem::pointer peekNextItem(const uint256&, SHAMapTreeNode::TNType& type);
|
||||
SHAMapItem::pointer peekPrevItem(const uint256&);
|
||||
|
||||
// comparison/sync functions
|
||||
void getMissingNodes(std::vector<SHAMapNode>& nodeIDs, std::vector<uint256>& hashes, int max,
|
||||
SHAMapSyncFilter* filter);
|
||||
bool getNodeFat(const SHAMapNode& node, std::vector<SHAMapNode>& nodeIDs,
|
||||
std::list<std::vector<unsigned char> >& rawNode, bool fatRoot, bool fatLeaves);
|
||||
bool getRootNode(Serializer& s, SHANodeFormat format);
|
||||
bool addRootNode(const uint256& hash, const std::vector<unsigned char>& rootNode, SHANodeFormat format,
|
||||
SHAMapSyncFilter* filter);
|
||||
bool addRootNode(const std::vector<unsigned char>& rootNode, SHANodeFormat format,
|
||||
SHAMapSyncFilter* filter);
|
||||
bool addKnownNode(const SHAMapNode& nodeID, const std::vector<unsigned char>& rawNode,
|
||||
SHAMapSyncFilter* filter);
|
||||
|
||||
// status functions
|
||||
void setImmutable() { assert(mState != smsInvalid); mState = smsImmutable; }
|
||||
void clearImmutable() { mState = smsModifying; }
|
||||
bool isSynching() const { return (mState == smsFloating) || (mState == smsSynching); }
|
||||
void setSynching() { mState = smsSynching; }
|
||||
void setFloating() { mState = smsFloating; }
|
||||
void clearSynching() { mState = smsModifying; }
|
||||
bool isValid() { return mState != smsInvalid; }
|
||||
|
||||
// caution: otherMap must be accessed only by this function
|
||||
// return value: true=successfully completed, false=too different
|
||||
bool compare(SHAMap::ref otherMap, SHAMapDiff& differences, int maxCount);
|
||||
|
||||
int armDirty();
|
||||
static int flushDirty(SHADirtyMap& dirtyMap, int maxNodes, HashedObjectType t, uint32 seq);
|
||||
boost::shared_ptr<SHADirtyMap> disarmDirty();
|
||||
|
||||
void setSeq(uint32 seq) { mSeq = seq; }
|
||||
uint32 getSeq() { return mSeq; }
|
||||
|
||||
// overloads for backed maps
|
||||
boost::shared_ptr<SHAMapTreeNode> fetchNodeExternal(const SHAMapNode& id, const uint256& hash);
|
||||
|
||||
bool operator==(const SHAMap& s) { return getHash() == s.getHash(); }
|
||||
|
||||
// trusted path operations - prove a particular node is in a particular ledger
|
||||
std::list<std::vector<unsigned char> > getTrustedPath(const uint256& index);
|
||||
static std::vector<unsigned char> checkTrustedPath(const uint256& ledgerHash, const uint256& leafIndex,
|
||||
const std::list<std::vector<unsigned char> >& path);
|
||||
|
||||
void walkMap(std::vector<SHAMapMissingNode>& missingNodes, int maxMissing);
|
||||
|
||||
bool getPath(const uint256& index, std::vector< std::vector<unsigned char> >& nodes, SHANodeFormat format);
|
||||
|
||||
bool deepCompare(SHAMap& other);
|
||||
virtual void dump(bool withHashes = false);
|
||||
};
|
||||
|
||||
#endif
|
||||
// vim:ts=4
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user