Moved cpp code to src/cpp and js code to src/js.

This commit is contained in:
Stefan Thomas
2012-11-06 12:02:59 -08:00
parent 3c880b8301
commit fa3fab5816
214 changed files with 62 additions and 57 deletions

View 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

View 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

File diff suppressed because it is too large Load Diff

View 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

View 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

View 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
}

View 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
View 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
View 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);

View 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;
}

View 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
View 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
View 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

View 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

View 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

View 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
View 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
View 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

View 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
View 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

View 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
View 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

View 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;
}

View 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

View 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

View 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

View 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

View 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

View 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

View 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;
}

View 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

View 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

View 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
View 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
View 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

File diff suppressed because it is too large Load Diff

315
src/cpp/ripple/Ledger.h Normal file
View 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

View 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

View 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

File diff suppressed because it is too large Load Diff

View 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

File diff suppressed because it is too large Load Diff

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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;
}

View 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
View 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
View 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

File diff suppressed because it is too large Load Diff

242
src/cpp/ripple/NetworkOPs.h Normal file
View 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

View 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

View 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);
}

View 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

View 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
View 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);
}
};
}

View 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

View 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);
};

View 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);
}
}
}
}

View 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);
};

View 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;
}

View 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 */

View 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

View 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

View 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

View 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

File diff suppressed because it is too large Load Diff

175
src/cpp/ripple/Peer.h Normal file
View 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

View 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
View 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

View 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

View 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

View 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
View 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);

View File

View File

@@ -0,0 +1,6 @@
class RPCCommands
{
public:
HttpReply& handleCommand();
};

View 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
View 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();
};

File diff suppressed because it is too large Load Diff

163
src/cpp/ripple/RPCHandler.h Normal file
View 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 &params);
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 &params);
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 &params);
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

View 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

View 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
View 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
View 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

View 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

View 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

File diff suppressed because it is too large Load Diff

194
src/cpp/ripple/RippleCalc.h Normal file
View 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

View 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

View 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();
};

View 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

View 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
View 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
View 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